Skip to content

Commit

Permalink
feat(dunning): Add dunning campaign to customer, database changes
Browse files Browse the repository at this point in the history
 ## Roadmap Task

👉 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're first automating the overdue balance payment request, before looking at individual invoices.

 ## Description

The goal of this change is to create set the relationship between
customers and dunning campaigns.

A customer could have one dunning campaign applied,
or, inherit its organization default campaign (none applied)
or, be excluded from dunning at all (with a boolean flag).

Also, when deleting a dunning campaign, all customers with the campaign
assigned must be reset.
  • Loading branch information
ancorcruz committed Oct 16, 2024
1 parent 34c82c5 commit 9c9ce33
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 53 deletions.
22 changes: 11 additions & 11 deletions app/models/clickhouse/events_enriched.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ class EventsEnriched < BaseRecord
#
# Table name: events_enriched
#
# code :string not null, primary key
# decimal_value :decimal(26, )
# enriched_at :datetime not null
# precise_total_amount_cents :decimal(40, 15)
# properties :string not null
# sorted_properties :string not null
# timestamp :datetime not null, primary key
# value :string
# external_subscription_id :string not null, primary key
# organization_id :string not null, primary key
# transaction_id :string not null
# aggregation_type :string
# code :string not null, primary key
# filters :string not null
# grouped_by :string not null
# properties :string not null
# timestamp :datetime not null
# value :string
# charge_id :string not null, primary key
# external_subscription_id :string not null, primary key
# organization_id :string not null, primary key
# transaction_id :string not null, primary key
#
89 changes: 48 additions & 41 deletions app/models/customer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Customer < ApplicationRecord
before_save :ensure_slug

belongs_to :organization
belongs_to :applied_dunning_campaign, optional: true, class_name: 'DunningCampaign'

has_many :subscriptions
has_many :events
Expand Down Expand Up @@ -192,55 +193,61 @@ def ensure_slug
#
# Table name: customers
#
# id :uuid not null, primary key
# address_line1 :string
# address_line2 :string
# city :string
# country :string
# currency :string
# customer_type :enum
# deleted_at :datetime
# document_locale :string
# email :string
# finalize_zero_amount_invoice :integer default("inherit"), not null
# firstname :string
# invoice_grace_period :integer
# lastname :string
# legal_name :string
# legal_number :string
# logo_url :string
# name :string
# net_payment_term :integer
# payment_provider :string
# payment_provider_code :string
# phone :string
# shipping_address_line1 :string
# shipping_address_line2 :string
# shipping_city :string
# shipping_country :string
# shipping_state :string
# shipping_zipcode :string
# slug :string
# state :string
# tax_identification_number :string
# timezone :string
# url :string
# vat_rate :float
# zipcode :string
# created_at :datetime not null
# updated_at :datetime not null
# external_id :string not null
# external_salesforce_id :string
# organization_id :uuid not null
# sequential_id :bigint
# id :uuid not null, primary key
# address_line1 :string
# address_line2 :string
# city :string
# country :string
# currency :string
# customer_type :enum
# deleted_at :datetime
# document_locale :string
# email :string
# exclude_from_dunning_campaign :boolean default(FALSE), not null
# finalize_zero_amount_invoice :integer default("inherit"), not null
# firstname :string
# invoice_grace_period :integer
# last_dunning_campaign_attempt :integer default(0), not null
# last_dunning_campaign_attempt_at :datetime
# lastname :string
# legal_name :string
# legal_number :string
# logo_url :string
# name :string
# net_payment_term :integer
# payment_provider :string
# payment_provider_code :string
# phone :string
# shipping_address_line1 :string
# shipping_address_line2 :string
# shipping_city :string
# shipping_country :string
# shipping_state :string
# shipping_zipcode :string
# slug :string
# state :string
# tax_identification_number :string
# timezone :string
# url :string
# vat_rate :float
# zipcode :string
# created_at :datetime not null
# updated_at :datetime not null
# applied_dunning_campaign_id :uuid
# external_id :string not null
# external_salesforce_id :string
# organization_id :uuid not null
# sequential_id :bigint
#
# Indexes
#
# index_customers_on_applied_dunning_campaign_id (applied_dunning_campaign_id)
# index_customers_on_deleted_at (deleted_at)
# index_customers_on_external_id_and_organization_id (external_id,organization_id) UNIQUE WHERE (deleted_at IS NULL)
# index_customers_on_organization_id (organization_id)
#
# Foreign Keys
#
# fk_rails_... (applied_dunning_campaign_id => dunning_campaigns.id)
# fk_rails_... (organization_id => organizations.id)
#
3 changes: 3 additions & 0 deletions app/models/dunning_campaign.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ class DunningCampaign < ApplicationRecord
include PaperTrailTraceable

belongs_to :organization

has_many :thresholds, class_name: "DunningCampaignThreshold", dependent: :destroy
has_many :customers, foreign_key: :applied_dunning_campaign_id, dependent: :nullify

accepts_nested_attributes_for :thresholds

validates :name, presence: true
Expand Down
14 changes: 14 additions & 0 deletions db/migrate/20241016104211_add_dunning_campaign_to_customers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

class AddDunningCampaignToCustomers < ActiveRecord::Migration[7.1]
def change
safety_assured do
change_table :customers, bulk: true do |t|
t.references :applied_dunning_campaign, type: :uuid, null: true, foreign_key: {to_table: :dunning_campaigns}, index: true
t.boolean :exclude_from_dunning_campaign, default: false, null: false
t.integer :last_dunning_campaign_attempt, default: 0, null: false
t.timestamp :last_dunning_campaign_attempt_at
end
end
end
end
8 changes: 7 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions spec/models/customer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

it_behaves_like 'paper_trail traceable'

it { is_expected.to belong_to(:applied_dunning_campaign).optional }

it { is_expected.to have_many(:integration_customers).dependent(:destroy) }
it { is_expected.to have_many(:payment_requests) }

Expand Down
1 change: 1 addition & 0 deletions spec/models/dunning_campaign_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

it { is_expected.to belong_to(:organization) }
it { is_expected.to have_many(:thresholds).dependent(:destroy) }
it { is_expected.to have_many(:customers).dependent(:nullify) }

it { is_expected.to validate_presence_of(:name) }

Expand Down

0 comments on commit 9c9ce33

Please sign in to comment.