diff --git a/app/models/credit_note_item.rb b/app/models/credit_note_item.rb index 55d2c4421b9..f82742af8dd 100644 --- a/app/models/credit_note_item.rb +++ b/app/models/credit_note_item.rb @@ -11,6 +11,16 @@ class CreditNoteItem < ApplicationRecord def applied_taxes credit_note.applied_taxes.where(tax_code: fee.applied_taxes.select('fees_taxes.tax_code')) end + + # This method returns item amount with coupons applied + # coupons are applied proportionally to the way they're applied on corresponding fee + # so knowing the item total proportion to fee total we can calculate item amount with coupons + def sub_total_excluding_taxes_amount_cents + return 0 if amount_cents.zero? || fee.amount_cents.zero? + + item_proportion_to_fee = amount_cents.to_f / fee.amount_cents + item_proportion_to_fee * fee.sub_total_excluding_taxes_amount_cents + end end # == Schema Information diff --git a/app/services/credit_notes/create_service.rb b/app/services/credit_notes/create_service.rb index ea2aa212651..410d4a0da4d 100644 --- a/app/services/credit_notes/create_service.rb +++ b/app/services/credit_notes/create_service.rb @@ -60,6 +60,7 @@ def call deliver_webhook deliver_email handle_refund if should_handle_refund? + report_to_tax_provider if credit_note.should_sync_credit_note? Integrations::Aggregator::CreditNotes::CreateJob.perform_later(credit_note:) @@ -190,6 +191,10 @@ def handle_refund end end + def report_to_tax_provider + CreditNotes::ProviderTaxes::ReportJob.perform_later(credit_note:) + end + def compute_amounts_and_taxes taxes_result = CreditNotes::ApplyTaxesService.call( invoice:, diff --git a/app/services/integrations/aggregator/taxes/credit_notes/payload.rb b/app/services/integrations/aggregator/taxes/credit_notes/payload.rb index 47fa14ec893..349ab03d488 100644 --- a/app/services/integrations/aggregator/taxes/credit_notes/payload.rb +++ b/app/services/integrations/aggregator/taxes/credit_notes/payload.rb @@ -51,7 +51,7 @@ def cn_item(item) { 'item_id' => fee.item_id, 'item_code' => mapped_item.external_id, - 'amount_cents' => (fee.sub_total_excluding_taxes_amount_cents&.to_i || 0) * -1 + 'amount_cents' => item.sub_total_excluding_taxes_amount_cents.to_i * -1 } end diff --git a/spec/factories/customers.rb b/spec/factories/customers.rb index ddd48b79bd1..cb6ea7e5a1e 100644 --- a/spec/factories/customers.rb +++ b/spec/factories/customers.rb @@ -39,5 +39,11 @@ shipping_state { state } shipping_country { country } end + + trait :with_tax_integration do + after :create do |customer| + create(:anrok_customer, customer:) + end + end end end diff --git a/spec/models/credit_note_item_spec.rb b/spec/models/credit_note_item_spec.rb index ccfa9ee8d88..62006e0f83c 100644 --- a/spec/models/credit_note_item_spec.rb +++ b/spec/models/credit_note_item_spec.rb @@ -1,3 +1,29 @@ # frozen_string_literal: true require 'rails_helper' + +RSpec.describe CreditNoteItem, type: :model do + subject(:credit_note_item) { create(:credit_note_item) } + + it { is_expected.to belong_to(:credit_note) } + it { is_expected.to belong_to(:fee) } + + describe '#sub_total_excluding_taxes_amount_cents' do + let(:credit_note_item) { build(:credit_note_item, amount_cents: 100, fee: fee) } + let(:fee) { build(:fee, amount_cents: 1000, precise_amount_cents: 1000, precise_coupons_amount_cents: 0) } + + context 'when there are no coupons applied' do + it 'returns item amount with coupons applied' do + expect(credit_note_item.sub_total_excluding_taxes_amount_cents).to eq(100) + end + end + + context 'when there are coupons applied' do + let(:fee) { build(:fee, amount_cents: 1000, precise_amount_cents: 1000, precise_coupons_amount_cents: 20) } + + it 'returns item amount with coupons applied' do + expect(credit_note_item.sub_total_excluding_taxes_amount_cents).to eq(98) + end + end + end +end diff --git a/spec/services/credit_notes/create_service_spec.rb b/spec/services/credit_notes/create_service_spec.rb index 5b0691e1f7a..b1dc75d2f50 100644 --- a/spec/services/credit_notes/create_service_spec.rb +++ b/spec/services/credit_notes/create_service_spec.rb @@ -135,6 +135,16 @@ let(:service_call) { create_service.call } end + context 'when customer has tax_provider set up' do + let(:customer) { create(:customer, :with_tax_integration, organization:) } + + it 'sync with tax provider' do + expect do + create_service.call + end.to have_enqueued_job(CreditNotes::ProviderTaxes::ReportJob) + end + end + context 'when organization does not have right email settings' do before { invoice.organization.update!(email_settings: []) } diff --git a/spec/services/integrations/aggregator/taxes/credit_notes/payload_spec.rb b/spec/services/integrations/aggregator/taxes/credit_notes/payload_spec.rb index 9be569b3ef6..c848a21117e 100644 --- a/spec/services/integrations/aggregator/taxes/credit_notes/payload_spec.rb +++ b/spec/services/integrations/aggregator/taxes/credit_notes/payload_spec.rb @@ -44,7 +44,9 @@ :fee, invoice:, add_on:, - created_at: current_time - 3.seconds + created_at: current_time - 3.seconds, + amount_cents: 200, + precise_amount_cents: 200 ) end let(:fee_add_on_two) do @@ -52,7 +54,10 @@ :fee, invoice:, add_on: add_on_two, - created_at: current_time - 2.seconds + created_at: current_time - 2.seconds, + amount_cents: 200, + precise_amount_cents: 200, + precise_coupons_amount_cents: 20 ) end let(:credit_note) do @@ -66,10 +71,10 @@ end let(:credit_note_item1) do - create(:credit_note_item, credit_note:, fee: fee_add_on, amount_cents: fee_add_on.amount_cents) + create(:credit_note_item, credit_note:, fee: fee_add_on, amount_cents: 190) end let(:credit_note_item2) do - create(:credit_note_item, credit_note:, fee: fee_add_on_two, amount_cents: fee_add_on_two.amount_cents) + create(:credit_note_item, credit_note:, fee: fee_add_on_two, amount_cents: 180) end let(:body) do @@ -92,12 +97,12 @@ { 'item_id' => fee_add_on.item_id, 'item_code' => 'm1', - 'amount_cents' => -200 + 'amount_cents' => -190 }, { 'item_id' => fee_add_on_two.item_id, 'item_code' => '1', - 'amount_cents' => -200 + 'amount_cents' => -162 } ] }