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 ff985e574679..2ff08ee2d791 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 1d48c4fc9229..b18477924caf 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 000000000000..a5cf2465537b --- /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 dd2348a4ca6a..1ad45691dfa6 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 51a5959a1e1d..fb95ca558ccf 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