Skip to content

Commit

Permalink
Take tax_category from Line Items and Fix validation: rounding for su…
Browse files Browse the repository at this point in the history
…bcent amounts (#14)

* fix tax rounding for sub-cent amounts

* adjust spec so that we still test quantity working

* use tax_category per tax rate
  • Loading branch information
SubandiK authored Jan 8, 2025
1 parent 45ba1cf commit d251c68
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 20 deletions.
12 changes: 6 additions & 6 deletions lib/secretariat/invoice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,17 @@ def tax_reason_text
tax_reason || TAX_EXEMPTION_REASONS[tax_category]
end

def tax_category_code(version: 2)
def tax_category_code(tax, version: 2)
if version == 1
return TAX_CATEGORY_CODES_1[tax_category] || 'S'
return TAX_CATEGORY_CODES_1[tax.tax_category || tax_category] || 'S'
end
TAX_CATEGORY_CODES[tax_category] || 'S'
TAX_CATEGORY_CODES[tax.tax_category || tax_category] || 'S'
end

def taxes
taxes = {}
line_items.each do |line_item|
taxes[line_item.tax_percent] = Tax.new(tax_percent: BigDecimal(line_item.tax_percent)) if taxes[line_item.tax_percent].nil?
taxes[line_item.tax_percent] = Tax.new(tax_percent: BigDecimal(line_item.tax_percent), tax_category: line_item.tax_category) if taxes[line_item.tax_percent].nil?
taxes[line_item.tax_percent].tax_amount += BigDecimal(line_item.tax_amount)
taxes[line_item.tax_percent].base_amount += BigDecimal(line_item.net_amount) * line_item.quantity
end
Expand All @@ -91,7 +91,7 @@ def valid?
end
taxes.each do |tax|
calc_tax = tax.base_amount * BigDecimal(tax.tax_percent) / BigDecimal(100)
calc_tax = calc_tax.round(2, :down)
calc_tax = calc_tax.round(2)
if tax.tax_amount != calc_tax
@errors << "Tax amount and calculated tax amount deviate for rate #{tax.tax_percent}: #{tax.tax_amount} / #{calc_tax}"
return false
Expand Down Expand Up @@ -230,7 +230,7 @@ def to_xml(version: 1, validate: true)
xml['ram'].ExemptionReason tax_reason_text
end
Helpers.currency_element(xml, 'ram', 'BasisAmount', tax.base_amount, currency_code, add_currency: version == 1)
xml['ram'].CategoryCode tax_category_code(version: version)
xml['ram'].CategoryCode tax_category_code(tax, version: version)

percent = by_version(version, 'ApplicablePercent', 'RateApplicablePercent')
xml['ram'].send(percent, Helpers.format(tax.tax_percent))
Expand Down
1 change: 1 addition & 0 deletions lib/secretariat/line_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ def valid?
end

calculated_tax = charge_price * BigDecimal(tax_percent) / BigDecimal(100)
calculated_tax = calculated_tax.round(2)
if calculated_tax != tax
@errors << "Tax and calculated tax deviate: #{tax} / #{calculated_tax}"
return false
Expand Down
1 change: 1 addition & 0 deletions lib/secretariat/tax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module Secretariat
Tax = Struct.new('Tax',
:tax_percent,
:tax_amount,
:tax_category,
:base_amount,
keyword_init: true
) do
Expand Down
53 changes: 39 additions & 14 deletions test/invoice_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,27 +130,40 @@ def make_de_invoice_with_multiple_tax_rates
)
line_item = LineItem.new(
name: 'Depfu Starter Plan',
quantity: 1,
quantity: 2,
unit: :PIECE,
gross_amount: '23.80',
net_amount: '20',
charge_amount: '20',
charge_amount: '40',
tax_category: :STANDARDRATE,
tax_percent: '19',
tax_amount: "3.80",
tax_amount: "7.60",
origin_country_code: 'DE',
currency_code: 'EUR'
)
line_item2 = LineItem.new(
name: 'Cup of Coffee',
quantity: 2,
quantity: 1,
unit: :PIECE,
gross_amount: '2.14',
net_amount: '2',
charge_amount: '4',
gross_amount: '2.68',
net_amount: '2.50',
charge_amount: '2.50',
tax_category: :STANDARDRATE,
tax_percent: '7',
tax_amount: "0.28",
tax_amount: "0.18",
origin_country_code: 'DE',
currency_code: 'EUR'
)
line_item3 = LineItem.new(
name: 'Returnable Deposit',
quantity: 1,
unit: :PIECE,
gross_amount: '5',
net_amount: '5',
charge_amount: '5',
tax_category: :ZEROTAXPRODUCTS,
tax_percent: '0',
tax_amount: "0",
origin_country_code: 'DE',
currency_code: 'EUR'
)
Expand All @@ -162,18 +175,18 @@ def make_de_invoice_with_multiple_tax_rates
seller: seller,
buyer: buyer,
buyer_reference: "112233",
line_items: [line_item, line_item2],
line_items: [line_item, line_item2, line_item3],
currency_code: 'USD',
payment_type: :CREDITCARD,
payment_text: 'Kreditkarte',
payment_iban: 'DE02120300000000202051',
payment_terms_text: "Zahlbar innerhalb von 14 Tagen ohne Abzug",
tax_category: :STANDARDRATE,
tax_amount: '4.08',
basis_amount: '24',
grand_total_amount: '28.08',
tax_amount: '7.78',
basis_amount: '47.50',
grand_total_amount: '55.28',
due_amount: 0,
paid_amount: '28.08',
paid_amount: '55.28',
payment_due_date: Date.today + 14
)
end
Expand Down Expand Up @@ -296,7 +309,7 @@ def test_de_multiple_taxes_invoice_v2
assert_equal [], errors
end

def test_de_multiple_taxes_invoice_against_schematron
def test_de_multiple_taxes_invoice_against_schematron_1
xml = make_de_invoice_with_multiple_tax_rates.to_xml(version: 1)
v = Validator.new(xml, version: 1)
errors = v.validate_against_schematron
Expand All @@ -308,5 +321,17 @@ def test_de_multiple_taxes_invoice_against_schematron
end
assert_equal [], errors
end
def test_de_multiple_taxes_invoice_against_schematron_2
xml = make_de_invoice_with_multiple_tax_rates.to_xml(version: 2)
v = Validator.new(xml, version: 2)
errors = v.validate_against_schematron
if !errors.empty?
puts xml
errors.each do |error|
puts "#{error[:line]}: #{error[:message]}"
end
end
assert_equal [], errors
end
end
end

0 comments on commit d251c68

Please sign in to comment.