Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat(invoice_custom_sections): api controllers #3007

Merged
merged 32 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
437c855
Add InvoiceCustomSections::UpdateService
annvelents Dec 6, 2024
96e6c4f
add destroy service to invoice_custom_sections family
annvelents Dec 9, 2024
7b2a62c
WIP adding destroy mutation
annvelents Dec 10, 2024
a04cd65
add single invoice_custom_section resolver
annvelents Dec 11, 2024
c8011e9
fix the tests
annvelents Dec 11, 2024
7051cd3
fix linter
annvelents Dec 11, 2024
2745433
fix rebase conflict
annvelents Dec 20, 2024
4eab213
Add InvoiceCustomSections::UpdateService
annvelents Dec 6, 2024
ffd5867
add destroy service to invoice_custom_sections family
annvelents Dec 9, 2024
fcf806d
WIP adding destroy mutation
annvelents Dec 10, 2024
e84efc6
add single invoice_custom_section resolver
annvelents Dec 11, 2024
e8cabad
fix the tests
annvelents Dec 11, 2024
413b539
fix linter
annvelents Dec 11, 2024
c8b5648
WIP update customer with invoice_custom sections service
annvelents Dec 13, 2024
6544c27
refactoring of the select and deselect services
annvelents Dec 13, 2024
4ef7bf6
fix rebase conflict
annvelents Dec 20, 2024
49a698d
fix and add tests
annvelents Dec 23, 2024
08b35b4
fix linter
annvelents Dec 23, 2024
0d0dee2
refactor managing invoice_custom_sections
annvelents Dec 30, 2024
51eb397
fix index on code uniqueness to apply only on not deleted ics
annvelents Dec 27, 2024
14597eb
split index removing and addition
annvelents Dec 27, 2024
f416378
add controller for invoice_custom_sections
annvelents Dec 26, 2024
92d44b4
add routes for ics on customer level
annvelents Dec 26, 2024
9ecb177
fix linter
annvelents Dec 26, 2024
889c78c
update available api actions
annvelents Jan 6, 2025
1abc819
more updates of the api logic
annvelents Jan 6, 2025
dad304f
WIP adding tests
annvelents Jan 6, 2025
68cefa3
working on tests enhansing
annvelents Jan 7, 2025
50a46ed
fix linter
annvelents Jan 7, 2025
22b7d7a
conflicts after rebase
annvelents Jan 9, 2025
898f811
code review changes
annvelents Jan 10, 2025
79d4545
Feat(invoice_custom_sections): add create_invoice_applied_custom_sect…
annvelents Jan 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions app/controllers/api/v1/customers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def create_params
:net_payment_term,
:external_salesforce_id,
:finalize_zero_amount_invoice,
:skip_invoice_custom_sections,
integration_customers: [
:id,
:external_customer_id,
Expand Down Expand Up @@ -156,7 +157,8 @@ def create_params
:state,
:country
],
tax_codes: []
tax_codes: [],
invoice_custom_section_codes: []
)
end

Expand All @@ -165,7 +167,7 @@ def render_customer(customer)
json: ::V1::CustomerSerializer.new(
customer,
root_name: 'customer',
includes: %i[taxes integration_customers]
includes: %i[taxes integration_customers applicable_invoice_custom_sections]
)
)
end
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api/v1/invoices_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def render_invoice(invoice)
json: ::V1::InvoiceSerializer.new(
invoice,
root_name: 'invoice',
includes: %i[customer integration_customers subscriptions fees credits metadata applied_taxes error_details]
includes: %i[customer integration_customers subscriptions fees credits metadata applied_taxes error_details applied_invoice_custom_sections]
)
)
end
Expand Down
11 changes: 7 additions & 4 deletions app/graphql/resolvers/invoice_custom_sections_resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@ class InvoiceCustomSectionsResolver < Resolvers::BaseResolver
type Types::InvoiceCustomSections::Object.collection_type, null: true

def resolve(page: nil, limit: nil)
current_organization.invoice_custom_sections.left_outer_joins(:invoice_custom_section_selections).order(
Arel.sql('CASE WHEN invoice_custom_section_selections.id IS NOT NULL THEN 0 ELSE 1 END'),
:name
).page(page).per(limit)
current_organization.invoice_custom_sections
.joins('LEFT JOIN invoice_custom_section_selections ON invoice_custom_sections.id = invoice_custom_section_selections.invoice_custom_section_id
AND invoice_custom_section_selections.customer_id is NULL')
.order(
Arel.sql('CASE WHEN invoice_custom_section_selections.id IS NOT NULL THEN 0 ELSE 1 END'),
:name
).page(page).per(limit)
end
end
end
2 changes: 1 addition & 1 deletion app/models/api_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class ApiKey < ApplicationRecord
RESOURCES = %w[
add_on analytic billable_metric coupon applied_coupon credit_note customer_usage
customer event fee invoice organization payment_request plan subscription lifetime_usage
tax wallet wallet_transaction webhook_endpoint webhook_jwt_public_key
tax wallet wallet_transaction webhook_endpoint webhook_jwt_public_key invoice_custom_section
].freeze

MODES = %w[read write].freeze
Expand Down
2 changes: 1 addition & 1 deletion app/models/customer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ def applicable_net_payment_term
def applicable_invoice_custom_sections
return [] if skip_invoice_custom_sections?

selected_invoice_custom_sections.presence || organization.selected_invoice_custom_sections
selected_invoice_custom_sections.order(:name).presence || organization.selected_invoice_custom_sections.order(:name)
end

def editable?
Expand Down
4 changes: 3 additions & 1 deletion app/models/invoice_custom_section.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ class InvoiceCustomSection < ApplicationRecord
has_many :invoice_custom_section_selections, dependent: :destroy

validates :name, presence: true
validates :code, presence: true, uniqueness: {scope: :organization_id}
validates :code,
presence: true,
uniqueness: {conditions: -> { where(deleted_at: nil) }, scope: :organization_id}

default_scope -> { kept }

Expand Down
12 changes: 11 additions & 1 deletion app/serializers/v1/customer_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ def serialize
external_salesforce_id: model.external_salesforce_id,
finalize_zero_amount_invoice: model.finalize_zero_amount_invoice,
billing_configuration:,
shipping_address: model.shipping_address
shipping_address: model.shipping_address,
skip_invoice_custom_sections: model.skip_invoice_custom_sections
}

payload = payload.merge(metadata)
payload = payload.merge(taxes) if include?(:taxes)
payload = payload.merge(vies_check) if include?(:vies_check)
payload = payload.merge(integration_customers) if include?(:integration_customers)
payload = payload.merge(applicable_invoice_custom_sections) if include?(:applicable_invoice_custom_sections)

payload
end
Expand Down Expand Up @@ -98,5 +100,13 @@ def integration_customers
collection_name: 'integration_customers'
).serialize
end

def applicable_invoice_custom_sections
::CollectionSerializer.new(
model.applicable_invoice_custom_sections,
::V1::InvoiceCustomSectionSerializer,
collection_name: 'applicable_invoice_custom_sections'
).serialize
end
end
end
18 changes: 18 additions & 0 deletions app/serializers/v1/invoice_custom_section_serializer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module V1
class InvoiceCustomSectionSerializer < ModelSerializer
def serialize
{
lago_id: model.id,
code: model.code,
name: model.name,
description: model.description,
details: model.details,
display_name: model.display_name,
applied_to_organization: model.selected_for_organization?,
organization_id: model.organization_id
}
end
end
end
9 changes: 9 additions & 0 deletions app/serializers/v1/invoice_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def serialize
payload.merge!(applied_taxes) if include?(:applied_taxes)
payload.merge!(error_details) if include?(:error_details)
payload.merge!(applied_usage_thresholds) if model.progressive_billing?
payload.merge!(applied_invoice_custom_sections) if include?(:applied_invoice_custom_sections)

payload
end
Expand Down Expand Up @@ -112,5 +113,13 @@ def applied_usage_thresholds
collection_name: 'applied_usage_thresholds'
).serialize
end

def applied_invoice_custom_sections
::CollectionSerializer.new(
model.applied_invoice_custom_sections,
::V1::Invoices::AppliedInvoiceCustomSectionSerializer,
collection_name: 'applied_invoice_custom_sections'
).serialize
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module V1
module Invoices
class AppliedInvoiceCustomSectionSerializer < ModelSerializer
def serialize
{
lago_id: model.id,
lago_invoice_id: model.invoice_id,
code: model.code,
details: model.details,
display_name: model.display_name,
created_at: model.created_at.iso8601
}
end
end
end
end
6 changes: 6 additions & 0 deletions app/services/customers/create_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ def create_from_api(organization:, params:)
taxes_result.raise_if_error!
end

Customers::ManageInvoiceCustomSectionsService.call(
customer:,
skip_invoice_custom_sections: params[:skip_invoice_custom_sections],
section_codes: params[:invoice_custom_section_codes]
).raise_if_error!

if new_customer && params[:metadata]
params[:metadata].each { |m| create_metadata(customer:, args: m) }
elsif params[:metadata]
Expand Down
25 changes: 18 additions & 7 deletions app/services/customers/manage_invoice_custom_sections_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,30 @@

module Customers
class ManageInvoiceCustomSectionsService < BaseService
def initialize(customer:, skip_invoice_custom_sections:, section_ids:)
def initialize(customer:, skip_invoice_custom_sections:, section_ids: nil, section_codes: nil)
@customer = customer
@section_ids = section_ids
@section_codes = section_codes
@skip_invoice_custom_sections = skip_invoice_custom_sections

super
end

def call
return result.not_found_failure!(resource: "customer") unless customer
raise_invalid_params if skip_invoice_custom_sections && section_ids.present?
return fail_with_double_selection if !section_ids.nil? && !section_codes.nil?
return fail_with_invalid_params if skip_invoice_custom_sections && !(section_ids || section_codes).nil?

ActiveRecord::Base.transaction do
unless skip_invoice_custom_sections.nil?
if !skip_invoice_custom_sections.nil?
customer.selected_invoice_custom_sections = [] if !!skip_invoice_custom_sections
customer.skip_invoice_custom_sections = skip_invoice_custom_sections
end

unless section_ids.nil?
if !section_ids.nil? || !section_codes.nil?
customer.skip_invoice_custom_sections = false
return result if customer.applicable_invoice_custom_sections.ids == section_ids
return result if customer.selected_invoice_custom_sections.ids == section_ids ||
customer.selected_invoice_custom_sections.map(&:code) == section_codes

assign_selected_sections
end
Expand All @@ -35,14 +38,22 @@ def call

private

attr_reader :customer, :section_ids, :skip_invoice_custom_sections
attr_reader :customer, :section_ids, :skip_invoice_custom_sections, :section_codes

def raise_invalid_params
def fail_with_double_selection
result.validation_failure!(errors: {invoice_custom_sections: ['section_ids_and_section_codes_sent_together']})
end

def fail_with_invalid_params
result.validation_failure!(errors: {invoice_custom_sections: ['skip_sections_and_selected_ids_sent_together']})
end

def assign_selected_sections
# Note: when assigning organization's sections, an empty array will be sent
if section_ids.nil?
return customer.selected_invoice_custom_sections = customer.organization.invoice_custom_sections.where(code: section_codes)
end

customer.selected_invoice_custom_sections = customer.organization.invoice_custom_sections.where(id: section_ids)
end
end
Expand Down
2 changes: 2 additions & 0 deletions app/services/invoice_custom_sections/update_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ def initialize(invoice_custom_section:, update_params:, selected: false)
end

def call
return result.not_found_failure!(resource: 'invoice_custom_section') unless invoice_custom_section

invoice_custom_section.update!(update_params)
if selected
Organizations::SelectInvoiceCustomSectionService.call(section: invoice_custom_section)
Expand Down
1 change: 1 addition & 0 deletions app/services/invoices/add_on_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def create

create_add_on_fee(invoice)
compute_amounts(invoice)
Invoices::ApplyInvoiceCustomSectionsService.call(invoice:)

invoice.save!

Expand Down
1 change: 1 addition & 0 deletions app/services/invoices/advance_charges_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def create_group_invoice
end

Invoices::ComputeAmountsFromFees.call(invoice:)
Invoices::ApplyInvoiceCustomSectionsService.call(invoice:)

invoice.payment_status = :succeeded
Invoices::TransitionToFinalStatusService.call(invoice:)
Expand Down
34 changes: 34 additions & 0 deletions app/services/invoices/apply_invoice_custom_sections_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

module Invoices
class ApplyInvoiceCustomSectionsService < BaseService
def initialize(invoice:)
@invoice = invoice
@customer = invoice.customer

super()
end

def call
result.applied_sections = []
return result if customer.skip_invoice_custom_sections

customer.applicable_invoice_custom_sections.each do |custom_section|
invoice.applied_invoice_custom_sections.create!(
code: custom_section.code,
details: custom_section.details,
display_name: custom_section.display_name,
name: custom_section.name
)
end
result.applied_sections = invoice.applied_invoice_custom_sections
result
rescue ActiveRecord::RecordInvalid => e
result.record_validation_failure!(record: e.record)
end

private

attr_reader :invoice, :customer
end
end
1 change: 1 addition & 0 deletions app/services/invoices/create_one_off_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def call
end

Invoices::ComputeAmountsFromFees.call(invoice:, provider_taxes: result.fees_taxes)
Invoices::ApplyInvoiceCustomSectionsService.call(invoice:)
invoice.payment_status = invoice.total_amount_cents.positive? ? :pending : :succeeded
Invoices::TransitionToFinalStatusService.call(invoice:)
invoice.save!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def call
Invoices::ComputeAmountsFromFees.call(invoice:, provider_taxes: result.fees_taxes)
create_credit_note_credit
create_applied_prepaid_credit if should_create_applied_prepaid_credit?
Invoices::ApplyInvoiceCustomSectionsService.call(invoice:)

invoice.payment_status = invoice.total_amount_cents.positive? ? :pending : :succeeded
Invoices::TransitionToFinalStatusService.call(invoice:)
Expand Down
1 change: 1 addition & 0 deletions app/services/invoices/paid_credit_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def call
ActiveRecord::Base.transaction do
create_credit_fee(invoice)
compute_amounts(invoice)
Invoices::ApplyInvoiceCustomSectionsService.call(invoice:)

if License.premium? && wallet_transaction.invoice_requires_successful_payment?
invoice.open!
Expand Down
1 change: 1 addition & 0 deletions app/services/invoices/progressive_billing_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def call

Credits::ProgressiveBillingService.call(invoice:)
Credits::AppliedCouponsService.call(invoice:)
Invoices::ApplyInvoiceCustomSectionsService.call(invoice:)

totals_result = Invoices::ComputeTaxesAndTotalsService.call(invoice:)
return totals_result if !totals_result.success? && totals_result.error.is_a?(BaseService::UnknownTaxFailure)
Expand Down
3 changes: 3 additions & 0 deletions app/services/invoices/refresh_draft_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def call
recurring:,
context:
)
Invoices::ApplyInvoiceCustomSectionsService.call(invoice:)

invoice.credit_notes.each do |credit_note|
subscription_id = cn_subscription_ids.find { |h| h[:credit_note_id] == credit_note.id }[:subscription_id]
Expand Down Expand Up @@ -121,6 +122,7 @@ def reset_invoice_values
invoice_subscriptions.destroy_all
invoice.applied_taxes.destroy_all
invoice.error_details.discard_all
invoice.applied_invoice_custom_sections.destroy_all

invoice.taxes_amount_cents = 0
invoice.total_amount_cents = 0
Expand All @@ -129,6 +131,7 @@ def reset_invoice_values
invoice.sub_total_excluding_taxes_amount_cents = 0
invoice.sub_total_including_taxes_amount_cents = 0
invoice.progressive_billing_credit_amount_cents = 0

invoice.save!
end
end
Expand Down
1 change: 1 addition & 0 deletions app/services/invoices/subscription_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def call
recurring:,
context:
)
Invoices::ApplyInvoiceCustomSectionsService.call(invoice:)

set_invoice_generated_status unless invoice.pending?
invoice.save!
Expand Down
2 changes: 1 addition & 1 deletion app/services/webhooks/invoices/add_on_created_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def object_serializer
::V1::InvoiceSerializer.new(
object,
root_name: 'invoice',
includes: %i[customer subscriptions fees]
includes: %i[customer subscriptions fees applied_invoice_custom_sections]
)
end

Expand Down
2 changes: 1 addition & 1 deletion app/services/webhooks/invoices/created_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def object_serializer
::V1::InvoiceSerializer.new(
object,
root_name: 'invoice',
includes: %i[customer subscriptions fees credits applied_taxes]
includes: %i[customer subscriptions fees credits applied_taxes applied_invoice_custom_sections]
)
end

Expand Down
2 changes: 1 addition & 1 deletion app/services/webhooks/invoices/one_off_created_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def object_serializer
::V1::InvoiceSerializer.new(
object,
root_name: 'invoice',
includes: %i[customer fees applied_taxes]
includes: %i[customer fees applied_taxes applied_invoice_custom_sections]
)
end

Expand Down
Loading
Loading