1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-01-29 18:52:22 +01:00

improved slot price calculation

This commit is contained in:
Sylvain 2021-12-21 15:30:08 +01:00
parent 6091cec82e
commit 2d807e6c94

View File

@ -16,7 +16,6 @@ class CartItem::Reservation < CartItem::BaseItem
end
def price
base_amount = hourly_rate
is_privileged = @operator.privileged? && @operator.id != @customer.id
prepaid = { minutes: PrepaidPackService.minutes_available(@customer, @reservable) }
@ -25,10 +24,10 @@ class CartItem::Reservation < CartItem::BaseItem
hours_available = credits
@slots.each_with_index do |slot, index|
amount += get_slot_price(base_amount, slot, is_privileged,
elements: elements,
has_credits: (index < hours_available),
prepaid: prepaid)
amount += get_slot_price_from_prices(applicable_prices, slot, is_privileged,
elements: elements,
has_credits: (index < hours_available),
prepaid: prepaid)
end
{ elements: elements, amount: amount }
@ -61,6 +60,27 @@ class CartItem::Reservation < CartItem::BaseItem
0
end
##
# Compute the price of a single slot, according to the list of applicable prices.
# @param prices {{ prices: Array<{price: Price, duration: number}> }} list of prices to use with the current reservation
# @see get_slot_price
##
def get_slot_price_from_prices(prices, slot, is_privileged, options = {})
options = GET_SLOT_PRICE_DEFAULT_OPTS.merge(options)
slot_minutes = (slot[:end_at].to_time - slot[:start_at].to_time) / SECONDS_PER_MINUTE
price = prices[:prices].find { |p| p[:duration] <= slot_minutes && p[:duration].positive? }
price = prices[:prices].first if price.nil?
hourly_rate = (price[:price].amount.to_f / price[:price].duration) * MINUTES_PER_HOUR
# apply the base price to the real slot duration
real_price = get_slot_price(hourly_rate, slot, is_privileged, options)
price[:duration] -= slot_minutes
real_price
end
##
# Compute the price of a single slot, according to the base price and the ability for an admin
# to offer the slot.
@ -103,15 +123,14 @@ class CartItem::Reservation < CartItem::BaseItem
real_price
end
# We compute the hourly rate according to the prices of the current reservation
# If there are prices for durations longer than 1 hour, but shorter than the total duration,
# we use these prices before using the hourly rate.
# We determine the list of prices applicable to current reservation
# The longest available price is always used in priority.
# Eg. If the reservation is for 12 hours, and there are prices for 3 hours, 7 hours,
# and the base price (1 hours), we use the 7 hours price, then 3 hours price, and finally the base price.
# Then we divide the total price by the total duration to get the hourly rate.
def hourly_rate
# and the base price (1 hours), we use the 7 hours price, then 3 hours price, and finally the base price twice (7+3+1+1 = 12).
# All these prices are returned to be applied to the reservation.
def applicable_prices
total_duration = @slots.map { |slot| (slot[:end_at].to_time - slot[:start_at].to_time) / SECONDS_PER_MINUTE }.reduce(:+)
price = 0
rates = { prices: [] }
remaining_duration = total_duration
while remaining_duration.positive?
@ -119,15 +138,16 @@ class CartItem::Reservation < CartItem::BaseItem
.where(Price.arel_table[:duration].lteq(remaining_duration))
.maximum(:duration)
max_duration = 60 if max_duration.nil?
max_duration_amount = @reservable.prices.find_by(group_id: @customer.group_id, plan_id: @plan.try(:id), duration: max_duration)
.amount
max_duration_price = @reservable.prices.find_by(group_id: @customer.group_id, plan_id: @plan.try(:id), duration: max_duration)
current_duration = [remaining_duration, max_duration].min
price += (max_duration_amount / max_duration) * current_duration
remaining_duration -= max_duration
rates[:prices].push(price: max_duration_price, duration: current_duration)
remaining_duration -= current_duration
end
price / (total_duration / MINUTES_PER_HOUR)
rates[:prices].sort! { |a, b| b[:duration] <=> a[:duration] }
rates
end
##