From 1041b0b25231df98ff41eb23095e813674a4a49a Mon Sep 17 00:00:00 2001 From: Vincent Pochet Date: Tue, 20 Aug 2024 11:30:59 +0200 Subject: [PATCH] feat(ProgressiveBilling): Add progressive billing invoice relation to credit --- app/models/credit.rb | 35 ++++++++++++------- app/models/invoice.rb | 1 + ...progressive_billing_invoices_to_credits.rb | 9 +++++ db/schema.rb | 5 ++- spec/factories/credits.rb | 8 +++++ spec/models/credit_spec.rb | 33 +++++++++++++++++ spec/models/invoice_spec.rb | 2 ++ 7 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 db/migrate/20240820090312_attach_progressive_billing_invoices_to_credits.rb diff --git a/app/models/credit.rb b/app/models/credit.rb index 7a05521705c..d3050c6f5ee 100644 --- a/app/models/credit.rb +++ b/app/models/credit.rb @@ -6,6 +6,7 @@ class Credit < ApplicationRecord belongs_to :invoice belongs_to :applied_coupon, optional: true belongs_to :credit_note, optional: true + belongs_to :progressive_billing_invoice, class_name: 'Invoice', optional: true has_one :coupon, -> { with_discarded }, through: :applied_coupon @@ -18,18 +19,21 @@ class Credit < ApplicationRecord def item_id return coupon&.id if applied_coupon_id + return progressive_billing_invoice_id if progressive_billing_invoice_id? credit_note.id end def item_type return 'coupon' if applied_coupon_id? + return 'invoice' if progressive_billing_invoice_id? 'credit_note' end def item_code return coupon&.code if applied_coupon_id? + return progressive_billing_invoice.number if progressive_billing_invoice_id? credit_note.number end @@ -37,6 +41,10 @@ def item_code def item_name return coupon&.name if applied_coupon_id? + if progressive_billing_invoice_id? + return progressive_billing_invoice.fees.first&.invoice_name + end + # TODO: change it depending on invoice template credit_note.invoice.number end @@ -62,25 +70,28 @@ def invoice_coupon_display_name # # Table name: credits # -# id :uuid not null, primary key -# amount_cents :bigint not null -# amount_currency :string not null -# before_taxes :boolean default(FALSE), not null -# created_at :datetime not null -# updated_at :datetime not null -# applied_coupon_id :uuid -# credit_note_id :uuid -# invoice_id :uuid +# id :uuid not null, primary key +# amount_cents :bigint not null +# amount_currency :string not null +# before_taxes :boolean default(FALSE), not null +# created_at :datetime not null +# updated_at :datetime not null +# applied_coupon_id :uuid +# credit_note_id :uuid +# invoice_id :uuid +# progressive_billing_invoice_id :uuid # # Indexes # -# index_credits_on_applied_coupon_id (applied_coupon_id) -# index_credits_on_credit_note_id (credit_note_id) -# index_credits_on_invoice_id (invoice_id) +# index_credits_on_applied_coupon_id (applied_coupon_id) +# index_credits_on_credit_note_id (credit_note_id) +# index_credits_on_invoice_id (invoice_id) +# index_credits_on_progressive_billing_invoice_id (progressive_billing_invoice_id) # # Foreign Keys # # fk_rails_... (applied_coupon_id => applied_coupons.id) # fk_rails_... (credit_note_id => credit_notes.id) # fk_rails_... (invoice_id => invoices.id) +# fk_rails_... (progressive_billing_invoice_id => invoices.id) # diff --git a/app/models/invoice.rb b/app/models/invoice.rb index eacd0840f36..e1fbdc65ca7 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -26,6 +26,7 @@ class Invoice < ApplicationRecord has_many :plans, through: :subscriptions has_many :metadata, class_name: 'Metadata::InvoiceMetadata', dependent: :destroy has_many :credit_notes + has_many :progressive_billing_credits, class_name: 'Credit', foreign_key: :progressive_billing_invoice_id has_many :applied_taxes, class_name: 'Invoice::AppliedTax', dependent: :destroy has_many :taxes, through: :applied_taxes diff --git a/db/migrate/20240820090312_attach_progressive_billing_invoices_to_credits.rb b/db/migrate/20240820090312_attach_progressive_billing_invoices_to_credits.rb new file mode 100644 index 00000000000..e2b57390e7d --- /dev/null +++ b/db/migrate/20240820090312_attach_progressive_billing_invoices_to_credits.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class AttachProgressiveBillingInvoicesToCredits < ActiveRecord::Migration[7.1] + def change + add_column :credits, :progressive_billing_invoice_id, :uuid + add_foreign_key :credits, :invoices, column: :progressive_billing_invoice_id + add_index :credits, :progressive_billing_invoice_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 2ded0643132..e8e845d5351 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_08_19_092354) do +ActiveRecord::Schema[7.1].define(version: 2024_08_20_090312) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -372,9 +372,11 @@ t.datetime "updated_at", null: false t.uuid "credit_note_id" t.boolean "before_taxes", default: false, null: false + t.uuid "progressive_billing_invoice_id" t.index ["applied_coupon_id"], name: "index_credits_on_applied_coupon_id" t.index ["credit_note_id"], name: "index_credits_on_credit_note_id" t.index ["invoice_id"], name: "index_credits_on_invoice_id" + t.index ["progressive_billing_invoice_id"], name: "index_credits_on_progressive_billing_invoice_id" end create_table "customer_metadata", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| @@ -1202,6 +1204,7 @@ add_foreign_key "credits", "applied_coupons" add_foreign_key "credits", "credit_notes" add_foreign_key "credits", "invoices" + add_foreign_key "credits", "invoices", column: "progressive_billing_invoice_id" add_foreign_key "customer_metadata", "customers" add_foreign_key "customers", "organizations" add_foreign_key "customers_taxes", "customers" diff --git a/spec/factories/credits.rb b/spec/factories/credits.rb index c40ed6d6b94..1af7fbc4cba 100644 --- a/spec/factories/credits.rb +++ b/spec/factories/credits.rb @@ -16,4 +16,12 @@ amount_cents { 200 } amount_currency { 'EUR' } end + + factory :progressive_billing_invoice_credit, class: 'Credit' do + invoice + progressive_billing_invoice factory: :invoice + + amount_cents { 200 } + amount_currency { 'EUR' } + end end diff --git a/spec/models/credit_spec.rb b/spec/models/credit_spec.rb index 6865a8de503..19862f450cf 100644 --- a/spec/models/credit_spec.rb +++ b/spec/models/credit_spec.rb @@ -3,6 +3,15 @@ require 'rails_helper' RSpec.describe Credit, type: :model do + subject(:credit) { create(:credit) } + + describe 'associations' do + it { is_expected.to belong_to(:invoice) } + it { is_expected.to belong_to(:applied_coupon).optional } + it { is_expected.to belong_to(:credit_note).optional } + it { is_expected.to belong_to(:progressive_billing_invoice).optional } + end + describe 'invoice item' do context 'when credit is a coupon' do subject(:credit) { create(:credit, applied_coupon:) } @@ -63,5 +72,29 @@ end end end + + context 'when credit is a progressive billing invoice' do + subject(:credit) { create(:progressive_billing_invoice_credit, progressive_billing_invoice:) } + + let(:progressive_billing_invoice) { create(:invoice, invoice_type: :progressive_billing) } + let(:progressive_billing_fee) do + create( + :fee, + invoice: progressive_billing_invoice, + fee_type: :progressive_billing + ) + end + + before { progressive_billing_fee } + + it 'returns invoice details', aggregate_failures: true do + aggregate_failures do + expect(credit.item_id).to eq(progressive_billing_invoice.id) + expect(credit.item_type).to eq('invoice') + expect(credit.item_code).to eq(progressive_billing_invoice.number) + expect(credit.item_name).to eq(progressive_billing_fee.invoice_name) + end + end + end end end diff --git a/spec/models/invoice_spec.rb b/spec/models/invoice_spec.rb index 039b7c94e14..709b30b9ba4 100644 --- a/spec/models/invoice_spec.rb +++ b/spec/models/invoice_spec.rb @@ -14,6 +14,8 @@ it { is_expected.to have_many(:payment_requests) } it { is_expected.to belong_to(:payable_group).optional } + it { is_expected.to have_many(:progressive_billing_credits) } + it 'has fixed status mapping' do expect(described_class::VISIBLE_STATUS).to match(draft: 0, finalized: 1, voided: 2, failed: 4) expect(described_class::INVISIBLE_STATUS).to match(generating: 3, open: 5)