Class: HhPackage::ReservationPackages::PriceFinder
- Inherits:
-
Object
- Object
- HhPackage::ReservationPackages::PriceFinder
- Defined in:
- app/my_lib/hh_package/reservation_packages/price_finder.rb
Instance Attribute Summary collapse
-
#dynamic_pricing_flipper ⇒ Object
readonly
Returns the value of attribute dynamic_pricing_flipper.
-
#group_sections ⇒ Object
Returns the value of attribute group_sections.
-
#menu_sections ⇒ Object
Returns the value of attribute menu_sections.
-
#use_dynamic_pricing ⇒ Object
Returns the value of attribute use_dynamic_pricing.
Instance Method Summary collapse
-
#calculate_come_more_pay_less_pricing(pricing, pricing_group, adult, date) ⇒ Integer
Calculate the price for a group of adults based on the provided pricing information.
-
#come_more_pay_less_sample_price_for_preview(package, with_date: false, restaurant_id: nil) ⇒ Object
return any pricing_group just for preview to user when a package has come more pay less feature when adult price is 1000, group size is 5, group size to pay is 4 => :size_to_pay=>4, :price=>1000, :price_format=>“฿1,000”, :currency=>“THB”, :sample_price_per_person=>800, :sample_price_per_pesron_format=>“฿800”.
- #filter_dynamic_pricings(package) ⇒ Object
-
#find_add_on_final_price(add_on, quantity, adult, kids) ⇒ Integer
Calculate the final price for an add-on based on its pricing model.
- #find_add_on_kids_price(add_on) ⇒ Object
-
#find_add_on_refund_final_price(add_on, total_add_on_price_cents) ⇒ Integer
Calculate the final price for refund guarantee based on the add-on's refund policy.
-
#find_base_price(package, adult, date) ⇒ Object
set menus_sections= [] required for ala carte type.
-
#find_kids_price(package, adult, date, packages: []) ⇒ Hash
Find the price for kids based on the selected packages.
-
#find_package_final_price(package, quantity, adult, kids, date, custom_price_cents: nil, packages: []) ⇒ Object
Calculate the final price for a package based on its pricing model and dynamic pricing type.
-
#find_package_refund_final_price(package, total_package_price_cents) ⇒ Integer
Calculate the final price for refund guarantee based on the package's refund policy.
-
#initialize(use_dynamic_pricing: false, time_zone: nil) ⇒ PriceFinder
constructor
A new instance of PriceFinder.
-
#kids_price_list(package) ⇒ Array<Hash>
This method is to transform the dynamic pricing's kids price to be `kids_price_v2` in the Serializer.
- #use_dynamic_pricing? ⇒ Boolean
- #use_kids_price?(package) ⇒ Boolean
- #valid_come_more_pay_less?(package, date) ⇒ Boolean
- #valid_come_more_pay_less_date?(date, pricing_group) ⇒ Boolean
Constructor Details
#initialize(use_dynamic_pricing: false, time_zone: nil) ⇒ PriceFinder
Returns a new instance of PriceFinder.
9 10 11 12 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 9 def initialize(use_dynamic_pricing: false, time_zone: nil) self.use_dynamic_pricing = use_dynamic_pricing @dynamic_pricing_flipper = DynamicPricingsFlipper.new(time_zone) end |
Instance Attribute Details
#dynamic_pricing_flipper ⇒ Object (readonly)
Returns the value of attribute dynamic_pricing_flipper.
7 8 9 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 7 def dynamic_pricing_flipper @dynamic_pricing_flipper end |
#group_sections ⇒ Object
Returns the value of attribute group_sections.
6 7 8 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 6 def group_sections @group_sections end |
#menu_sections ⇒ Object
Returns the value of attribute menu_sections.
6 7 8 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 6 def @menu_sections end |
#use_dynamic_pricing ⇒ Object
Returns the value of attribute use_dynamic_pricing.
6 7 8 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 6 def use_dynamic_pricing @use_dynamic_pricing end |
Instance Method Details
#calculate_come_more_pay_less_pricing(pricing, pricing_group, adult, date) ⇒ Integer
Calculate the price for a group of adults based on the provided pricing information
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 435 def calculate_come_more_pay_less_pricing(pricing, pricing_group, adult, date) if valid_come_more_pay_less_date?(date, pricing_group) group_size = pricing_group.group_size group_size_to_pay = pricing_group.group_size_to_pay adult = adult.to_i # If there are fewer adults than the group size, return the base price return (pricing.price_cents * adult.to_f).to_i if adult < group_size # Calculate the price for groups of size group_size_to_pay full_groups_price = (adult / group_size) * group_size_to_pay * pricing.price # Calculate the price for any remaining adults remaining_adults_price = (adult % group_size) * pricing.price # Add the prices together to get the total price (full_groups_price.cents + remaining_adults_price.cents).to_i else (pricing.price_cents * adult).to_i end end |
#come_more_pay_less_sample_price_for_preview(package, with_date: false, restaurant_id: nil) ⇒ Object
return any pricing_group just for preview to user when a package has come more pay less feature when adult price is 1000, group size is 5, group size to pay is 4
=> {:group_size=>5, :size_to_pay=>4, :price=>1000, :price_format=>"฿1,000", :currency=>"THB", :sample_price_per_person=>800, :sample_price_per_pesron_format=>"฿800"}
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 175 def come_more_pay_less_sample_price_for_preview(package, with_date: false, restaurant_id: nil) if [:per_person_and_normal_price, :per_pack_and_normal_price].include?(package.pricing_model_and_dynamic_pricing_type) && package.pricing_groups.present? pricing_group = package.pricing_groups.first return {} if pricing_group.blank? return {} if pricing_group.present? && !pricing_group.valid_to_show_as_preview? pricing = filter_dynamic_pricings(package).first return {} if pricing.blank? # Find restaurant package by restaurant_id if provided, otherwise fallback to first restaurant_package = if restaurant_id.present? package.restaurant_packages.find_by(restaurant_id: restaurant_id) else backtrace = Rails.backtrace_cleaner.clean(caller) APMErrorHandler.report('RestaurantPackage lookup without restaurant_id', { package_id: package.id, package_type: package.class.name, caller_location: backtrace, }) package.restaurant_packages.active_scope.first end start_date = pricing_group.start_date.presence || restaurant_package&.start_date end_date = pricing_group.end_date.presence || restaurant_package&.end_date sample_price = calculate_come_more_pay_less_pricing(pricing, pricing_group, pricing_group.group_size, start_date) / pricing_group.group_size price_format = HhMoney.new(pricing.price_cents, pricing.price_currency).default_format sample_price_format = HhMoney.new(sample_price, pricing.price_currency).default_format result = { group_size: pricing_group.group_size, size_to_pay: pricing_group.group_size_to_pay, price: pricing.price.amount.to_i, price_format: price_format, currency: pricing.price_currency, sample_price_per_person: sample_price, sample_price_per_person_format: sample_price_format, # TODO: remove below typo "peson" attributes after release, # for backward compatibility sample_price_per_peson: sample_price, sample_price_per_peson_format: sample_price_format, } if with_date return result.merge( start_date: start_date, end_date: end_date, ) end result else {} end end |
#filter_dynamic_pricings(package) ⇒ Object
424 425 426 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 424 def filter_dynamic_pricings(package) dynamic_pricing_flipper.filter_by_package package.dynamic_pricings, package, use_dynamic_pricing? end |
#find_add_on_final_price(add_on, quantity, adult, kids) ⇒ Integer
Calculate the final price for an add-on based on its pricing model.
303 304 305 306 307 308 309 310 311 312 313 314 315 316 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 303 def find_add_on_final_price(add_on, quantity, adult, kids) pricing = add_on.pricing pricing_type = pricing.pricing_type case pricing_type when AddOns::Pricing::PRICING_TYPE_PER_PERSON add_on_price_calculation_per_person(pricing, adult, kids, add_on) when AddOns::Pricing::PRICING_TYPE_PER_ITEM add_on_price_calculation_per_item(pricing, quantity, kids, add_on) else APMErrorHandler.report("Unknown add-on pricing type: #{pricing_type}", add_on_id: add_on.id) 0 end end |
#find_add_on_kids_price(add_on) ⇒ Object
163 164 165 166 167 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 163 def find_add_on_kids_price(add_on) currency = fetch_add_on_currency(add_on) build_add_on_kids_price_hash(add_on, find_kids_price_by_add_on(add_on, currency), currency) end |
#find_add_on_refund_final_price(add_on, total_add_on_price_cents) ⇒ Integer
Calculate the final price for refund guarantee based on the add-on's refund policy.
347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 347 def find_add_on_refund_final_price(add_on, total_add_on_price_cents) return 0 if add_on.blank? || !add_on.accept_refund_guarantee? case add_on.refund_type.to_sym when AddOns::AddOn::REFUND_TYPE_PERCENTAGE ((total_add_on_price_cents * add_on.refund_amount).to_d / 100).ceil when AddOns::AddOn::REFUND_TYPE_FIXED_AMOUNT # refund_amount is in cents add_on.refund_amount.to_i else APMErrorHandler.report("Unknown refund type: #{add_on.refund_type}", add_on_id: add_on.id) 0 end end |
#find_base_price(package, adult, date) ⇒ Object
set menus_sections= [] required for ala carte type
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 23 def find_base_price(package, adult, date) case package.pricing_model_and_dynamic_pricing_type when :per_person_and_normal_price dynamic_pricings = filter_dynamic_pricings(package) dynamic_pricings.first when :per_person_and_by_party_size_price dynamic_pricings = filter_dynamic_pricings(package) pricing = dynamic_pricings.select do |p| adult.to_i.between?(p.min_seat.to_i, p.max_seat.to_i) end.first if pricing.blank? dynamic_pricing = dynamic_pricings.first if adult < dynamic_pricing.min_seat.to_i raise(InvalidPackageData, I18n.t('errors.package.ayce.min_party_size', adults: dynamic_pricing.min_seat)) end if adult > dynamic_pricing.max_seat.to_i raise(InvalidPackageData, I18n.t('errors.package.ayce.max_party_size', adults: dynamic_pricing.max_seat)) end else pricing end when :per_person_and_by_day_price, :per_pack_and_by_day_price dynamic_pricings = if date.present? package.dynamic_pricings else filter_dynamic_pricings(package) end if use_dynamic_pricing? || date.present? # prioritize pricing.by_day_category equal holiday first, then `custom_day, based on the `selected_days` holiday_pricings = dynamic_pricings.select do |p| p.by_day_category == 'holiday' && date.between?(p.start_date, p.end_date) end return holiday_pricings.first if holiday_pricings.present? wday = date.wday.to_s custom_day_pricings = dynamic_pricings.select do |p| # selected_days are integers representing the day of the week, 0 is Sunday, 6 is Saturday p.by_day_category == 'custom_day' && p.selected_days.include?(wday) end return custom_day_pricings.first if custom_day_pricings.present? # HH_LOGGER.debug('Price for selected day is not found', # package_id: package.id, # package_type: package.class.name, # adult: adult, # date: date, # dynamic_pricings: dynamic_pricings.map(&:attributes), # wday: wday) # send_invalid_dynamic_pricing_email(package, use_dynamic_pricing, adult, date, wday) if use_dynamic_pricing? # client app could send the date data, but they don't use dynamic pricing yet # so we need to return the most expensive price if use_dynamic_pricing? APMErrorHandler.set_default_context_and_labels(dynamic_pricings: dynamic_pricings.map(&:attributes), use_dynamic_pricing: use_dynamic_pricing, package: package.attributes, adult: adult, date: date) raise(InvalidPackageData, 'Price for selected day is not found') else filter_dynamic_pricings(package).first end else dynamic_pricings.first end when :per_pack_and_normal_price dynamic_pricings = filter_dynamic_pricings(package) dynamic_pricings.first.presence || raise(InvalidPackageData, 'Pricing is empty') when :per_set_and_normal_price dynamic_pricings = filter_dynamic_pricings(package) dynamic_pricings.first.presence || raise(InvalidPackageData, 'Pricing is empty') when :per_menu_ala_carte_and_normal_price total_price_cents = 0 sections = .map(&:with_indifferent_access) sections.each do |section| section['menus'].each do || = HhPackage::PackageMenu.find_by(id: ['id']) next if .blank? sub_price = .price_cents * ['quantity'].to_i total_price_cents += sub_price end end currency = package.package_attr.price_currency.presence || HhPackage::Pricing::DEFAULT_CURRENCY OpenStruct.new(price_cents: total_price_cents, kids_price_cents: 0, price_currency: currency) when :per_item_and_normal_price # For DIY packages using per_item pricing model # Return a base pricing structure that will be calculated later in find_package_final_price currency = package.package_attr.price_currency.presence || HhPackage::Pricing::DEFAULT_CURRENCY # Create an OpenStruct that mimics HhPackage::Pricing with a price method OpenStruct.new( price_cents: 0, kids_price_cents: 0, price_currency: currency, price: Money.new(0, currency), ) else raise InvalidPackageData, 'Unknown pricing model and dynamic pricing type' end end |
#find_kids_price(package, adult, date, packages: []) ⇒ Hash
Find the price for kids based on the selected packages. This method returns more than just a price, as it also identifies the package from which the kids' price is derived. This is necessary because the “mix and match” feature of AYCE (All-You-Can-Eat) packages allows two different kids' prices.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 148 def find_kids_price(package, adult, date, packages: []) currency = fetch_package_currency(package) zero_price = HhMoney.new(0, currency) # Return a zero-price hash if the package is not of Ayce type return build_kids_price_hash(package, zero_price, currency) unless package.is_a?(HhPackage::Package::Ayce) # If mix and match is enabled, find the maximum kids price from the packages if package.use_mix_and_match?(packages.count) find_max_or_zero_kids_price(package, adult, date, packages, currency, zero_price) else build_kids_price_hash(package, find_kids_price_by_package(package, adult, date, currency, packages), currency) end end |
#find_package_final_price(package, quantity, adult, kids, date, custom_price_cents: nil, packages: []) ⇒ Object
Calculate the final price for a package based on its pricing model and dynamic pricing type.
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 243 def find_package_final_price(package, quantity, adult, kids, date, custom_price_cents: nil, packages: []) package_count = packages.count pricing = if custom_price_cents.blank? find_base_price(package, adult, date) else OpenStruct.new(price_cents: custom_price_cents, kids_price_cents: 0, price_currency: currency) end # set 1 as default charge_based_quantity for flat charge_amount_type # flat means charge amount is fixed per booking charge_based_quantity = package.charge_amount_type.to_sym == :flat ? 1 : quantity selected_qty = package.mix_n_match_qty(adult, charge_based_quantity, package_count) case package.pricing_model_and_dynamic_pricing_type(package_count: package_count) when :per_person_and_normal_price, :per_person_and_by_party_size_price, :per_person_and_by_day_price if valid_come_more_pay_less?(package, date) calculate_come_more_pay_less_total(pricing, package, selected_qty, kids, date, packages) else dynamic_price_calculation(pricing, selected_qty, kids, date, package, packages: packages) end # when :per_person_and_by_party_size_price, :per_person_and_by_day_price # dynamic_price_calculation(pricing, selected_qty, kids, date, package, package_count: package_count) when :per_pack_and_by_day_price pricing.price_cents * quantity.to_f when :per_pack_and_normal_price, :per_set_and_normal_price # ayce package with mix and match feature will use per pack mode # possible case in this block is also ayce package with mix and match feature and come more pay less feature if valid_come_more_pay_less?(package, date) calculate_come_more_pay_less_total(pricing, package, selected_qty, kids, date, packages) elsif package.use_mix_and_match?(package_count) dynamic_price_calculation(pricing, selected_qty, kids, date, package, packages: packages) elsif package.ala_carte? || .blank? pricing.price_cents * quantity.to_f else pricing.price_cents end when :per_menu_ala_carte_and_normal_price pricing.price_cents when :per_item_and_normal_price # Handle DIY packages with group_sections (pricing menus) if package.type_short == HhPackage::Package::Diy::TYPE_SHORT && group_sections.present? calculate_diy_group_sections_price(package) else pricing.price_cents * quantity.to_i end else raise NotImplementedError, "Unknown #{package.pricing_model_and_dynamic_pricing_type} pricing model and dynamic pricing type" end end |
#find_package_refund_final_price(package, total_package_price_cents) ⇒ Integer
Calculate the final price for refund guarantee based on the package's refund policy.
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 324 def find_package_refund_final_price(package, total_package_price_cents) package_attr = package.package_attr return 0 if package_attr.blank? || !package_attr.accept_refund_guarantee? case package_attr.refund_type.to_sym when HhPackage::PackageAttr::REFUND_TYPE_PERCENTAGE ((total_package_price_cents * package_attr.refund_amount).to_d / 100).ceil when HhPackage::PackageAttr::REFUND_TYPE_FIXED_AMOUNT # refund_amount is in cents package_attr.refund_amount.to_i else APMErrorHandler.report("Unknown refund type: #{package_attr.refund_type}", package_id: package.id) 0 end end |
#kids_price_list(package) ⇒ Array<Hash>
This method is to transform the dynamic pricing's kids price to be `kids_price_v2` in the Serializer
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 379 def kids_price_list(package) if use_kids_price?(package) price_policy = package.package_attr.kids_policy.to_s price_policy_short = (package.package_attr&.kids_policy_short.presence || package.package_attr&.kids_policy_short_en).to_s # map dynamic_pricings but remove duplicate kids price cents filter_dynamic_pricings(package).map do |pricing| kids_price_cents = pricing.kids_price_cents price_value = if kids_price_cents.zero? 'Free' else HhMoney.format_rounded(kids_price_cents, pricing.price_currency, round: :down) end { price_policy: price_policy, price_policy_short: price_policy_short, price_value: price_value, price_cents: kids_price_cents } end.uniq { |p| p[:price_value] } else [] end end |
#use_dynamic_pricing? ⇒ Boolean
14 15 16 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 14 def use_dynamic_pricing? @use_dynamic_pricing == true end |
#use_kids_price?(package) ⇒ Boolean
362 363 364 365 366 367 368 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 362 def use_kids_price?(package) return false unless package.is_a?(HhPackage::Package::Ayce) filter_dynamic_pricings(package).select do |pricing| pricing.kids_price_cents.positive? end.present? end |
#valid_come_more_pay_less?(package, date) ⇒ Boolean
415 416 417 418 419 420 421 422 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 415 def valid_come_more_pay_less?(package, date) return false unless package&.package_attr&.comemore_payless? pricing_group = package.pricing_groups.first return false if pricing_group.blank? valid_come_more_pay_less_date?(date, pricing_group) end |
#valid_come_more_pay_less_date?(date, pricing_group) ⇒ Boolean
404 405 406 407 408 409 410 411 412 413 |
# File 'app/my_lib/hh_package/reservation_packages/price_finder.rb', line 404 def valid_come_more_pay_less_date?(date, pricing_group) return false if date.blank? date = date.to_date return true if pricing_group.start_date.nil? || pricing_group.end_date.nil? return true if date.between?(pricing_group.start_date, pricing_group.end_date) false end |