1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-02-20 14:54:15 +01:00

(bug) test on invoice items was not idempotent

This commit is contained in:
Sylvain 2022-10-28 10:24:41 +02:00
parent d9eee1aa3f
commit 46e042fd38
4 changed files with 45 additions and 38 deletions

View File

@ -13,15 +13,17 @@ class Invoice < PaymentDocument
belongs_to :wallet_transaction
belongs_to :coupon
has_one :avoir, class_name: 'Invoice', foreign_key: :invoice_id, dependent: :destroy
has_one :payment_schedule_item
has_one :payment_gateway_object, as: :item
belongs_to :operator_profile, foreign_key: :operator_profile_id, class_name: 'InvoicingProfile'
has_one :avoir, class_name: 'Invoice', dependent: :destroy, inverse_of: :avoir
has_one :payment_schedule_item, dependent: :nullify
has_one :payment_gateway_object, as: :item, dependent: :destroy
belongs_to :operator_profile, class_name: 'InvoicingProfile'
delegate :user, to: :invoicing_profile
before_create :add_environment
after_create :update_reference, :chain_record
after_commit :generate_and_send_invoice, on: [:create], if: :persisted?
after_update :log_changes
after_commit :generate_and_send_invoice, on: [:create], if: :persisted?
validates_with ClosedPeriodValidator
@ -44,10 +46,6 @@ class Invoice < PaymentDocument
"#{prefix}-#{id}_#{created_at.strftime('%d%m%Y')}.pdf"
end
def user
invoicing_profile.user
end
def order_number
PaymentDocumentService.generate_order_number(self)
end
@ -75,7 +73,7 @@ class Invoice < PaymentDocument
invoice_items.each do |ii|
paid_items += 1 unless ii.amount.zero?
next unless attrs[:invoice_items_ids].include? ii.id # list of items to refund (partial refunds)
raise Exception if ii.invoice_item # cannot refund an item that was already refunded
raise StandardError if ii.invoice_item # cannot refund an item that was already refunded
refund_items += 1 unless ii.amount.zero?
avoir_ii = avoir.invoice_items.build(ii.dup.attributes)
@ -84,17 +82,7 @@ class Invoice < PaymentDocument
avoir.total += avoir_ii.amount
end
# handle coupon
unless avoir.coupon_id.nil?
discount = avoir.total
if avoir.coupon.type == 'percent_off'
discount = avoir.total * avoir.coupon.percent_off / 100.0
elsif avoir.coupon.type == 'amount_off'
discount = (avoir.coupon.amount_off / paid_items) * refund_items
else
raise InvalidCouponError
end
avoir.total -= discount
end
avoir.total = CouponService.apply_on_refund(avoir.total, avoir.coupon, paid_items, refund_items)
avoir
end
@ -145,6 +133,10 @@ class Invoice < PaymentDocument
invoice_items.where(main: true).first
end
def other_items
invoice_items.where(main: [nil, false])
end
# get amount total paid
def amount_paid
total - (wallet_amount || 0)

View File

@ -4,7 +4,7 @@
class PaymentScheduleItem < Footprintable
belongs_to :payment_schedule
belongs_to :invoice
has_one :payment_gateway_object, as: :item
has_one :payment_gateway_object, as: :item, dependent: :destroy
after_create :chain_record

View File

@ -40,6 +40,21 @@ class CouponService
price
end
# Apply the provided coupon to the given amount, considering that this applies to a refund invoice (Avoir),
# potentially partial
def self.apply_on_refund(amount, coupon, paid_items = 1, refund_items = 1)
return amount if coupon.nil?
case coupon.type
when 'percent_off'
amount - (Rational(amount * coupon.percent_off) / Rational(100.0)).to_f.ceil
when 'amount_off'
amount - (Rational(coupon.amount_off / paid_items) * Rational(refund_items)).to_f.ceil
else
raise InvalidCouponError
end
end
##
# Find the coupon associated with the given code and check it is valid for the given user
# @param code {String} the literal code of the coupon

View File

@ -58,14 +58,14 @@ class Invoices::RoundTest < ActionDispatch::IntegrationTest
### amount paid = 1295
invoice = Invoice.last
assert_equal 121, invoice.invoice_items.first.amount
assert_equal 1498, invoice.invoice_items.last.amount
assert_equal 121, invoice.main_item.amount
assert_equal 1498, invoice.other_items.last.amount
assert_equal 1295, invoice.total
coupon_service = CouponService.new
total_without_coupon = coupon_service.invoice_total_no_coupon(invoice)
assert_equal 97, coupon_service.ventilate(total_without_coupon, invoice.invoice_items.first.amount, invoice.coupon)
assert_equal 1198, coupon_service.ventilate(total_without_coupon, invoice.invoice_items.last.amount, invoice.coupon)
assert_equal 97, coupon_service.ventilate(total_without_coupon, invoice.main_item.amount, invoice.coupon)
assert_equal 1198, coupon_service.ventilate(total_without_coupon, invoice.other_items.last.amount, invoice.coupon)
assert_equal 324, total_without_coupon - invoice.total
vat_service = VatHistoryService.new
@ -121,14 +121,14 @@ class Invoices::RoundTest < ActionDispatch::IntegrationTest
### amount paid = 2336
invoice = Invoice.last
assert_equal 1423, invoice.invoice_items.first.amount
assert_equal 1498, invoice.invoice_items.last.amount
assert_equal 1423, invoice.main_item.amount
assert_equal 1498, invoice.other_items.last.amount
assert_equal 2336, invoice.total
coupon_service = CouponService.new
total_without_coupon = coupon_service.invoice_total_no_coupon(invoice)
assert_equal 1138, coupon_service.ventilate(total_without_coupon, invoice.invoice_items.first.amount, invoice.coupon)
assert_equal 1198, coupon_service.ventilate(total_without_coupon, invoice.invoice_items.last.amount, invoice.coupon)
assert_equal 1138, coupon_service.ventilate(total_without_coupon, invoice.main_item.amount, invoice.coupon)
assert_equal 1198, coupon_service.ventilate(total_without_coupon, invoice.other_items.last.amount, invoice.coupon)
assert_equal 585, total_without_coupon - invoice.total
vat_service = VatHistoryService.new
@ -184,14 +184,14 @@ class Invoices::RoundTest < ActionDispatch::IntegrationTest
### amount paid = 1319
invoice = Invoice.last
assert_equal 121, invoice.invoice_items.first.amount
assert_equal 1498, invoice.invoice_items.last.amount
assert_equal 121, invoice.main_item.amount
assert_equal 1498, invoice.other_items.last.amount
assert_equal 1319, invoice.total
coupon_service = CouponService.new
total_without_coupon = coupon_service.invoice_total_no_coupon(invoice)
assert_equal 99, coupon_service.ventilate(total_without_coupon, invoice.invoice_items.first.amount, invoice.coupon)
assert_equal 1220, coupon_service.ventilate(total_without_coupon, invoice.invoice_items.last.amount, invoice.coupon)
assert_equal 99, coupon_service.ventilate(total_without_coupon, invoice.main_item.amount, invoice.coupon)
assert_equal 1220, coupon_service.ventilate(total_without_coupon, invoice.other_items.last.amount, invoice.coupon)
assert_equal 300, total_without_coupon - invoice.total
vat_service = VatHistoryService.new
@ -247,14 +247,14 @@ class Invoices::RoundTest < ActionDispatch::IntegrationTest
### amount paid = 2621
invoice = Invoice.last
assert_equal 1423, invoice.invoice_items.first.amount
assert_equal 1498, invoice.invoice_items.last.amount
assert_equal 1423, invoice.main_item.amount
assert_equal 1498, invoice.other_items.last.amount
assert_equal 2621, invoice.total
coupon_service = CouponService.new
total_without_coupon = coupon_service.invoice_total_no_coupon(invoice)
assert_equal 1277, coupon_service.ventilate(total_without_coupon, invoice.invoice_items.first.amount, invoice.coupon)
assert_equal 1344, coupon_service.ventilate(total_without_coupon, invoice.invoice_items.last.amount, invoice.coupon)
assert_equal 1277, coupon_service.ventilate(total_without_coupon, invoice.main_item.amount, invoice.coupon)
assert_equal 1344, coupon_service.ventilate(total_without_coupon, invoice.other_items.last.amount, invoice.coupon)
assert_equal 300, total_without_coupon - invoice.total
vat_service = VatHistoryService.new