From 1c848c58fad4fda5992c540a73096fc650ab930d Mon Sep 17 00:00:00 2001 From: Toon Willems Date: Mon, 26 Aug 2024 10:16:24 +0200 Subject: [PATCH] Add specs for credits behaviour --- ...create_from_progressive_billing_invoice.rb | 5 +- spec/rails_helper.rb | 6 ++ ...e_from_progressive_billing_invoice_spec.rb | 79 +++++++++++++++++++ .../progressive_billing_service_spec.rb | 17 +++- spec/spec_helper.rb | 7 ++ 5 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 spec/services/credit_notes/create_from_progressive_billing_invoice_spec.rb diff --git a/app/services/credit_notes/create_from_progressive_billing_invoice.rb b/app/services/credit_notes/create_from_progressive_billing_invoice.rb index ff985e57467..2ff08ee2d79 100644 --- a/app/services/credit_notes/create_from_progressive_billing_invoice.rb +++ b/app/services/credit_notes/create_from_progressive_billing_invoice.rb @@ -12,6 +12,7 @@ def initialize(progressive_billing_invoice:, amount:, reason: :other) def call return result unless amount.positive? + return result.forbidden_failure! unless progressive_billing_invoice.progressive_billing? # Important to call this method as it modifies @amount if needed items = calculate_items! @@ -34,7 +35,7 @@ def calculate_items! remaining = amount # The amount can be greater than a single fee amount. We'll keep on deducting until we've credited enough - progressive_billing_invoice.fees.each do |fee| + progressive_billing_invoice.fees.order(amount_cents: :desc).each do |fee| # no further credit remaining break if remaining.zero? @@ -49,7 +50,7 @@ def calculate_items! end # it could be that we have some amount remaining - # TODO: verify and check in v2 + # TODO(ProgressiveBilling): verify and check in v2 if remaining.positive? @amount -= remaining end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 1d48c4fc922..b18477924ca 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -8,6 +8,12 @@ require 'simplecov' require 'money-rails/test_helpers' +if ENV['RUBY_DEBUG_PORT'] + require 'debug/open_nonstop' +else + require 'debug' +end + def pp(*args) # Uncomment the following line if you can't find where you left a `pp` call # ap caller.first diff --git a/spec/services/credit_notes/create_from_progressive_billing_invoice_spec.rb b/spec/services/credit_notes/create_from_progressive_billing_invoice_spec.rb new file mode 100644 index 00000000000..a5cf2465537 --- /dev/null +++ b/spec/services/credit_notes/create_from_progressive_billing_invoice_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe CreditNotes::CreateFromProgressiveBillingInvoice, type: :service do + subject(:credit_service) { described_class.new(progressive_billing_invoice:, amount:, reason:) } + + let(:reason) { :other } + let(:amount) { 0 } + let(:invoice_type) { :progressive_billing } + let(:customer) { create(:customer) } + let(:organization) { customer.organization } + + let(:progressive_billing_invoice) do + create( + :invoice, + customer:, + organization:, + currency: 'EUR', + fees_amount_cents: 120, + total_amount_cents: 120, + invoice_type: + ) + end + + let(:fee1) do + create( + :fee, + invoice: progressive_billing_invoice, + amount_cents: 80 + ) + end + + let(:fee2) do + create( + :fee, + invoice: progressive_billing_invoice, + amount_cents: 40 + ) + end + + before do + progressive_billing_invoice + fee1 + fee2 + end + + describe "#call" do + it "does nothing when amount is zero" do + expect { credit_service.call }.not_to change(CreditNote, :count) + end + + context "with amount greater than zero" do + let(:amount) { 100 } + + context 'when called with a subscription invoice' do + let(:invoice_type) { :subscription } + + it "fails when the passed in invoice is not a progressive billing invoice" do + result = credit_service.call + expect(result).not_to be_success + end + end + + it "creates a credit note for all required fees" do + result = credit_service.call + credit_note = result.credit_note + + expect(credit_note.credit_amount_cents).to eq(amount) + expect(credit_note.items.size).to eq(2) + + credit_fee1 = credit_note.items.find { |i| i.fee == fee1 } + expect(credit_fee1.amount_cents).to eq(80) + credit_fee2 = credit_note.items.find { |i| i.fee == fee2 } + expect(credit_fee2.amount_cents).to eq(20) + end + end + end +end diff --git a/spec/services/credits/progressive_billing_service_spec.rb b/spec/services/credits/progressive_billing_service_spec.rb index dd2348a4ca6..1ad45691dfa 100644 --- a/spec/services/credits/progressive_billing_service_spec.rb +++ b/spec/services/credits/progressive_billing_service_spec.rb @@ -175,7 +175,7 @@ end describe "#call" do - it "applies one credit to the invoice" do + it "applies all the credits to the invoice" do result = credit_service.call expect(result.credits.size).to eq(2) first_credit = result.credits.find { |credit| credit.progressive_billing_invoice == progressive_billing_invoice } @@ -185,7 +185,20 @@ expect(first_credit.amount_cents).to eq(980) expect(invoice.progressive_billing_credit_amount_cents).to eq(1000) - # todo: validate that we've created a credit note here + end + + it "creates credit notes for the remainder of the progressive billed invoices" do + expect { credit_service.call }.to change(CreditNote, :count).by(2) + # we were able to credit 1000 from the invoice, this means we've got 20 and 200 remaining respectively + aggregate_failures do + expect(progressive_billing_invoice2.credit_notes.size).to eq(1) + expect(progressive_billing_invoice3.credit_notes.size).to eq(1) + + first = progressive_billing_invoice2.credit_notes.sole + expect(first.credit_amount_cents).to eq(20) + last = progressive_billing_invoice3.credit_notes.sole + expect(last.credit_amount_cents).to eq(200) + end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 51a5959a1e1..fb95ca558cc 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,13 @@ require 'webmock/rspec' +# Allow remote debugging when RUBY_DEBUG_PORT is set +if ENV['RUBY_DEBUG_PORT'] + require 'debug/open_nonstop' +else + require 'debug' +end + RSpec.configure do |config| config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true