2020-11-03 16:50:11 +01:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
# create PaymentSchedules for various items
|
|
|
|
class PaymentScheduleService
|
|
|
|
##
|
|
|
|
# Compute a payment schedule for a new subscription to the provided plan
|
|
|
|
# @param plan {Plan}
|
2020-11-05 14:55:06 +01:00
|
|
|
# @param total {Number} Total amount of the current shopping cart (which includes this plan) - without coupon
|
2020-11-03 16:50:11 +01:00
|
|
|
# @param coupon {Coupon} apply this coupon, if any
|
|
|
|
##
|
2021-01-04 17:03:44 +01:00
|
|
|
def compute(plan, total, coupon: nil, subscription: nil)
|
2020-11-05 14:55:06 +01:00
|
|
|
other_items = total - plan.amount
|
2020-12-30 12:19:33 +01:00
|
|
|
# base monthly price of the plan
|
|
|
|
price = plan.amount
|
2020-11-05 14:55:06 +01:00
|
|
|
ps = PaymentSchedule.new(scheduled: plan, total: price + other_items, coupon: coupon)
|
2020-11-03 16:50:11 +01:00
|
|
|
deadlines = plan.duration / 1.month
|
2020-11-04 16:22:31 +01:00
|
|
|
per_month = (price / deadlines).truncate
|
2020-12-30 10:16:39 +01:00
|
|
|
adjustment = if per_month * deadlines + other_items.truncate != ps.total
|
|
|
|
ps.total - (per_month * deadlines + other_items.truncate)
|
2020-11-03 16:50:11 +01:00
|
|
|
else
|
|
|
|
0
|
|
|
|
end
|
|
|
|
items = []
|
|
|
|
(0..deadlines - 1).each do |i|
|
|
|
|
date = DateTime.current + i.months
|
2021-01-04 17:03:44 +01:00
|
|
|
details = { recurring: per_month, subscription_id: subscription&.id }
|
2020-11-05 14:55:06 +01:00
|
|
|
amount = if i.zero?
|
2020-12-30 10:16:39 +01:00
|
|
|
details[:adjustment] = adjustment.truncate
|
|
|
|
details[:other_items] = other_items.truncate
|
|
|
|
per_month + adjustment.truncate + other_items.truncate
|
2020-11-05 14:55:06 +01:00
|
|
|
else
|
|
|
|
per_month
|
|
|
|
end
|
2020-12-30 12:19:33 +01:00
|
|
|
if coupon
|
|
|
|
cs = CouponService.new
|
|
|
|
if (coupon.validity_per_user == 'once' && i.zero?) || coupon.validity_per_user == 'forever'
|
|
|
|
details[:without_coupon] = amount
|
|
|
|
amount = cs.apply(amount, coupon)
|
|
|
|
end
|
|
|
|
end
|
2020-11-03 16:50:11 +01:00
|
|
|
items.push PaymentScheduleItem.new(
|
2020-11-05 14:55:06 +01:00
|
|
|
amount: amount,
|
2020-11-03 16:50:11 +01:00
|
|
|
due_date: date,
|
2020-11-12 16:44:55 +01:00
|
|
|
payment_schedule: ps,
|
|
|
|
details: details
|
2020-11-03 16:50:11 +01:00
|
|
|
)
|
|
|
|
end
|
2021-01-20 13:47:29 +01:00
|
|
|
ps.total = items.map(&:amount).reduce(:+)
|
2020-11-03 16:50:11 +01:00
|
|
|
{ payment_schedule: ps, items: items }
|
|
|
|
end
|
2020-11-10 17:02:21 +01:00
|
|
|
|
2020-12-23 16:25:24 +01:00
|
|
|
def create(subscription, total, coupon: nil, operator: nil, payment_method: nil, reservation: nil, user: nil, setup_intent_id: nil)
|
2020-12-29 18:55:00 +01:00
|
|
|
subscription = reservation.generate_subscription if !subscription && reservation&.plan_id
|
2020-12-30 10:16:39 +01:00
|
|
|
raise InvalidSubscriptionError unless subscription&.persisted?
|
2020-12-21 12:02:51 +01:00
|
|
|
|
2021-01-04 17:03:44 +01:00
|
|
|
schedule = compute(subscription.plan, total, coupon: coupon, subscription: subscription)
|
2020-11-10 17:02:21 +01:00
|
|
|
ps = schedule[:payment_schedule]
|
|
|
|
items = schedule[:items]
|
|
|
|
|
2020-12-16 18:33:43 +01:00
|
|
|
ps.scheduled = reservation || subscription
|
2020-11-10 17:02:21 +01:00
|
|
|
ps.payment_method = payment_method
|
2020-12-23 16:25:24 +01:00
|
|
|
ps.stp_setup_intent_id = setup_intent_id
|
2020-11-16 16:37:40 +01:00
|
|
|
ps.operator_profile = operator.invoicing_profile
|
|
|
|
ps.invoicing_profile = user.invoicing_profile
|
2020-12-29 18:55:00 +01:00
|
|
|
ps.payment_schedule_items = items
|
2020-11-10 17:02:21 +01:00
|
|
|
items.each do |item|
|
|
|
|
item.payment_schedule = ps
|
|
|
|
end
|
2020-11-16 16:37:40 +01:00
|
|
|
ps
|
2020-11-10 17:02:21 +01:00
|
|
|
end
|
2020-11-03 16:50:11 +01:00
|
|
|
end
|