Skip to content

Commit

Permalink
feat(ProgressiveBilling): Add progressive billing invoice relation to…
Browse files Browse the repository at this point in the history
… credit (#2440)

## Context

AI companies want their users to pay before the end of a period if usage
skyrockets. The problem being that self-serve companies can overuse
their API without paying, triggering lots of costs on their side.

## Description

This PR adds the `progressive_billing_invoice_id` foreign key column to
the `credits` table. It will allow us to take a progressive billing
invoice into account when generating a subscription invoice
  • Loading branch information
vincent-pochet authored Aug 20, 2024
1 parent fbab483 commit e2a9695
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 13 deletions.
35 changes: 23 additions & 12 deletions app/models/credit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -18,25 +19,32 @@ 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

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
Expand All @@ -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)
#
1 change: 1 addition & 0 deletions app/models/invoice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
5 changes: 4 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions spec/factories/credits.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
33 changes: 33 additions & 0 deletions spec/models/credit_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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:) }
Expand Down Expand Up @@ -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
2 changes: 2 additions & 0 deletions spec/models/invoice_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit e2a9695

Please sign in to comment.