X-Git-Url: http://wagnertech.de/gitweb/gitweb.cgi/mfinanz.git/blobdiff_plain/5da10e0193a990f94c50252738f33f42d0e7e567..371ab59da0ebb21a8438cab732530e1cf5f9471c:/SL/DB/Helper/ZUGFeRD.pm diff --git a/SL/DB/Helper/ZUGFeRD.pm b/SL/DB/Helper/ZUGFeRD.pm index 3ad825c74..99e08256e 100644 --- a/SL/DB/Helper/ZUGFeRD.pm +++ b/SL/DB/Helper/ZUGFeRD.pm @@ -6,12 +6,14 @@ use utf8; use parent qw(Exporter); our @EXPORT = qw(create_zugferd_data create_zugferd_xmp_data); +use SL::DB::BankAccount; use SL::DB::GenericTranslation; use SL::DB::Tax; use SL::DB::TaxKey; use SL::Helper::ISO3166; use SL::Helper::ISO4217; use SL::Helper::UNECERecommendation20; +use SL::VATIDNr; use Carp; use Encode qw(encode); @@ -176,6 +178,28 @@ sub _line_item { # } +sub _specified_trade_settlement_payment_means { + my ($self, %params) = @_; + + # + $params{xml}->startTag('ram:SpecifiedTradeSettlementPaymentMeans'); + $params{xml}->dataElement('ram:TypeCode', $self->direct_debit ? 59 : 58); # 59 = SEPA direct debit, 58 = SEPA credit transfer + + if ($self->direct_debit) { + $params{xml}->startTag('ram:PayerPartyDebtorFinancialAccount'); + $params{xml}->dataElement('ram:IBANID', $self->customer->iban); + $params{xml}->endTag; + + } else { + $params{xml}->startTag('ram:PayeePartyCreditorFinancialAccount'); + $params{xml}->dataElement('ram:IBANID', $params{bank_account}->iban); + $params{xml}->endTag; + } + + $params{xml}->endTag; + # +} + sub _taxes { my ($self, %params) = @_; @@ -386,13 +410,9 @@ sub _exchanged_document { sub _specified_tax_registration { my ($ustid_nr, %params) = @_; - return unless $ustid_nr; - - $ustid_nr = "DE$ustid_nr" unless $ustid_nr =~ m{^[A-Z]{2}}; - # $params{xml}->startTag("ram:SpecifiedTaxRegistration"); - $params{xml}->dataElement("ram:ID", _u8($ustid_nr), schemeID => "VA"); + $params{xml}->dataElement("ram:ID", _u8(SL::VATIDNr->normalize($ustid_nr)), schemeID => "VA"); $params{xml}->endTag; # } @@ -520,6 +540,7 @@ sub _applicable_header_trade_settlement { $params{xml}->startTag("ram:ApplicableHeaderTradeSettlement"); $params{xml}->dataElement("ram:InvoiceCurrencyCode", _u8(SL::Helper::ISO4217::map_currency_name_to_code($self->currency->name) // 'EUR')); + _specified_trade_settlement_payment_means($self, %params); _taxes($self, %params); _payment_terms($self, %params); _totals($self, %params); @@ -546,12 +567,17 @@ sub _supply_chain_trade_transaction { sub _validate_data { my ($self) = @_; + my %result; my $prefix = $::locale->text('The ZUGFeRD invoice data cannot be generated because the data validation failed.') . ' '; if (!$::instance_conf->get_co_ustid) { SL::X::ZUGFeRDValidation->throw(message => $prefix . $::locale->text('The VAT registration number is missing in the client configuration.')); } + if (!SL::VATIDNr->validate($::instance_conf->get_co_ustid)) { + SL::X::ZUGFeRDValidation->throw(message => $prefix . $::locale->text("The VAT ID number in the client configuration is invalid.")); + } + if (!$::instance_conf->get_company || any { my $get = "get_address_$_"; !$::instance_conf->$get } qw(street1 zipcode city)) { SL::X::ZUGFeRDValidation->throw(message => $prefix . $::locale->text('The company\'s address information is incomplete in the client configuration.')); } @@ -572,43 +598,54 @@ sub _validate_data { if ($failed_unit) { SL::X::ZUGFeRDValidation->throw(message => $prefix . $::locale->text('One of the units used (#1) cannot be mapped to a known unit code from the UN/ECE Recommendation 20 list.', $failed_unit)); } + + if ($self->direct_debit) { + if (!$self->customer->iban) { + SL::X::ZUGFeRDValidation->throw(message => $prefix . $::locale->text('The customer\'s bank account number (IBAN) is missing.')); + } + + } else { + my $bank_accounts = SL::DB::Manager::BankAccount->get_all; + $result{bank_account} = scalar(@{ $bank_accounts }) == 1 ? $bank_accounts->[0] : first { $_->use_for_zugferd } @{ $bank_accounts }; + + if (!$result{bank_account}) { + SL::X::ZUGFeRDValidation->throw(message => $prefix . $::locale->text('No bank account flagged for ZUGFeRD usage was found.')); + } + } + + return %result; } sub create_zugferd_data { - my ($self) = @_; + my ($self) = @_; - _validate_data($self); + my $output = ''; - my %ptc_data = $self->calculate_prices_and_taxes; - my $output = ''; - my $xml = XML::Writer->new( - OUTPUT => \$output, - DATA_MODE => 1, - DATA_INDENT => 2, - ENCODING => 'utf-8', + my %params = _validate_data($self); + $params{ptc_data} = { $self->calculate_prices_and_taxes }; + $params{xml} = XML::Writer->new( + OUTPUT => \$output, + DATA_MODE => 1, + DATA_INDENT => 2, + ENCODING => 'utf-8', ); - my %params = ( - ptc_data => \%ptc_data, - xml => $xml, - ); - - $xml->xmlDecl(); + $params{xml}->xmlDecl(); # - $xml->startTag("rsm:CrossIndustryInvoice", - "xmlns:a" => "urn:un:unece:uncefact:data:standard:QualifiedDataType:100", - "xmlns:rsm" => "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100", - "xmlns:qdt" => "urn:un:unece:uncefact:data:standard:QualifiedDataType:10", - "xmlns:ram" => "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100", - "xmlns:xs" => "http://www.w3.org/2001/XMLSchema", - "xmlns:udt" => "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"); + $params{xml}->startTag("rsm:CrossIndustryInvoice", + "xmlns:a" => "urn:un:unece:uncefact:data:standard:QualifiedDataType:100", + "xmlns:rsm" => "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100", + "xmlns:qdt" => "urn:un:unece:uncefact:data:standard:QualifiedDataType:10", + "xmlns:ram" => "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100", + "xmlns:xs" => "http://www.w3.org/2001/XMLSchema", + "xmlns:udt" => "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"); _exchanged_document_context($self, %params); _exchanged_document($self, %params); _supply_chain_trade_transaction($self, %params); - $xml->endTag; + $params{xml}->endTag; # return $output;