Skip to content

Commit

Permalink
feat(dunning): Refactor payment_requests and remove payable_groups (#…
Browse files Browse the repository at this point in the history
…2456)

## Context

We want to be able to manually request payment of the overdue balance
and send emails for reminders.


https://getlago.canny.io/feature-requests/p/send-reminders-for-overdue-invoices

## Description

The goal of this PR is to refactor how we define a payment request by
removing the payable group entity.

---------

Co-authored-by: Ancor Cruz <[email protected]>
  • Loading branch information
rsempe and ancorcruz authored Aug 22, 2024
1 parent aaae52d commit 681bdfa
Show file tree
Hide file tree
Showing 22 changed files with 161 additions and 198 deletions.
2 changes: 1 addition & 1 deletion app/controllers/api/v1/payment_requests_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def index
if result.success?
render(
json: ::CollectionSerializer.new(
result.payment_requests.preload(:customer, payment_requestable: :invoices),
result.payment_requests.preload(:customer, :invoices),
::V1::PaymentRequestSerializer,
collection_name: "payment_requests",
meta: pagination_metadata(result.payment_requests),
Expand Down
1 change: 0 additions & 1 deletion app/models/customer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class Customer < ApplicationRecord
has_many :add_ons, through: :applied_add_ons
has_many :wallets
has_many :wallet_transactions, through: :wallets
has_many :payable_groups
has_many :payment_provider_customers,
class_name: 'PaymentProviderCustomers::BaseCustomer',
dependent: :destroy
Expand Down
9 changes: 4 additions & 5 deletions app/models/invoice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,10 @@ class Invoice < ApplicationRecord

belongs_to :customer, -> { with_discarded }
belongs_to :organization
belongs_to :payable_group, optional: true

has_many :fees
has_many :credits
has_many :wallet_transactions
has_many :payments, as: :payable
has_many :payment_requests, as: :payment_requestable
has_many :invoice_subscriptions
has_many :subscriptions, through: :invoice_subscriptions
has_many :plans, through: :subscriptions
Expand All @@ -33,6 +30,10 @@ class Invoice < ApplicationRecord
has_many :integration_resources, as: :syncable
has_many :error_details, as: :owner, dependent: :destroy

has_many :applied_payment_requests, class_name: "PaymentRequest::AppliedInvoice"
has_many :payment_requests, through: :applied_payment_requests
has_many :payments, as: :payable

has_one_attached :file

monetize :coupons_amount_cents,
Expand Down Expand Up @@ -438,7 +439,6 @@ def status_changed_to_finalized?
# customer_id :uuid
# organization_id :uuid not null
# organization_sequential_id :integer default(0), not null
# payable_group_id :uuid
# sequential_id :integer
#
# Indexes
Expand All @@ -447,7 +447,6 @@ def status_changed_to_finalized?
# index_invoices_on_customer_id_and_sequential_id (customer_id,sequential_id) UNIQUE
# index_invoices_on_number (number)
# index_invoices_on_organization_id (organization_id)
# index_invoices_on_payable_group_id (payable_group_id)
# index_invoices_on_payment_overdue (payment_overdue)
# index_invoices_on_sequential_id (sequential_id)
# index_invoices_on_status (status)
Expand Down
1 change: 0 additions & 1 deletion app/models/organization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class Organization < ApplicationRecord
has_many :add_ons
has_many :invites
has_many :integrations, class_name: 'Integrations::BaseIntegration'
has_many :payable_groups
has_many :payment_providers, class_name: 'PaymentProviders::BaseProvider'
has_many :payment_requests
has_many :taxes
Expand Down
38 changes: 0 additions & 38 deletions app/models/payable_group.rb

This file was deleted.

4 changes: 0 additions & 4 deletions app/models/payment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ class Payment < ApplicationRecord
include PaperTrailTraceable

belongs_to :payable, polymorphic: true
belongs_to :payment_request, optional: true
belongs_to :payment_provider, optional: true, class_name: 'PaymentProviders::BaseProvider'
belongs_to :payment_provider_customer, class_name: 'PaymentProviderCustomers::BaseCustomer'

Expand Down Expand Up @@ -35,7 +34,6 @@ def should_sync_payment?
# payable_id :uuid
# payment_provider_customer_id :uuid
# payment_provider_id :uuid
# payment_request_id :uuid
# provider_payment_id :string not null
#
# Indexes
Expand All @@ -44,11 +42,9 @@ def should_sync_payment?
# index_payments_on_payable_type_and_payable_id (payable_type,payable_id)
# index_payments_on_payment_provider_customer_id (payment_provider_customer_id)
# index_payments_on_payment_provider_id (payment_provider_id)
# index_payments_on_payment_request_id (payment_request_id)
#
# Foreign Keys
#
# fk_rails_... (invoice_id => invoices.id)
# fk_rails_... (payment_provider_id => payment_providers.id)
# fk_rails_... (payment_request_id => payment_requests.id)
#
36 changes: 18 additions & 18 deletions app/models/payment_request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,40 @@
class PaymentRequest < ApplicationRecord
include PaperTrailTraceable

has_many :payments
has_many :applied_invoices, class_name: "PaymentRequest::AppliedInvoice"
has_many :invoices, through: :applied_invoices
has_many :payments, as: :payable

belongs_to :organization
belongs_to :customer, -> { with_discarded }
belongs_to :payment_requestable, polymorphic: true

validates :email, presence: true
validates :amount_cents, presence: true
validates :amount_currency, presence: true

def invoices
payment_requestable.is_a?(Invoice) ? [payment_requestable] : payment_requestable.invoices
end
PAYMENT_STATUS = %i[pending succeeded failed].freeze

enum payment_status: PAYMENT_STATUS, _prefix: :payment
end

# == Schema Information
#
# Table name: payment_requests
#
# id :uuid not null, primary key
# amount_cents :bigint default(0), not null
# amount_currency :string not null
# email :string not null
# payment_requestable_type :string default("Invoice"), not null
# created_at :datetime not null
# updated_at :datetime not null
# customer_id :uuid not null
# organization_id :uuid not null
# payment_requestable_id :uuid not null
# id :uuid not null, primary key
# amount_cents :bigint default(0), not null
# amount_currency :string not null
# email :string not null
# payment_status :integer default("pending"), not null
# created_at :datetime not null
# updated_at :datetime not null
# customer_id :uuid not null
# organization_id :uuid not null
#
# Indexes
#
# idx_on_payment_requestable_type_payment_requestable_b151968780 (payment_requestable_type,payment_requestable_id)
# index_payment_requests_on_customer_id (customer_id)
# index_payment_requests_on_organization_id (organization_id)
# index_payment_requests_on_customer_id (customer_id)
# index_payment_requests_on_organization_id (organization_id)
#
# Foreign Keys
#
Expand Down
32 changes: 32 additions & 0 deletions app/models/payment_request/applied_invoice.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

class PaymentRequest
class AppliedInvoice < ApplicationRecord
self.table_name = "invoices_payment_requests"

belongs_to :invoice
belongs_to :payment_request
end
end

# == Schema Information
#
# Table name: invoices_payment_requests
#
# id :uuid not null, primary key
# created_at :datetime not null
# updated_at :datetime not null
# invoice_id :uuid not null
# payment_request_id :uuid not null
#
# Indexes
#
# idx_on_invoice_id_payment_request_id_aa550779a4 (invoice_id,payment_request_id) UNIQUE
# index_invoices_payment_requests_on_invoice_id (invoice_id)
# index_invoices_payment_requests_on_payment_request_id (payment_request_id)
#
# Foreign Keys
#
# fk_rails_... (invoice_id => invoices.id)
# fk_rails_... (payment_request_id => payment_requests.id)
#
11 changes: 4 additions & 7 deletions app/services/payment_requests/create_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,13 @@ def call
end

ActiveRecord::Base.transaction do
# NOTE: Create payable group for the overdue invoices
payable_group = customer.payable_groups.create!(organization:)
invoices.update_all(payable_group_id: payable_group.id) # rubocop:disable Rails/SkipsModelValidations

# NOTE: Create payment request for the payable group
payment_request = payable_group.payment_requests.create!(
payment_request = customer.payment_requests.create!(
organization:,
customer:,
amount_cents: invoices.sum(:total_amount_cents),
amount_currency: invoices.first.currency,
email:
email:,
invoices:
)

# NOTE: Send payment_request.created webhook
Expand Down
45 changes: 45 additions & 0 deletions db/migrate/20240821093145_create_invoices_payment_requests.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true

class CreateInvoicesPaymentRequests < ActiveRecord::Migration[7.1]
def change
create_table :invoices_payment_requests, id: :uuid do |t|
t.references :invoice, type: :uuid, null: false, foreign_key: true
t.references :payment_request, type: :uuid, null: false, foreign_key: true
t.timestamps
end

add_index :invoices_payment_requests, %i[invoice_id payment_request_id], unique: true

remove_column :payments, :payment_request_id, :uuid

# NOTE: Migrate existing data from invoices to invoices_payment_requests
reversible do |dir|
dir.up do
execute <<-SQL
INSERT INTO invoices_payment_requests (invoice_id, payment_request_id, created_at, updated_at)
SELECT invoices.id, payment_requests.id, invoices.created_at, invoices.updated_at
FROM invoices
inner join payment_requests on payment_requests.payment_requestable_id = invoices.payable_group_id
and payment_requests.payment_requestable_type = 'PayableGroup'
WHERE payable_group_id IS NOT NULL
SQL
end
end

drop_table :payable_groups, id: :uuid do |t|
t.references :customer, type: :uuid, null: false, foreign_key: true
t.references :organization, type: :uuid, null: false, foreign_key: true
t.integer :payment_status, null: false, default: 0
t.timestamps
end

change_table :payment_requests, bulk: true do |t|
t.remove :payment_requestable_id, type: :uuid
t.remove :payment_requestable_type, type: :string

t.integer :payment_status, null: false, default: 0
end

remove_column :invoices, :payable_group_id, :uuid
end
end
33 changes: 13 additions & 20 deletions db/schema.rb

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

Loading

0 comments on commit 681bdfa

Please sign in to comment.