From 9add396266414b522a12fde66fd1053aa8586de9 Mon Sep 17 00:00:00 2001 From: Lovro Colic Date: Mon, 9 Dec 2024 15:58:12 +0100 Subject: [PATCH 1/4] refactor how issuing date is set --- app/models/invoice.rb | 15 ++++----- .../update_invoice_grace_period_service.rb | 32 ++++++++----------- .../update_invoice_grace_period_service.rb | 32 ++++++++----------- spec/jobs/clock/finalize_invoices_job_spec.rb | 4 +-- spec/models/invoice_spec.rb | 18 +++++++++++ ...pdate_invoice_grace_period_service_spec.rb | 4 +-- ...pdate_invoice_grace_period_service_spec.rb | 4 +-- .../organizations/update_service_spec.rb | 4 +-- 8 files changed, 59 insertions(+), 54 deletions(-) diff --git a/app/models/invoice.rb b/app/models/invoice.rb index 1edd5075a26..811e108b14a 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -99,14 +99,13 @@ class Invoice < ApplicationRecord scope :ready_to_be_refreshed, -> { where(ready_to_be_refreshed: true) } scope :ready_to_be_finalized, lambda { - date = <<-SQL - ( - invoices.created_at + - COALESCE(customers.invoice_grace_period, organizations.invoice_grace_period) * INTERVAL '1 DAY' - ) - SQL - - draft.joins(:customer, :organization).where("#{Arel.sql(date)} < ?", Time.current) + draft + .joins(customer: :organization) + .where( + "issuing_date#{Utils::Timezone.at_time_zone_sql} <= " \ + "DATE(?#{Utils::Timezone.at_time_zone_sql})", + Time.current + ) } scope :created_before, diff --git a/app/services/customers/update_invoice_grace_period_service.rb b/app/services/customers/update_invoice_grace_period_service.rb index 030c6f7e212..b1ad786a8ce 100644 --- a/app/services/customers/update_invoice_grace_period_service.rb +++ b/app/services/customers/update_invoice_grace_period_service.rb @@ -4,27 +4,29 @@ module Customers class UpdateInvoiceGracePeriodService < BaseService def initialize(customer:, grace_period:) @customer = customer - @grace_period = grace_period + @grace_period = grace_period.to_i super end def call - if grace_period != customer.invoice_grace_period + old_grace_period = customer.invoice_grace_period.to_i + grace_period_diff = grace_period - old_grace_period + + if grace_period != old_grace_period customer.invoice_grace_period = grace_period customer.save! + # NOTE: Update issuing_date on draft invoices. + customer.invoices.draft.each do |invoice| + invoice.issuing_date = invoice.issuing_date + grace_period_diff.days + invoice.payment_due_date = grace_period_payment_due_date(invoice) + invoice.save! + end + # NOTE: Finalize related draft invoices. customer.invoices.ready_to_be_finalized.each do |invoice| Invoices::RefreshDraftAndFinalizeService.call(invoice:) end - - # NOTE: Update issuing_date on draft invoices. - customer.invoices.draft.each do |invoice| - invoice.update!( - issuing_date: grace_period_issuing_date(invoice), - payment_due_date: grace_period_payment_due_date(invoice) - ) - end end result.customer = customer @@ -35,16 +37,8 @@ def call attr_reader :customer, :grace_period - def invoice_created_at(invoice) - invoice.created_at.in_time_zone(customer.applicable_timezone).to_date - end - - def grace_period_issuing_date(invoice) - invoice_created_at(invoice) + customer.applicable_invoice_grace_period.days - end - def grace_period_payment_due_date(invoice) - grace_period_issuing_date(invoice) + customer.applicable_net_payment_term.days + invoice.issuing_date + customer.applicable_net_payment_term.days end end end diff --git a/app/services/organizations/update_invoice_grace_period_service.rb b/app/services/organizations/update_invoice_grace_period_service.rb index 595c0138186..8a7b6c88dfa 100644 --- a/app/services/organizations/update_invoice_grace_period_service.rb +++ b/app/services/organizations/update_invoice_grace_period_service.rb @@ -4,27 +4,29 @@ module Organizations class UpdateInvoiceGracePeriodService < BaseService def initialize(organization:, grace_period:) @organization = organization - @grace_period = grace_period + @grace_period = grace_period.to_i super end def call - if grace_period != organization.invoice_grace_period + old_grace_period = organization.invoice_grace_period.to_i + grace_period_diff = grace_period - old_grace_period + + if grace_period != old_grace_period organization.invoice_grace_period = grace_period organization.save! + # NOTE: Update issuing_date on draft invoices. + organization.invoices.draft.each do |invoice| + invoice.issuing_date = invoice.issuing_date + grace_period_diff.days + invoice.payment_due_date = grace_period_payment_due_date(invoice) + invoice.save! + end + # NOTE: Finalize related draft invoices. organization.invoices.ready_to_be_finalized.each do |invoice| Invoices::RefreshDraftAndFinalizeService.call(invoice:) end - - # NOTE: Update issuing_date on draft invoices. - organization.invoices.draft.each do |invoice| - invoice.update!( - issuing_date: grace_period_issuing_date(invoice), - payment_due_date: grace_period_payment_due_date(invoice) - ) - end end result.organization = organization @@ -35,16 +37,8 @@ def call attr_reader :organization, :grace_period - def invoice_created_at(invoice) - invoice.created_at.in_time_zone(invoice.customer.applicable_timezone).to_date - end - - def grace_period_issuing_date(invoice) - invoice_created_at(invoice) + invoice.customer.applicable_invoice_grace_period.days - end - def grace_period_payment_due_date(invoice) - grace_period_issuing_date(invoice) + invoice.customer.applicable_net_payment_term.days + invoice.issuing_date + invoice.customer.applicable_net_payment_term.days end end end diff --git a/spec/jobs/clock/finalize_invoices_job_spec.rb b/spec/jobs/clock/finalize_invoices_job_spec.rb index e8e58d4e796..33b6a32ae8f 100644 --- a/spec/jobs/clock/finalize_invoices_job_spec.rb +++ b/spec/jobs/clock/finalize_invoices_job_spec.rb @@ -11,7 +11,7 @@ create( :invoice, status: :draft, - created_at: DateTime.parse('20 Jun 2022'), + issuing_date: DateTime.parse('23 Jun 2022').to_date, customer:, organization: customer.organization ) @@ -20,7 +20,7 @@ create( :invoice, status: :finalized, - created_at: DateTime.parse('20 Jun 2022'), + issuing_date: DateTime.parse('23 Jun 2022').to_date, customer:, organization: customer.organization ) diff --git a/spec/models/invoice_spec.rb b/spec/models/invoice_spec.rb index ce813d26fca..282003dce99 100644 --- a/spec/models/invoice_spec.rb +++ b/spec/models/invoice_spec.rb @@ -137,6 +137,24 @@ end end + describe 'ready_to_be_finalized' do + let(:invoice) { create(:invoice, status: :draft, issuing_date: Time.current - 1.day) } + + before { invoice } + + it 'returns all invoices that are ready for finalization' do + expect(Invoice.ready_to_be_finalized.pluck(:id)).to include(invoice.id) + end + + context 'when issuing date has not been reached' do + let(:invoice) { create(:invoice, status: :draft, issuing_date: Time.current + 1.day) } + + it 'returns all invoices that are ready for finalization' do + expect(Invoice.ready_to_be_finalized.pluck(:id)).not_to include(invoice.id) + end + end + end + describe 'when status is visible' do it do described_class::VISIBLE_STATUS.keys.each do |status| diff --git a/spec/services/customers/update_invoice_grace_period_service_spec.rb b/spec/services/customers/update_invoice_grace_period_service_spec.rb index bab3673b6e7..b1b583884f8 100644 --- a/spec/services/customers/update_invoice_grace_period_service_spec.rb +++ b/spec/services/customers/update_invoice_grace_period_service_spec.rb @@ -12,11 +12,11 @@ describe '#call' do let(:invoice_to_be_finalized) do - create(:invoice, status: :draft, customer:, created_at: DateTime.parse('19 Jun 2022'), organization:) + create(:invoice, status: :draft, customer:, issuing_date: DateTime.parse('19 Jun 2022').to_date, organization:) end let(:invoice_to_not_be_finalized) do - create(:invoice, status: :draft, customer:, created_at: DateTime.parse('21 Jun 2022'), organization:) + create(:invoice, status: :draft, customer:, issuing_date: DateTime.parse('21 Jun 2022').to_date, organization:) end before do diff --git a/spec/services/organizations/update_invoice_grace_period_service_spec.rb b/spec/services/organizations/update_invoice_grace_period_service_spec.rb index a52b11a6f9c..a680a2866ae 100644 --- a/spec/services/organizations/update_invoice_grace_period_service_spec.rb +++ b/spec/services/organizations/update_invoice_grace_period_service_spec.rb @@ -12,11 +12,11 @@ describe '#call' do let(:invoice_to_be_finalized) do - create(:invoice, status: :draft, customer:, created_at: DateTime.parse('19 Jun 2022'), organization:) + create(:invoice, status: :draft, customer:, issuing_date: DateTime.parse('19 Jun 2022').to_date, organization:) end let(:invoice_to_not_be_finalized) do - create(:invoice, status: :draft, customer:, created_at: DateTime.parse('21 Jun 2022'), organization:) + create(:invoice, status: :draft, customer:, issuing_date: DateTime.parse('21 Jun 2022').to_date, organization:) end before do diff --git a/spec/services/organizations/update_service_spec.rb b/spec/services/organizations/update_service_spec.rb index b16eba2dd80..6a0c3aadc65 100644 --- a/spec/services/organizations/update_service_spec.rb +++ b/spec/services/organizations/update_service_spec.rb @@ -114,11 +114,11 @@ let(:customer) { create(:customer, organization:) } let(:invoice_to_be_finalized) do - create(:invoice, status: :draft, customer:, created_at: DateTime.parse('19 Jun 2022'), organization:) + create(:invoice, status: :draft, customer:, issuing_date: DateTime.parse('19 Jun 2022').to_date, organization:) end let(:invoice_to_not_be_finalized) do - create(:invoice, status: :draft, customer:, created_at: DateTime.parse('21 Jun 2022'), organization:) + create(:invoice, status: :draft, customer:, issuing_date: DateTime.parse('21 Jun 2022').to_date, organization:) end let(:invoice_grace_period) { 2 } From ab394bf8ae479b861fa5901e2a4943139270b8b0 Mon Sep 17 00:00:00 2001 From: Lovro Colic Date: Mon, 9 Dec 2024 16:17:32 +0100 Subject: [PATCH 2/4] fix linter issues --- spec/models/invoice_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/models/invoice_spec.rb b/spec/models/invoice_spec.rb index 282003dce99..ea64b77e5c4 100644 --- a/spec/models/invoice_spec.rb +++ b/spec/models/invoice_spec.rb @@ -143,14 +143,14 @@ before { invoice } it 'returns all invoices that are ready for finalization' do - expect(Invoice.ready_to_be_finalized.pluck(:id)).to include(invoice.id) + expect(described_class.ready_to_be_finalized.pluck(:id)).to include(invoice.id) end context 'when issuing date has not been reached' do let(:invoice) { create(:invoice, status: :draft, issuing_date: Time.current + 1.day) } it 'returns all invoices that are ready for finalization' do - expect(Invoice.ready_to_be_finalized.pluck(:id)).not_to include(invoice.id) + expect(described_class.ready_to_be_finalized.pluck(:id)).not_to include(invoice.id) end end end From 2c1d117a7881e046d3e4c5fc842b4b5efd2f7d83 Mon Sep 17 00:00:00 2001 From: Lovro Colic Date: Tue, 10 Dec 2024 15:51:13 +0100 Subject: [PATCH 3/4] update invoice ready_to_be_finalize scope and slightly adjust logic --- app/models/invoice.rb | 11 +---------- .../update_invoice_grace_period_service.rb | 6 +++--- .../update_invoice_grace_period_service.rb | 14 +++++++++++--- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/app/models/invoice.rb b/app/models/invoice.rb index 811e108b14a..51737e81ad1 100644 --- a/app/models/invoice.rb +++ b/app/models/invoice.rb @@ -97,16 +97,7 @@ class Invoice < ApplicationRecord scope :invisible, -> { where(status: INVISIBLE_STATUS.keys) } scope :with_generated_number, -> { where(status: %w[finalized voided]) } scope :ready_to_be_refreshed, -> { where(ready_to_be_refreshed: true) } - scope :ready_to_be_finalized, - lambda { - draft - .joins(customer: :organization) - .where( - "issuing_date#{Utils::Timezone.at_time_zone_sql} <= " \ - "DATE(?#{Utils::Timezone.at_time_zone_sql})", - Time.current - ) - } + scope :ready_to_be_finalized, -> { draft.where('issuing_date <= ?', Time.current.to_date) } scope :created_before, lambda { |invoice| diff --git a/app/services/customers/update_invoice_grace_period_service.rb b/app/services/customers/update_invoice_grace_period_service.rb index b1ad786a8ce..2a637f113c8 100644 --- a/app/services/customers/update_invoice_grace_period_service.rb +++ b/app/services/customers/update_invoice_grace_period_service.rb @@ -10,21 +10,21 @@ def initialize(customer:, grace_period:) def call old_grace_period = customer.invoice_grace_period.to_i - grace_period_diff = grace_period - old_grace_period + grace_period_diff = customer.applicable_invoice_grace_period.to_i - customer.applicable_invoice_grace_period.to_i if grace_period != old_grace_period customer.invoice_grace_period = grace_period customer.save! # NOTE: Update issuing_date on draft invoices. - customer.invoices.draft.each do |invoice| + customer.invoices.draft.find_each do |invoice| invoice.issuing_date = invoice.issuing_date + grace_period_diff.days invoice.payment_due_date = grace_period_payment_due_date(invoice) invoice.save! end # NOTE: Finalize related draft invoices. - customer.invoices.ready_to_be_finalized.each do |invoice| + customer.invoices.ready_to_be_finalized.find_each do |invoice| Invoices::RefreshDraftAndFinalizeService.call(invoice:) end end diff --git a/app/services/organizations/update_invoice_grace_period_service.rb b/app/services/organizations/update_invoice_grace_period_service.rb index 8a7b6c88dfa..6848613e848 100644 --- a/app/services/organizations/update_invoice_grace_period_service.rb +++ b/app/services/organizations/update_invoice_grace_period_service.rb @@ -10,21 +10,23 @@ def initialize(organization:, grace_period:) def call old_grace_period = organization.invoice_grace_period.to_i - grace_period_diff = grace_period - old_grace_period if grace_period != old_grace_period organization.invoice_grace_period = grace_period organization.save! # NOTE: Update issuing_date on draft invoices. - organization.invoices.draft.each do |invoice| + organization.invoices.draft.find_each do |invoice| + grace_period_diff = invoice.customer.applicable_invoice_grace_period.to_i - + old_applicable_grace_period(invoice.customer, old_grace_period) + invoice.issuing_date = invoice.issuing_date + grace_period_diff.days invoice.payment_due_date = grace_period_payment_due_date(invoice) invoice.save! end # NOTE: Finalize related draft invoices. - organization.invoices.ready_to_be_finalized.each do |invoice| + organization.invoices.ready_to_be_finalized.find_each do |invoice| Invoices::RefreshDraftAndFinalizeService.call(invoice:) end end @@ -40,5 +42,11 @@ def call def grace_period_payment_due_date(invoice) invoice.issuing_date + invoice.customer.applicable_net_payment_term.days end + + def old_applicable_grace_period(customer, old_org_grace_period) + return customer.invoice_grace_period if customer.invoice_grace_period.present? + + old_org_grace_period + end end end From 71ec130ba830f7c3b3f25b652ff271b5f47a3fe0 Mon Sep 17 00:00:00 2001 From: Lovro Colic Date: Tue, 10 Dec 2024 16:05:20 +0100 Subject: [PATCH 4/4] add small fix --- app/services/customers/update_invoice_grace_period_service.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/services/customers/update_invoice_grace_period_service.rb b/app/services/customers/update_invoice_grace_period_service.rb index 2a637f113c8..3ad0c64e11f 100644 --- a/app/services/customers/update_invoice_grace_period_service.rb +++ b/app/services/customers/update_invoice_grace_period_service.rb @@ -10,12 +10,14 @@ def initialize(customer:, grace_period:) def call old_grace_period = customer.invoice_grace_period.to_i - grace_period_diff = customer.applicable_invoice_grace_period.to_i - customer.applicable_invoice_grace_period.to_i + old_applicable_grace_period = customer.applicable_invoice_grace_period.to_i if grace_period != old_grace_period customer.invoice_grace_period = grace_period customer.save! + grace_period_diff = customer.applicable_invoice_grace_period.to_i - old_applicable_grace_period + # NOTE: Update issuing_date on draft invoices. customer.invoices.draft.find_each do |invoice| invoice.issuing_date = invoice.issuing_date + grace_period_diff.days