Skip to content

Commit

Permalink
Feat(anrok): allow to save special provider tax types (#2421)
Browse files Browse the repository at this point in the history
## Context

Tax provider might send us taxes with explanation of specific rules that
were applied on these taxes. We want to save this information in
AppliedTaxes on fees and invoices

## Description


when parsing response from tax provider, if the tax_type is included in
SPECIAL_TAXATION_TYPES constant, we want to save it's name from received
tax reason or tax type
  • Loading branch information
annvelents authored Aug 19, 2024
1 parent e30357d commit 5be3146
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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']
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
39 changes: 39 additions & 0 deletions spec/services/fees/apply_provider_taxes_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
187 changes: 125 additions & 62 deletions spec/services/invoices/apply_provider_taxes_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 5be3146

Please sign in to comment.