Skip to content

Commit

Permalink
feat(dunning): validate dunning campaign has at least one threshold
Browse files Browse the repository at this point in the history
πŸ‘‰ 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

This change introduces validation to ensure dunning campaign at least
has one threshold
  • Loading branch information
ancorcruz committed Oct 31, 2024
1 parent 90accd3 commit 33ee119
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 0 deletions.
8 changes: 8 additions & 0 deletions app/models/dunning_campaign.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,20 @@ class DunningCampaign < ApplicationRecord
validates :days_between_attempts, numericality: {greater_than: 0}
validates :max_attempts, numericality: {greater_than: 0}
validates :code, uniqueness: {scope: :organization_id}
validate :must_have_at_least_one_threshold

scope :applied_to_organization, -> { where(applied_to_organization: true) }

def self.ransackable_attributes(_auth_object = nil)
%w[name code]
end

private

def must_have_at_least_one_threshold
errors.add(:thresholds, "must have at least one") if thresholds.empty?
end

end

# == Schema Information
Expand Down
4 changes: 4 additions & 0 deletions spec/factories/dunning_campaigns.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,9 @@
days_between_attempts { Faker::Number.number(digits: 2) }
max_attempts { Faker::Number.number(digits: 2) }
applied_to_organization { false }

after(:build) do |dunning_campaign|
dunning_campaign.thresholds << build(:dunning_campaign_threshold, dunning_campaign: dunning_campaign) if dunning_campaign.thresholds.empty?
end
end
end
7 changes: 7 additions & 0 deletions spec/models/dunning_campaign_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,11 @@
it { is_expected.to validate_numericality_of(:max_attempts).is_greater_than(0) }

it { is_expected.to validate_uniqueness_of(:code).scoped_to(:organization_id) }

it "is invalid without any thresholds" do
dunning_campaign = DunningCampaign.new

expect(dunning_campaign).not_to be_valid
expect(dunning_campaign.errors[:thresholds]).to include("must have at least one")
end
end
14 changes: 14 additions & 0 deletions spec/services/dunning_campaigns/create_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,19 @@
end
end
end

context "without thresholds" do
let(:thresholds) { [] }

it "returns an error" do
result = create_service.call

aggregate_failures do
expect(result).not_to be_success
expect(result.error).to be_a(BaseService::ValidationFailure)
expect(result.error.messages[:thresholds]).to eq(["must have at least one"])
end
end
end
end
end

0 comments on commit 33ee119

Please sign in to comment.