From 3fb6193098d1b897bfc01b4906955cc8dbf4a987 Mon Sep 17 00:00:00 2001 From: Pau Perez Date: Fri, 26 Jun 2020 18:27:02 +0200 Subject: [PATCH] Move adjustments logic from decorator into model --- app/models/spree/payment.rb | 32 ++++++++++++++++++++++ app/models/spree/payment_decorator.rb | 32 ---------------------- spec/models/spree/payment_original_spec.rb | 31 +++++++++++++++++++++ spec/models/spree/payment_spec.rb | 22 --------------- 4 files changed, 63 insertions(+), 54 deletions(-) diff --git a/app/models/spree/payment.rb b/app/models/spree/payment.rb index 6804c9fcd4..ef386f2218 100644 --- a/app/models/spree/payment.rb +++ b/app/models/spree/payment.rb @@ -15,6 +15,8 @@ module Spree class_name: "Spree::Payment", foreign_key: :source_id has_many :log_entries, as: :source + has_one :adjustment, as: :source, dependent: :destroy + before_validation :validate_source before_save :set_unique_identifier @@ -125,8 +127,38 @@ module Spree res || payment_method end + def ensure_correct_adjustment + revoke_adjustment_eligibility if ['failed', 'invalid'].include?(state) + return if adjustment.try(:finalized?) + + if adjustment + adjustment.originator = payment_method + adjustment.label = adjustment_label + adjustment.save + else + payment_method.create_adjustment(adjustment_label, order, self, true) + association(:adjustment).reload + end + end + + def adjustment_label + I18n.t('payment_method_fee') + end + private + # Don't charge fees for invalid or failed payments. + # This is called twice for failed payments, because the persistence of the 'failed' + # state is acheived through some trickery using an after_rollback callback on the + # payment model. See Spree::Payment#persist_invalid + def revoke_adjustment_eligibility + return unless adjustment.try(:reload) + return if adjustment.finalized? + + adjustment.update_attribute(:eligible, false) + adjustment.finalize! + end + def validate_source if source && !source.valid? source.errors.each do |field, error| diff --git a/app/models/spree/payment_decorator.rb b/app/models/spree/payment_decorator.rb index 0ccca1c4d4..3c88894bc2 100644 --- a/app/models/spree/payment_decorator.rb +++ b/app/models/spree/payment_decorator.rb @@ -4,31 +4,11 @@ module Spree Payment.class_eval do delegate :line_items, to: :order - has_one :adjustment, as: :source, dependent: :destroy - # We bypass this after_rollback callback that is setup in Spree::Payment # The issues the callback fixes are not experienced in OFN: # if a payment fails on checkout the state "failed" is persisted correctly def persist_invalid; end - def ensure_correct_adjustment - revoke_adjustment_eligibility if ['failed', 'invalid'].include?(state) - return if adjustment.try(:finalized?) - - if adjustment - adjustment.originator = payment_method - adjustment.label = adjustment_label - adjustment.save - else - payment_method.create_adjustment(adjustment_label, order, self, true) - association(:adjustment).reload - end - end - - def adjustment_label - I18n.t('payment_method_fee') - end - private def create_payment_profile @@ -41,17 +21,5 @@ module Spree rescue ActiveMerchant::ConnectionError => e gateway_error e end - - # Don't charge fees for invalid or failed payments. - # This is called twice for failed payments, because the persistence of the 'failed' - # state is acheived through some trickery using an after_rollback callback on the - # payment model. See Spree::Payment#persist_invalid - def revoke_adjustment_eligibility - return unless adjustment.try(:reload) - return if adjustment.finalized? - - adjustment.update_attribute(:eligible, false) - adjustment.finalize! - end end end diff --git a/spec/models/spree/payment_original_spec.rb b/spec/models/spree/payment_original_spec.rb index 7d3385b3da..70bce9a2b5 100644 --- a/spec/models/spree/payment_original_spec.rb +++ b/spec/models/spree/payment_original_spec.rb @@ -787,4 +787,35 @@ describe Spree::Payment do payment.refund! end end + + describe "applying transaction fees" do + let!(:order) { create(:order) } + let!(:line_item) { create(:line_item, order: order, quantity: 3, price: 5.00) } + + before do + order.reload.update! + end + + context "when order-based calculator" do + let!(:shop) { create(:enterprise) } + let!(:payment_method) { create(:payment_method, calculator: calculator) } + + let!(:calculator) do + Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10) + end + + context "when order complete and inventory tracking enabled" do + let!(:order) { create(:completed_order_with_totals, distributor: shop) } + let!(:variant) { order.line_items.first.variant } + let!(:inventory_item) { create(:inventory_item, enterprise: shop, variant: variant) } + + it "creates adjustment" do + payment = create(:payment, order: order, payment_method: payment_method, + amount: order.total) + expect(payment.adjustment).to be_present + expect(payment.adjustment.amount).not_to eq(0) + end + end + end + end end diff --git a/spec/models/spree/payment_spec.rb b/spec/models/spree/payment_spec.rb index a0e0fd4ff3..434ce3a2a3 100644 --- a/spec/models/spree/payment_spec.rb +++ b/spec/models/spree/payment_spec.rb @@ -11,28 +11,6 @@ module Spree order.reload.update! end - context "when order-based calculator" do - let!(:shop) { create(:enterprise) } - let!(:payment_method) { create(:payment_method, calculator: calculator) } - - let!(:calculator) do - Spree::Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10) - end - - context "when order complete and inventory tracking enabled" do - let!(:order) { create(:completed_order_with_totals, distributor: shop) } - let!(:variant) { order.line_items.first.variant } - let!(:inventory_item) { create(:inventory_item, enterprise: shop, variant: variant) } - - it "creates adjustment" do - payment = create(:payment, order: order, payment_method: payment_method, - amount: order.total) - expect(payment.adjustment).to be_present - expect(payment.adjustment.amount).not_to eq(0) - end - end - end - context "to Stripe payments" do let(:shop) { create(:enterprise) } let(:payment_method) { create(:stripe_payment_method, distributor_ids: [create(:distributor_enterprise).id], preferred_enterprise_id: shop.id) }