-
Notifications
You must be signed in to change notification settings - Fork 105
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(invoice-preview): add logic for applying coupons on preview invo…
…ice (#3073) ## Context Preview feature enables fetching first Lago invoice. This invoice is not persisted and is calculated on the fly. ## Description This PR adds service for applying coupons on preview invoice. The goal was not to affect heavily main service for applying coupons on invoices. In order not to repeat the code, some common logic is extracted s that it can be used on both places
- Loading branch information
1 parent
c730177
commit cf691a0
Showing
11 changed files
with
678 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# frozen_string_literal: true | ||
|
||
module AppliedCoupons | ||
class AmountService < BaseService | ||
def initialize(applied_coupon:, base_amount_cents:) | ||
@applied_coupon = applied_coupon | ||
@base_amount_cents = base_amount_cents | ||
|
||
super | ||
end | ||
|
||
def call | ||
return result.not_found_failure!(resource: 'applied_coupon') unless applied_coupon | ||
|
||
result.amount = compute_amount | ||
result | ||
end | ||
|
||
private | ||
|
||
attr_reader :applied_coupon, :base_amount_cents | ||
|
||
def compute_amount | ||
if applied_coupon.coupon.percentage? | ||
discounted_value = base_amount_cents * applied_coupon.percentage_rate.fdiv(100) | ||
|
||
return (discounted_value >= base_amount_cents) ? base_amount_cents : discounted_value.round | ||
end | ||
|
||
if applied_coupon.recurring? || applied_coupon.forever? | ||
return base_amount_cents if applied_coupon.amount_cents > base_amount_cents | ||
|
||
applied_coupon.amount_cents | ||
else | ||
return base_amount_cents if applied_coupon.remaining_amount > base_amount_cents | ||
|
||
applied_coupon.remaining_amount | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# frozen_string_literal: true | ||
|
||
module Coupons | ||
class PreviewService < BaseService | ||
def initialize(invoice:, applied_coupons:) | ||
@invoice = invoice | ||
@applied_coupons = applied_coupons | ||
|
||
super | ||
end | ||
|
||
def call | ||
return result.not_found_failure!(resource: 'invoice') unless invoice | ||
return result.not_found_failure!(resource: 'applied_coupons') unless applied_coupons | ||
|
||
result.credits = [] | ||
|
||
applied_coupons.each do |applied_coupon| | ||
break unless invoice.sub_total_excluding_taxes_amount_cents&.positive? | ||
next unless invoice.currency == applied_coupon.amount_currency | ||
|
||
fees = fees(applied_coupon) | ||
|
||
next if fees.none? | ||
|
||
base_amount_cents = base_amount_cents(applied_coupon, fees) | ||
credit = add_credit(applied_coupon, fees, base_amount_cents) | ||
|
||
result.credits << credit | ||
invoice.credits << credit | ||
end | ||
|
||
result.invoice = invoice | ||
result | ||
end | ||
|
||
private | ||
|
||
attr_reader :applied_coupons, :invoice | ||
|
||
def add_credit(applied_coupon, fees, base_amount_cents) | ||
credit_amount = AppliedCoupons::AmountService.call(applied_coupon:, base_amount_cents:).amount | ||
new_credit = Credit.new( | ||
invoice:, | ||
applied_coupon:, | ||
amount_cents: credit_amount, | ||
amount_currency: invoice.currency, | ||
before_taxes: true | ||
) | ||
|
||
fees.each do |fee| | ||
unless base_amount_cents.zero? | ||
fee.precise_coupons_amount_cents += fee.compute_precise_credit_amount_cents(credit_amount, base_amount_cents) | ||
end | ||
|
||
fee.precise_coupons_amount_cents = fee.amount_cents if fee.amount_cents < fee.precise_coupons_amount_cents | ||
end | ||
|
||
invoice.coupons_amount_cents += new_credit.amount_cents | ||
invoice.sub_total_excluding_taxes_amount_cents -= new_credit.amount_cents | ||
|
||
new_credit | ||
end | ||
|
||
def base_amount_cents(applied_coupon, fees) | ||
if applied_coupon.coupon.limited_billable_metrics? || applied_coupon.coupon.limited_plans? | ||
fees.sum(&:amount_cents) | ||
else | ||
invoice.sub_total_excluding_taxes_amount_cents | ||
end | ||
end | ||
|
||
# TODO: update later when charges will be added to the preview | ||
def fees(applied_coupon) | ||
if applied_coupon.coupon.limited_billable_metrics? | ||
Fee.none | ||
elsif applied_coupon.coupon.limited_plans? | ||
plan_related_fees(applied_coupon) | ||
else | ||
invoice.fees | ||
end | ||
end | ||
|
||
def plan_related_fees(applied_coupon) | ||
if applied_coupon.coupon.plans.map(&:id).include?(invoice.subscriptions[0].plan_id) | ||
invoice.fees | ||
else | ||
Fee.none | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.