diff --git a/app/models/customer.rb b/app/models/customer.rb index 3c792ea35f7..3711d160243 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -190,6 +190,14 @@ def overdue_balance_cents invoices.payment_overdue.where(currency:).sum(:total_amount_cents) end + def reset_dunning_campaign! + update!( + dunning_campaign_completed: false, + last_dunning_campaign_attempt: 0, + last_dunning_campaign_attempt_at: nil + ) + end + private def ensure_slug diff --git a/app/services/customers/update_service.rb b/app/services/customers/update_service.rb index cbb58a178de..127ae608217 100644 --- a/app/services/customers/update_service.rb +++ b/app/services/customers/update_service.rb @@ -105,12 +105,6 @@ def call customer.exclude_from_dunning_campaign = args[:exclude_from_dunning_campaign] customer.applied_dunning_campaign = nil if args[:exclude_from_dunning_campaign] end - - if customer.applied_dunning_campaign_id_changed? || customer.exclude_from_dunning_campaign_changed? - customer.last_dunning_campaign_attempt = 0 - customer.last_dunning_campaign_attempt_at = nil - customer.dunning_campaign_completed = false - end end ActiveRecord::Base.transaction do @@ -119,6 +113,10 @@ def call customer.payment_provider_code = nil end + if customer.applied_dunning_campaign_id_changed? || customer.exclude_from_dunning_campaign_changed? + customer.reset_dunning_campaign! + end + customer.save! customer.reload diff --git a/app/services/dunning_campaigns/process_attempt_service.rb b/app/services/dunning_campaigns/process_attempt_service.rb index 9875c0622ca..2408fa0a901 100644 --- a/app/services/dunning_campaigns/process_attempt_service.rb +++ b/app/services/dunning_campaigns/process_attempt_service.rb @@ -16,7 +16,7 @@ def call return result unless applicable_dunning_campaign? return result unless dunning_campaign_threshold_reached? return result unless days_between_attempts_passed? - return result if dunning_campaign_completed? + return result if customer.dunning_campaign_completed? ActiveRecord::Base.transaction do payment_request_result = PaymentRequests::CreateService.call( @@ -30,6 +30,7 @@ def call customer.increment(:last_dunning_campaign_attempt) customer.last_dunning_campaign_attempt_at = Time.zone.now + customer.dunning_campaign_completed = last_dunning_campaign_attempt? customer.save! result.customer = customer @@ -64,7 +65,7 @@ def days_between_attempts_passed? (customer.last_dunning_campaign_attempt_at + dunning_campaign.days_between_attempts.days).past? end - def dunning_campaign_completed? + def last_dunning_campaign_attempt? customer.last_dunning_campaign_attempt >= dunning_campaign.max_attempts end diff --git a/app/services/invoices/update_service.rb b/app/services/invoices/update_service.rb index 2232755179d..27b81bb140f 100644 --- a/app/services/invoices/update_service.rb +++ b/app/services/invoices/update_service.rb @@ -44,11 +44,7 @@ def call invoice.payment_overdue = false if invoice.payment_requests.where.not(dunning_campaign_id: nil).exists? - invoice.customer.update!( - dunning_campaign_completed: false, - last_dunning_campaign_attempt: 0, - last_dunning_campaign_attempt_at: nil - ) + invoice.customer.reset_dunning_campaign! end end diff --git a/app/services/payment_requests/payments/adyen_service.rb b/app/services/payment_requests/payments/adyen_service.rb index 89545a7eecd..0b25168ebfa 100644 --- a/app/services/payment_requests/payments/adyen_service.rb +++ b/app/services/payment_requests/payments/adyen_service.rb @@ -272,11 +272,7 @@ def reset_customer_dunning_campaign_status(payment_status) return unless payment_status_succeeded?(payment_status) return unless payable.try(:dunning_campaign) - customer.update!( - dunning_campaign_completed: false, - last_dunning_campaign_attempt: 0, - last_dunning_campaign_attempt_at: nil - ) + customer.reset_dunning_campaign! end end end diff --git a/app/services/payment_requests/payments/gocardless_service.rb b/app/services/payment_requests/payments/gocardless_service.rb index e88b209d3c7..3628fb51cce 100644 --- a/app/services/payment_requests/payments/gocardless_service.rb +++ b/app/services/payment_requests/payments/gocardless_service.rb @@ -212,11 +212,7 @@ def reset_customer_dunning_campaign_status(payment_status) return unless payment_status_succeeded?(payment_status) return unless payable.try(:dunning_campaign) - customer.update!( - dunning_campaign_completed: false, - last_dunning_campaign_attempt: 0, - last_dunning_campaign_attempt_at: nil - ) + customer.reset_dunning_campaign! end end end diff --git a/spec/models/customer_spec.rb b/spec/models/customer_spec.rb index eacf44194dd..00a92c01183 100644 --- a/spec/models/customer_spec.rb +++ b/spec/models/customer_spec.rb @@ -594,4 +594,22 @@ end end end + + describe "#reset_dunning_campaign!" do + let(:customer) do + create( + :customer, + dunning_campaign_completed: true, + last_dunning_campaign_attempt: 5, + last_dunning_campaign_attempt_at: 1.day.ago + ) + end + + it "changes dunning campaign status counters" do + expect { customer.reset_dunning_campaign! && customer.reload } + .to change(customer, :dunning_campaign_completed).to(false) + .and change(customer, :last_dunning_campaign_attempt).to(0) + .and change(customer, :last_dunning_campaign_attempt_at).to(nil) + end + end end diff --git a/spec/services/dunning_campaigns/process_attempt_service_spec.rb b/spec/services/dunning_campaigns/process_attempt_service_spec.rb index 6060d148e60..ec61314f743 100644 --- a/spec/services/dunning_campaigns/process_attempt_service_spec.rb +++ b/spec/services/dunning_campaigns/process_attempt_service_spec.rb @@ -68,9 +68,28 @@ it "updates customer last dunning attempt data" do freeze_time do - expect { result } - .to change(customer.reload, :last_dunning_campaign_attempt).by(1) - .and change(customer.reload, :last_dunning_campaign_attempt_at).to(Time.zone.now) + expect { result && customer.reload } + .to change(customer, :last_dunning_campaign_attempt).by(1) + .and change(customer, :last_dunning_campaign_attempt_at).to(Time.zone.now) + .and not_change(customer, :dunning_campaign_completed).from(false) + end + end + + context "when dunning campaign max attemp is reached" do + let(:customer) do + create( + :customer, + organization:, + currency:, + last_dunning_campaign_attempt: dunning_campaign.max_attempts - 1, + last_dunning_campaign_attempt_at: dunning_campaign.days_between_attempts.days.ago, + dunning_campaign_completed: false + ) + end + + it "updates customer's dunning campaign completed flag" do + expect { result && customer.reload } + .to change(customer, :dunning_campaign_completed).to(true) end end @@ -117,13 +136,13 @@ end end - context "when the customer reaches dunning campaign max attempts" do + context "when the customer has completed the dunning campaign" do let(:customer) do create( :customer, organization:, currency:, - last_dunning_campaign_attempt: dunning_campaign.max_attempts + dunning_campaign_completed: true ) end