diff --git a/app/services/integrations/aggregator/taxes/invoices/base_service.rb b/app/services/integrations/aggregator/taxes/invoices/base_service.rb index 9a4d5c8c208..b22c7e6ace3 100644 --- a/app/services/integrations/aggregator/taxes/invoices/base_service.rb +++ b/app/services/integrations/aggregator/taxes/invoices/base_service.rb @@ -5,6 +5,7 @@ module Aggregator module Taxes module Invoices class BaseService < Integrations::Aggregator::BaseService + SPECIAL_TAXATION_TYPES = %w[exempt notCollecting productNotTaxed jurisNotTaxed].freeze def initialize(invoice:, fees: nil) @invoice = invoice @fees = fees || invoice.fees @@ -80,9 +81,9 @@ def process_void_response(body) def tax_breakdown(breakdown) breakdown.map do |b| - if b['type'] == 'exempt' + if SPECIAL_TAXATION_TYPES.include?(b['type']) OpenStruct.new( - name: b['reason'], + name: humanize_tax_name(b['reason'].presence || b['type']), rate: '0.00', tax_amount: 0, type: b['type'] @@ -97,6 +98,10 @@ def tax_breakdown(breakdown) end end end + + def humanize_tax_name(camelized_name) + camelized_name.underscore.humanize + end end end end diff --git a/spec/graphql/mutations/integrations/anrok/fetch_draft_invoice_taxes_spec.rb b/spec/graphql/mutations/integrations/anrok/fetch_draft_invoice_taxes_spec.rb index bd79eb3cf0b..606bcaf42e4 100644 --- a/spec/graphql/mutations/integrations/anrok/fetch_draft_invoice_taxes_spec.rb +++ b/spec/graphql/mutations/integrations/anrok/fetch_draft_invoice_taxes_spec.rb @@ -123,7 +123,7 @@ breakdown2 = fee['taxBreakdown'].last - expect(breakdown2['name']).to eq('reverseCharge') + expect(breakdown2['name']).to eq('Reverse charge') expect(breakdown2['rate']).to eq(0.0) expect(breakdown2['taxAmount']).to eq('0') expect(breakdown2['type']).to eq('exempt') diff --git a/spec/services/fees/apply_provider_taxes_service_spec.rb b/spec/services/fees/apply_provider_taxes_service_spec.rb index 944df5c7217..a7d946c648b 100644 --- a/spec/services/fees/apply_provider_taxes_service_spec.rb +++ b/spec/services/fees/apply_provider_taxes_service_spec.rb @@ -54,5 +54,44 @@ end.not_to change { fee.applied_taxes.count } end end + + context 'when applying taxes with specific provider rules' do + special_rules = + [ + {received_type: 'notCollecting', expected_name: 'Not collecting', tax_code: 'not_collecting'}, + {received_type: 'productNotTaxed', expected_name: 'Product not taxed', tax_code: 'product_not_taxed'}, + {received_type: 'jurisNotTaxed', expected_name: 'Juris not taxed', tax_code: 'juris_not_taxed'} + ] + special_rules.each do |applied_rule| + context "when tax provider returned specific rule applied to fees - #{applied_rule[:expected_name]}" do + let(:fee_taxes) do + OpenStruct.new( + tax_breakdown: [ + OpenStruct.new(name: applied_rule[:expected_name], type: applied_rule[:received_type], rate: '0.00', tax_amount: 0) + ] + ) + end + + it 'creates applied_taxes based on the provider rules' do + result = apply_service.call + + aggregate_failures do + expect(result).to be_success + + applied_taxes = result.applied_taxes + expect(applied_taxes.count).to eq(1) + + applied_tax = applied_taxes.first + expect(applied_tax).to have_attributes( + tax_code: applied_rule[:tax_code], + tax_name: applied_rule[:expected_name], + tax_description: applied_rule[:received_type] + ) + expect(fee).to have_attributes(taxes_amount_cents: 0, taxes_rate: 0) + end + end + end + end + end end end diff --git a/spec/services/integrations/aggregator/taxes/invoices/create_draft_service_spec.rb b/spec/services/integrations/aggregator/taxes/invoices/create_draft_service_spec.rb index cec582f2f93..6cc844d5a97 100644 --- a/spec/services/integrations/aggregator/taxes/invoices/create_draft_service_spec.rb +++ b/spec/services/integrations/aggregator/taxes/invoices/create_draft_service_spec.rb @@ -128,11 +128,48 @@ expect(result).to be_success expect(result.fees.first['tax_breakdown'].first['rate']).to eq('0.10') expect(result.fees.first['tax_breakdown'].first['name']).to eq('GST/HST') - expect(result.fees.first['tax_breakdown'].last['name']).to eq('reverseCharge') + expect(result.fees.first['tax_breakdown'].last['name']).to eq('Reverse charge') expect(result.fees.first['tax_breakdown'].last['type']).to eq('exempt') expect(result.fees.first['tax_breakdown'].last['rate']).to eq('0.00') end end + + context 'when special rules applied' do + before do + parsed_body = JSON.parse(body) + parsed_body['succeededInvoices'].first['fees'].first['tax_breakdown'] = [ + { + reason: "", + type: rule + } + ] + allow(response).to receive(:body).and_return(parsed_body.to_json) + end + + special_rules = + [ + {received_type: 'notCollecting', expected_name: 'Not collecting'}, + {received_type: 'productNotTaxed', expected_name: 'Product not taxed'}, + {received_type: 'jurisNotTaxed', expected_name: 'Juris not taxed'} + ] + + special_rules.each do |specific_rule| + context "when applied rule is #{specific_rule}" do + let(:rule) { specific_rule[:received_type] } + + it 'returns fee object with populated for the specific rule fields' do + result = service_call + aggregate_failures do + expect(result).to be_success + expect(result.fees.first['tax_breakdown'].last['name']).to eq(specific_rule[:expected_name]) + expect(result.fees.first['tax_breakdown'].last['type']).to eq(specific_rule[:received_type]) + expect(result.fees.first['tax_breakdown'].last['rate']).to eq('0.00') + expect(result.fees.first['tax_breakdown'].last['tax_amount']).to eq(0) + end + end + end + end + end end context 'when taxes are not successfully fetched' do diff --git a/spec/services/integrations/aggregator/taxes/invoices/create_service_spec.rb b/spec/services/integrations/aggregator/taxes/invoices/create_service_spec.rb index 5ba282d2a3f..cfbcadbdec8 100644 --- a/spec/services/integrations/aggregator/taxes/invoices/create_service_spec.rb +++ b/spec/services/integrations/aggregator/taxes/invoices/create_service_spec.rb @@ -129,7 +129,7 @@ expect(result).to be_success expect(result.fees.first['tax_breakdown'].first['rate']).to eq('0.10') expect(result.fees.first['tax_breakdown'].first['name']).to eq('GST/HST') - expect(result.fees.first['tax_breakdown'].last['name']).to eq('reverseCharge') + expect(result.fees.first['tax_breakdown'].last['name']).to eq('Reverse charge') expect(result.fees.first['tax_breakdown'].last['type']).to eq('exempt') expect(result.fees.first['tax_breakdown'].last['rate']).to eq('0.00') end diff --git a/spec/services/invoices/apply_provider_taxes_service_spec.rb b/spec/services/invoices/apply_provider_taxes_service_spec.rb index c98079884f7..24f6c90ddb4 100644 --- a/spec/services/invoices/apply_provider_taxes_service_spec.rb +++ b/spec/services/invoices/apply_provider_taxes_service_spec.rb @@ -47,76 +47,139 @@ end context 'with non zero fees amount' do - before do - fee1 = create(:fee, invoice:, amount_cents: 1000, precise_coupons_amount_cents: 0) - create( - :fee_applied_tax, - fee: fee1, - amount_cents: 100, - tax_name: 'tax 1', - tax_code: 'tax_1', - tax_rate: 10.0, - tax_description: 'type1' - ) + context 'with non-zero taxes' do + before do + fee1 = create(:fee, invoice:, amount_cents: 1000, precise_coupons_amount_cents: 0) + create( + :fee_applied_tax, + fee: fee1, + amount_cents: 100, + tax_name: 'tax 1', + tax_code: 'tax_1', + tax_rate: 10.0, + tax_description: 'type1' + ) - fee2 = create(:fee, invoice:, amount_cents: 2000, precise_coupons_amount_cents: 0) + fee2 = create(:fee, invoice:, amount_cents: 2000, precise_coupons_amount_cents: 0) - create( - :fee_applied_tax, - fee: fee2, - amount_cents: 200, - tax_name: 'tax 1', - tax_code: 'tax_1', - tax_rate: 10.0, - tax_description: 'type1' - ) - create( - :fee_applied_tax, - fee: fee2, - amount_cents: 240, - tax_name: 'tax 2', - tax_code: 'tax_2', - tax_rate: 12.0, - tax_description: 'type2' - ) - end + create( + :fee_applied_tax, + fee: fee2, + amount_cents: 200, + tax_name: 'tax 1', + tax_code: 'tax_1', + tax_rate: 10.0, + tax_description: 'type1' + ) + create( + :fee_applied_tax, + fee: fee2, + amount_cents: 240, + tax_name: 'tax 2', + tax_code: 'tax_2', + tax_rate: 12.0, + tax_description: 'type2' + ) + end - it 'creates applied taxes' do - result = apply_service.call + it 'creates applied taxes' do + result = apply_service.call - aggregate_failures do - expect(result).to be_success + aggregate_failures do + expect(result).to be_success - applied_taxes = result.applied_taxes - expect(applied_taxes.count).to eq(2) + applied_taxes = result.applied_taxes + expect(applied_taxes.count).to eq(2) - expect(applied_taxes.find { |item| item.tax_code == 'tax_1' }).to have_attributes( - invoice:, - tax_description: 'type1', - tax_code: 'tax_1', - tax_name: 'tax 1', - tax_rate: 10, - amount_currency: invoice.currency, - amount_cents: 300, - fees_amount_cents: 3000 - ) + expect(applied_taxes.find { |item| item.tax_code == 'tax_1' }).to have_attributes( + invoice:, + tax_description: 'type1', + tax_code: 'tax_1', + tax_name: 'tax 1', + tax_rate: 10, + amount_currency: invoice.currency, + amount_cents: 300, + fees_amount_cents: 3000 + ) - expect(applied_taxes.find { |item| item.tax_code == 'tax_2' }).to have_attributes( - invoice:, - tax_description: 'type2', - tax_code: 'tax_2', - tax_name: 'tax 2', - tax_rate: 12, - amount_currency: invoice.currency, - amount_cents: 240, - fees_amount_cents: 2000 - ) + expect(applied_taxes.find { |item| item.tax_code == 'tax_2' }).to have_attributes( + invoice:, + tax_description: 'type2', + tax_code: 'tax_2', + tax_name: 'tax 2', + tax_rate: 12, + amount_currency: invoice.currency, + amount_cents: 240, + fees_amount_cents: 2000 + ) - expect(invoice).to have_attributes( - taxes_amount_cents: 540, - taxes_rate: 18, - fees_amount_cents: 3000 - ) + expect(invoice).to have_attributes( + taxes_amount_cents: 540, + taxes_rate: 18, + fees_amount_cents: 3000 + ) + end + end + end + + context 'with special provider rules' do + special_rules = + [ + {received_type: 'notCollecting', expected_name: 'Not collecting', tax_code: 'not_collecting'}, + {received_type: 'productNotTaxed', expected_name: 'Product not taxed', tax_code: 'product_not_taxed'}, + {received_type: 'jurisNotTaxed', expected_name: 'Juris not taxed', tax_code: 'juris_not_taxed'} + ] + special_rules.each do |applied_rule| + context "when tax provider returned specific rule applied to fees - #{applied_rule[:expected_name]}" do + let(:fee_taxes) do + [ + OpenStruct.new( + tax_breakdown: [ + OpenStruct.new(name: applied_rule[:expected_name], type: applied_rule[:received_type], + rate: '0.00', tax_amount: 0) + ] + ), + OpenStruct.new( + tax_breakdown: [ + OpenStruct.new(name: applied_rule[:expected_name], type: applied_rule[:received_type], + rate: '0.00', tax_amount: 0) + ] + ) + ] + end + let(:fee1) { create(:fee, invoice:, amount_cents: 1000, precise_coupons_amount_cents: 0) } + let(:fee2) { create(:fee, invoice:, amount_cents: 2000, precise_coupons_amount_cents: 0) } + let(:fee1_applied_tax) do + create(:fee_applied_tax, fee: fee1, amount_cents: 0, tax_name: applied_rule[:expected_name], + tax_code: applied_rule[:tax_code], tax_rate: 0.0, tax_description: applied_rule[:received_type]) + end + let(:fee2_applied_tax) do + create(:fee_applied_tax, fee: fee2, amount_cents: 0, tax_name: applied_rule[:expected_name], + tax_code: applied_rule[:tax_code], tax_rate: 0.0, tax_description: applied_rule[:received_type]) + end + + before do + fee1_applied_tax + fee2_applied_tax + end + + it "creates applied taxes with #{applied_rule[:expected_name]} params" do + result = apply_service.call + + aggregate_failures do + expect(result).to be_success + + applied_taxes = result.applied_taxes + expect(applied_taxes.count).to eq(1) + applied_taxes.each do |applied_tax| + expect(applied_tax.tax_description).to eq(applied_rule[:received_type]) + expect(applied_tax.tax_code).to eq(applied_rule[:tax_code]) + expect(applied_tax.tax_name).to eq(applied_rule[:expected_name]) + expect(applied_tax.tax_rate).to eq(0.0) + end + end + end + end end end end