From 3a3f0b9b9b0c2c00cd23456b5ba2ca3137faccf9 Mon Sep 17 00:00:00 2001 From: Ronald Sacher Date: Mon, 1 Dec 2025 14:00:56 +0100 Subject: [PATCH] add sepa_direct_debit (direct_debit_mandate_reference_id, :direct_debit_creditor_id, direct_debit_iban - BT-89, BT-90, BT-91) --- lib/secretariat/constants.rb | 4 ++ lib/secretariat/invoice.rb | 14 ++++++ test/invoice_test.rb | 82 ++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/lib/secretariat/constants.rb b/lib/secretariat/constants.rb index 30f2ae7..291c1fa 100644 --- a/lib/secretariat/constants.rb +++ b/lib/secretariat/constants.rb @@ -53,6 +53,10 @@ module Secretariat :DEBITADVICE => "31", :CREDITCARD => "48", :DEBIT => "49", + :CREDITTRANSFER => "54", + :DIRECTDEBIT => "55", + :SEPACREDITTRANSFER => "58", + :SEPADIRECTDEBIT => "59", :COMPENSATION => "97" } diff --git a/lib/secretariat/invoice.rb b/lib/secretariat/invoice.rb index 4e88664..05af4e6 100644 --- a/lib/secretariat/invoice.rb +++ b/lib/secretariat/invoice.rb @@ -48,6 +48,9 @@ module Secretariat :tax_calculation_method, :notes, :attachments, + :direct_debit_mandate_reference_id, # BT-89 + :direct_debit_creditor_id, # BT-90 + :direct_debit_iban, # BT-91 keyword_init: true ) do @@ -260,6 +263,9 @@ def to_xml(version: 1, validate: true) end trade_settlement = by_version(version, 'ApplicableSupplyChainTradeSettlement', 'ApplicableHeaderTradeSettlement') xml['ram'].send(trade_settlement) do + if direct_debit_creditor_id + xml['ram'].CreditorReferenceID direct_debit_creditor_id # BT-90 + end if payment_reference.present? xml['ram'].PaymentReference payment_reference end @@ -278,6 +284,11 @@ def to_xml(version: 1, validate: true) xml['ram'].BICID payment_bic end end + if direct_debit_iban + xml['ram'].PayerPartyDebtorFinancialAccount do + xml['ram'].IBANID direct_debit_iban + end + end end taxes.each do |tax| xml['ram'].ApplicableTradeTax do @@ -311,6 +322,9 @@ def to_xml(version: 1, validate: true) Helpers.date_element(xml, payment_due_date) end end + if direct_debit_mandate_reference_id + xml['ram'].DirectDebitMandateID direct_debit_mandate_reference_id + end end monetary_summation = by_version(version, 'SpecifiedTradeSettlementMonetarySummation', 'SpecifiedTradeSettlementHeaderMonetarySummation') diff --git a/test/invoice_test.rb b/test/invoice_test.rb index cf7fbb7..9115b4d 100644 --- a/test/invoice_test.rb +++ b/test/invoice_test.rb @@ -57,6 +57,64 @@ def make_eu_invoice(tax_category: :REVERSECHARGE) ) end + def make_eu_invoice_with_sepa_direct_debit(tax_category: :REVERSECHARGE) + seller = TradeParty.new( + name: 'Depfu inc', + street1: 'Quickbornstr. 46', + city: 'Hamburg', + postal_code: '20253', + country_id: 'DE', + vat_id: 'DE304755032' + ) + buyer = TradeParty.new( + name: 'Depfu inc', + street1: 'Quickbornstr. 46', + city: 'Hamburg', + postal_code: '20253', + country_id: 'SE', + vat_id: 'SE304755032' + ) + line_item = LineItem.new( + name: 'Depfu Premium Plan', + quantity: 1, + gross_amount: BigDecimal('29'), + net_amount: BigDecimal('29'), + unit: :YEAR, + charge_amount: BigDecimal('29'), + tax_category: tax_category, + tax_percent: 0, + tax_amount: 0, + origin_country_code: 'DE', + currency_code: 'EUR', + service_period_start: Date.today, + service_period_end: Date.today + 364, + ) + Invoice.new( + id: '12345', + issue_date: Date.today, + # service_period on line_item. removed here to simplify testing of BillingSpecifiedPeriod presence + # service_period_start: Date.today, + # service_period_end: Date.today + 30, + seller: seller, + buyer: buyer, + line_items: [line_item], + currency_code: 'USD', + payment_type: :CREDITCARD, + payment_text: 'Kreditkarte', + tax_category: tax_category, + tax_amount: 0, + basis_amount: BigDecimal('29'), + grand_total_amount: BigDecimal('29'), + due_amount: 0, + paid_amount: 29, + payment_due_date: Date.today + 14, + notes: "This is a test invoice", + direct_debit_mandate_reference_id: "MANDATE REFERENCE", # BT-89 + direct_debit_creditor_id: "DE98ZZZ09999999999", # BT-90 + direct_debit_iban: "DE02120300000000202051", # BT-91 + ) + end + def make_foreign_invoice(tax_category: :TAXEXEMPT) seller = TradeParty.new( name: 'Depfu inc', @@ -381,6 +439,30 @@ def test_simple_eu_invoice_v2 puts e.errors end + def test_simple_eu_invoice_v2_with_sepa_direct_debit + begin + xml = make_eu_invoice_with_sepa_direct_debit.to_xml(version: 2) + rescue ValidationError => e + pp e.errors + end + + assert_match(%r{DE98ZZZ09999999999}, xml) + assert_match(%r{\s*DE02120300000000202051\s*}, xml) + assert_match(%r{MANDATE REFERENCE}, xml) + + v = Validator.new(xml, version: 2) + errors = v.validate_against_schema + if !errors.empty? + puts xml + errors.each do |error| + puts error + end + end + assert_equal [], errors + rescue ValidationError => e + puts e.errors + end + def test_simple_foreign_invoice_v2_taxexpempt begin xml = make_foreign_invoice(tax_category: :TAXEXEMPT).to_xml(version: 2)