diff --git a/lib/secretariat/invoice.rb b/lib/secretariat/invoice.rb index 140627b..84b2734 100644 --- a/lib/secretariat/invoice.rb +++ b/lib/secretariat/invoice.rb @@ -53,6 +53,9 @@ module Secretariat :direct_debit_creditor_id, # BT-90 :direct_debit_iban, # BT-91, :subject_code, # BT-21 + :type_code, # BT-3, defaults to 380 (commercial invoice) + :preceding_invoice_id, # BT-25 + :preceding_invoice_issue_date, # BT-26 keyword_init: true ) do @@ -212,7 +215,7 @@ def to_xml(version: 1, validate: true) if version == 1 xml['ram'].Name "RECHNUNG" end - xml['ram'].TypeCode '380' # TODO: make configurable + xml['ram'].TypeCode type_code || '380' xml['ram'].IssueDateTime do xml['udt'].DateTimeString(format: '102') do xml.text(issue_date.strftime("%Y%m%d")) @@ -351,6 +354,18 @@ def to_xml(version: 1, validate: true) Helpers.currency_element(xml, 'ram', 'TotalPrepaidAmount', paid_amount, currency_code, add_currency: version == 1) Helpers.currency_element(xml, 'ram', 'DuePayableAmount', due_amount, currency_code, add_currency: version == 1) end + if version == 2 && preceding_invoice_id.present? + xml['ram'].InvoiceReferencedDocument do + xml['ram'].IssuerAssignedID preceding_invoice_id + if preceding_invoice_issue_date + xml['ram'].FormattedIssueDateTime do + xml['qdt'].DateTimeString(format: '102') do + xml.text(preceding_invoice_issue_date.strftime("%Y%m%d")) + end + end + end + end + end end if version == 1 line_items.each_with_index do |item, i| diff --git a/lib/secretariat/trade_party.rb b/lib/secretariat/trade_party.rb index 8ad0125..d0f6b5e 100644 --- a/lib/secretariat/trade_party.rb +++ b/lib/secretariat/trade_party.rb @@ -35,7 +35,8 @@ def to_xml(xml, exclude_tax: false, version: 2) xml['ram'].Name name if legal_organization.present? xml['ram'].SpecifiedLegalOrganization do - xml['ram'].ID(schemeID: legal_organization[:scheme_id] || "0002") do + scheme_id = legal_organization.fetch(:scheme_id, "0002") # scheme_id: nil omits the scheme (BT-30-1) + xml['ram'].ID({ schemeID: scheme_id }.compact) do xml.text(legal_organization[:id]) end end diff --git a/test/invoice_test.rb b/test/invoice_test.rb index 8af2c76..cdd3739 100644 --- a/test/invoice_test.rb +++ b/test/invoice_test.rb @@ -861,6 +861,50 @@ def test_fr_invoice assert_match(%r{\s*304755032\s*}, xml) end + def test_type_code_defaults_to_commercial_invoice + xml = make_de_invoice.to_xml(version: 2) + assert_match(%r{380}, xml) + end + + def test_credit_note_with_preceding_invoice + invoice = make_eu_invoice + invoice.type_code = '381' + invoice.preceding_invoice_id = 'R-2026-0042' + invoice.preceding_invoice_issue_date = Date.new(2026, 10, 1) + xml = invoice.to_xml(version: 2) + + assert_match(%r{381}, xml) + assert_match(%r{\s*\s*R-2026-0042\s*\s*20261001\s*\s*}, xml) + assert_equal [], Validator.new(xml, version: 2).validate_against_schematron + end + + def test_preceding_invoice_without_issue_date + invoice = make_de_invoice + invoice.preceding_invoice_id = 'R-2026-0042' + xml = invoice.to_xml(version: 2) + + assert_match(%r{\s*R-2026-0042\s*}, xml) + end + + def test_no_preceding_invoice_by_default + xml = make_de_invoice.to_xml(version: 2) + refute_match(/InvoiceReferencedDocument/, xml) + end + + def test_legal_organization_without_scheme + invoice = make_fr_invoice + invoice.seller.legal_organization = { id: 'HRB 12345', scheme_id: nil } + xml = invoice.to_xml(version: 2) + assert_match(%r{\s*HRB 12345\s*}, xml) + end + + def test_legal_organization_scheme_defaults_to_sirene + invoice = make_fr_invoice + invoice.seller.legal_organization = { id: '304755032' } + xml = invoice.to_xml(version: 2) + assert_match(%r{304755032}, xml) + end + def test_invoice_with_quantity_causing_sub_cent_amounts errors = []