From a6c6967414720415e6181c94662265d4ce7f2811 Mon Sep 17 00:00:00 2001 From: Vincent Pochet Date: Fri, 23 Aug 2024 16:43:58 +0200 Subject: [PATCH] feat(ProgressiveBilling): Add label on invoice --- app/models/applied_usage_threshold.rb | 16 ++++++++++------ app/models/lifetime_usage.rb | 5 +++++ .../invoices/progressive_billing_service.rb | 15 +++++++-------- app/views/templates/invoices/v4.slim | 5 +++++ config/locales/en/invoice.yml | 1 + ...0823092643_create_applied_usage_thresholds.rb | 1 + db/schema.rb | 1 + spec/factories/applied_usage_thresholds.rb | 2 ++ spec/models/applied_usage_threshold_spec.rb | 7 +++---- spec/models/lifetime_usage_spec.rb | 10 ++++++++++ .../invoices/progressive_billing_service_spec.rb | 3 +++ 11 files changed, 48 insertions(+), 18 deletions(-) diff --git a/app/models/applied_usage_threshold.rb b/app/models/applied_usage_threshold.rb index 0bdcd5e9e220..fecd2e1b8277 100644 --- a/app/models/applied_usage_threshold.rb +++ b/app/models/applied_usage_threshold.rb @@ -1,21 +1,25 @@ # frozen_string_literal: true class AppliedUsageThreshold < ApplicationRecord - belongs_to :usage_threshold + belongs_to :usage_threshold, -> { with_discarded } belongs_to :invoice validates :usage_threshold_id, uniqueness: {scope: :invoice_id} + + monetize :lifetime_usage_amount_cents, + with_currency: ->(applied_usage_threshold) { applied_usage_threshold.invoice.currency } end # == Schema Information # # Table name: applied_usage_thresholds # -# id :uuid not null, primary key -# created_at :datetime not null -# updated_at :datetime not null -# invoice_id :uuid not null -# usage_threshold_id :uuid not null +# id :uuid not null, primary key +# lifetime_usage_amount_cents :bigint default(0), not null +# created_at :datetime not null +# updated_at :datetime not null +# invoice_id :uuid not null +# usage_threshold_id :uuid not null # # Indexes # diff --git a/app/models/lifetime_usage.rb b/app/models/lifetime_usage.rb index bacb1b270d90..44dc38d3f052 100644 --- a/app/models/lifetime_usage.rb +++ b/app/models/lifetime_usage.rb @@ -14,11 +14,16 @@ class LifetimeUsage < ApplicationRecord monetize :current_usage_amount_cents, :invoiced_usage_amount_cents, + :historical_usage_amount_cents, with_currency: ->(lifetime_usage) { lifetime_usage.subscription.plan.amount_currency } default_scope -> { kept } scope :needs_recalculation, -> { where(recalculate_current_usage: true).or(where(recalculate_invoiced_usage: true)) } + + def total_amount_cents + historical_usage_amount_cents + invoiced_usage_amount_cents + current_usage_amount_cents + end end # == Schema Information diff --git a/app/services/invoices/progressive_billing_service.rb b/app/services/invoices/progressive_billing_service.rb index 9295b129e3df..c146f6e34756 100644 --- a/app/services/invoices/progressive_billing_service.rb +++ b/app/services/invoices/progressive_billing_service.rb @@ -23,7 +23,6 @@ def call Credits::AppliedCouponsService.call(invoice:) Invoices::ComputeAmountsFromFees.call(invoice:) - create_credit_note_credit create_applied_prepaid_credit invoice.payment_status = invoice.total_amount_cents.positive? ? :pending : :succeeded @@ -109,19 +108,19 @@ def boundaries end def create_applied_usage_thresholds - usage_thresholds.each { AppliedUsageThreshold.create!(invoice:, usage_threshold: _1) } + usage_thresholds.each do + AppliedUsageThreshold.create!( + invoice:, + usage_threshold: _1, + lifetime_usage_amount_cents: lifetime_usage.total_amount_cents + ) + end end def should_deliver_email? License.premium? && subscription.organization.email_settings.include?('invoice.finalized') end - def create_credit_note_credit - credit_result = Credits::CreditNoteService.call(invoice:).raise_if_error! - - invoice.total_amount_cents -= credit_result.credits.sum(&:amount_cents) if credit_result.credits - end - def create_applied_prepaid_credit wallet = subscription.customer.wallets.active.first return unless wallet&.active? diff --git a/app/views/templates/invoices/v4.slim b/app/views/templates/invoices/v4.slim index 55ef494e608c..9671742e8671 100644 --- a/app/views/templates/invoices/v4.slim +++ b/app/views/templates/invoices/v4.slim @@ -462,6 +462,11 @@ html == SlimHelper.render('templates/invoices/v4/_eu_tax_management', self) + - if progressive_billing? + p.body-3.mb-24 + - applied_usage_threshold = applied_usage_thresholds.order(created_at: :asc).last + = I18n.t('invoice.reached_usage_threshold', usage_amount: MoneyHelper. applied_usage_threshold.lifetime_usage_amount, threshold_amount: MoneyHelper.format(applied_usage_threshold.threshold.amount_cents) + p.body-3.mb-24 = LineBreakHelper.break_lines(organization.invoice_footer) .powered-by diff --git a/config/locales/en/invoice.yml b/config/locales/en/invoice.yml index 56b66c61f9b0..cee74a3d9f03 100644 --- a/config/locales/en/invoice.yml +++ b/config/locales/en/invoice.yml @@ -55,6 +55,7 @@ en: progressive_billing_credit: Usage already billed quarter: quarter quarterly: Quarterly + reached_usage_threshold: This progressive billing invoice is generated because your lifetime usage has reached %{usage_amount}, exceeding the ${threshold_amount} threshold. see_breakdown: See breakdown for total unit sub_total: Subtotal sub_total_with_tax: Subtotal (incl. tax) diff --git a/db/migrate/20240823092643_create_applied_usage_thresholds.rb b/db/migrate/20240823092643_create_applied_usage_thresholds.rb index e3f45667fb20..e10c85e7abaa 100644 --- a/db/migrate/20240823092643_create_applied_usage_thresholds.rb +++ b/db/migrate/20240823092643_create_applied_usage_thresholds.rb @@ -5,6 +5,7 @@ def change create_table :applied_usage_thresholds, id: :uuid do |t| t.references :usage_threshold, null: false, foreign_key: true, type: :uuid t.references :invoice, null: false, foreign_key: true, type: :uuid + t.bigint :lifetime_usage_amount_cents, null: false, default: 0 t.timestamps diff --git a/db/schema.rb b/db/schema.rb index 42336df9a8ed..eeefd1d56cc7 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -132,6 +132,7 @@ create_table "applied_usage_thresholds", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.uuid "usage_threshold_id", null: false t.uuid "invoice_id", null: false + t.bigint "lifetime_usage_amount_cents", default: 0, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["invoice_id"], name: "index_applied_usage_thresholds_on_invoice_id" diff --git a/spec/factories/applied_usage_thresholds.rb b/spec/factories/applied_usage_thresholds.rb index 3a0e92b4e99b..276e4c631086 100644 --- a/spec/factories/applied_usage_thresholds.rb +++ b/spec/factories/applied_usage_thresholds.rb @@ -4,5 +4,7 @@ factory :applied_usage_threshold do usage_threshold invoice + + lifetime_usage_amount_cents { 100 } end end diff --git a/spec/models/applied_usage_threshold_spec.rb b/spec/models/applied_usage_threshold_spec.rb index e715f54f65b5..d00027cd699f 100644 --- a/spec/models/applied_usage_threshold_spec.rb +++ b/spec/models/applied_usage_threshold_spec.rb @@ -3,10 +3,9 @@ require 'rails_helper' RSpec.describe AppliedUsageThreshold, type: :model do - subject(:applied_usage_threshold) { build(:applied_usage_threshold) } + subject(:applied_usage_threshold) { create(:applied_usage_threshold, invoice:) } - it { is_expected.to belong_to(:usage_threshold) } - it { is_expected.to belong_to(:invoice) } + let(:invoice) { create(:invoice) } - it { is_expected.to validate_uniqueness_of(:usage_threshold_id).scoped_to(:invoice_id).case_insensitive } + it { is_expected.to belong_to(:usage_threshold) } end diff --git a/spec/models/lifetime_usage_spec.rb b/spec/models/lifetime_usage_spec.rb index 73a15a3f9c39..97eae27700c2 100644 --- a/spec/models/lifetime_usage_spec.rb +++ b/spec/models/lifetime_usage_spec.rb @@ -60,4 +60,14 @@ expect(described_class.needs_recalculation).to match_array([lifetime_usage1, lifetime_usage2]) end end + + describe '#total_amount_cents' do + it 'returns the sum of the historical, invoiced, and current usage' do + lifetime_usage.historical_usage_amount_cents = 100 + lifetime_usage.invoiced_usage_amount_cents = 200 + lifetime_usage.current_usage_amount_cents = 300 + + expect(lifetime_usage.total_amount_cents).to eq(600) + end + end end diff --git a/spec/services/invoices/progressive_billing_service_spec.rb b/spec/services/invoices/progressive_billing_service_spec.rb index 2404b2f9a8ef..f3b4f724ee1e 100644 --- a/spec/services/invoices/progressive_billing_service_spec.rb +++ b/spec/services/invoices/progressive_billing_service_spec.rb @@ -63,6 +63,9 @@ expect(invoice.invoice_subscriptions.count).to eq(1) expect(invoice.fees.count).to eq(1) expect(invoice.applied_usage_thresholds.count).to eq(1) + + expect(invoice.applied_usage_thresholds.first.lifetime_usage_amount_cents) + .to eq(lifetime_usage.total_amount_cents) end context 'with multiple thresholds' do