Skip to content

Commit

Permalink
feat(dunning): reset customer dunning counters on invoice updates (#2876
Browse files Browse the repository at this point in the history
)

## Roadmap

👉 https://getlago.canny.io/feature-requests/p/set-up-payment-retry-logic

👉
https://getlago.canny.io/feature-requests/p/send-reminders-for-overdue-invoices

 ## Context

We want to automate dunning process so that our users don't have to look
at each customer to maximize their chances of being paid retrying
payments of overdue balances and sending email reminders.

We are extending dunning campaigns management to edit and delete
campaigns.

 ## Description

When an invoice included in a payment request that belongs to a dunning
campaign is payment succeeded, this change resets the customer dunning
campaign status counters (completed, last attempt number and last
attempt timestamp).
  • Loading branch information
ancorcruz authored Nov 27, 2024
1 parent e920dcf commit 9b2b03b
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 1 deletion.
13 changes: 12 additions & 1 deletion app/services/invoices/update_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,18 @@ def call
end

ActiveRecord::Base.transaction do
invoice.payment_overdue = false if invoice.payment_overdue? && invoice.payment_succeeded?
if invoice.payment_overdue? && invoice.payment_succeeded?
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
)
end
end

invoice.save!

Invoices::Metadata::UpdateService.call(invoice:, params: params[:metadata]) if params[:metadata]
Expand Down
42 changes: 42 additions & 0 deletions spec/services/invoices/update_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,48 @@
end
end

context "when invoices is included in a payment request" do
let(:customer) do
create(
:customer,
dunning_campaign_completed: true,
last_dunning_campaign_attempt: 3,
last_dunning_campaign_attempt_at: 1.day.ago
)
end

let(:invoice) { create(:invoice, payment_overdue: true, customer:) }

let(:payment_request) do
create(:payment_request, customer:, invoices: [invoice])
end

before do
payment_request
end

it "does not reset customer dunning campaign status counters" do
expect { result && customer.reload }
.to not_change(customer, :dunning_campaign_completed)
.and not_change(customer, :last_dunning_campaign_attempt)
.and not_change { customer.last_dunning_campaign_attempt_at.to_i }
end

context "when payment request belongs to a dunning campaign" do
let(:dunning_campaign) { create(:dunning_campaign) }
let(:payment_request) do
create(:payment_request, customer:, invoices: [invoice], dunning_campaign:)
end

it "resets customer dunning campaign status counters" do
expect { result && 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

it 'calls SegmentTrackJob' do
result

Expand Down

0 comments on commit 9b2b03b

Please sign in to comment.