diff --git a/lib/secretariat/invoice.rb b/lib/secretariat/invoice.rb index ad86208..957925d 100644 --- a/lib/secretariat/invoice.rb +++ b/lib/secretariat/invoice.rb @@ -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 @@ -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 @@ -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)) diff --git a/lib/secretariat/line_item.rb b/lib/secretariat/line_item.rb index 4e8c160..17a8919 100644 --- a/lib/secretariat/line_item.rb +++ b/lib/secretariat/line_item.rb @@ -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 diff --git a/lib/secretariat/tax.rb b/lib/secretariat/tax.rb index 50a5fa6..d0726b2 100644 --- a/lib/secretariat/tax.rb +++ b/lib/secretariat/tax.rb @@ -20,6 +20,7 @@ module Secretariat Tax = Struct.new('Tax', :tax_percent, :tax_amount, + :tax_category, :base_amount, keyword_init: true ) do diff --git a/test/invoice_test.rb b/test/invoice_test.rb index f4fe781..f7b0021 100644 --- a/test/invoice_test.rb +++ b/test/invoice_test.rb @@ -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' ) @@ -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 @@ -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 @@ -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