From b24dfcbb7755c7c2d3389185091f951755de463c Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Fri, 6 Mar 2020 15:11:15 +0100 Subject: [PATCH] =?utf8?q?ZUGFeRD:=20Zahlungsinfos=20erg=C3=A4nzt:=20Konto?= =?utf8?q?nummer,=20Typ=3DEinzug/=C3=9Cberweisung?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- SL/Controller/SimpleSystemSetting.pm | 1 + SL/DB/Helper/ZUGFeRD.pm | 82 +++++++++++++------ SL/DB/MetaSetup/BankAccount.pm | 1 + locale/de/all | 3 + .../bank_account_flag_for_zugferd_usage.sql | 14 ++++ .../_bank_account_form.html | 4 + 6 files changed, 82 insertions(+), 23 deletions(-) create mode 100644 sql/Pg-upgrade2/bank_account_flag_for_zugferd_usage.sql diff --git a/SL/Controller/SimpleSystemSetting.pm b/SL/Controller/SimpleSystemSetting.pm index 2e559a31d..4fec19a9f 100644 --- a/SL/Controller/SimpleSystemSetting.pm +++ b/SL/Controller/SimpleSystemSetting.pm @@ -35,6 +35,7 @@ my %supported_types = ( { method => 'bank', title => t8('Bank'), }, { method => 'bank_code', title => t8('Bank code'), }, { method => 'bic', title => t8('BIC'), }, + { title => t8('Use for ZUGFeRD'), formatter => sub { $_[0]->use_for_zugferd ? t8('yes') : t8('no') } }, { method => 'reconciliation_starting_date_as_date', title => t8('Date'), align => 'right' }, { method => 'reconciliation_starting_balance_as_number', title => t8('Balance'), align => 'right' }, ], diff --git a/SL/DB/Helper/ZUGFeRD.pm b/SL/DB/Helper/ZUGFeRD.pm index 3ad825c74..051125677 100644 --- a/SL/DB/Helper/ZUGFeRD.pm +++ b/SL/DB/Helper/ZUGFeRD.pm @@ -6,6 +6,7 @@ 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; @@ -176,6 +177,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) = @_; @@ -520,6 +543,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,6 +570,7 @@ 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) { @@ -572,43 +597,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; diff --git a/SL/DB/MetaSetup/BankAccount.pm b/SL/DB/MetaSetup/BankAccount.pm index b72a170b4..cd3ffce45 100644 --- a/SL/DB/MetaSetup/BankAccount.pm +++ b/SL/DB/MetaSetup/BankAccount.pm @@ -21,6 +21,7 @@ __PACKAGE__->meta->columns( reconciliation_starting_balance => { type => 'numeric', precision => 15, scale => 5 }, reconciliation_starting_date => { type => 'date' }, sortkey => { type => 'integer', not_null => 1 }, + use_for_zugferd => { type => 'boolean', not_null => 1 }, ); __PACKAGE__->meta->primary_key_columns([ 'id' ]); diff --git a/locale/de/all b/locale/de/all index 43959b9db..72f7aa969 100755 --- a/locale/de/all +++ b/locale/de/all @@ -2005,6 +2005,7 @@ $self->{texts} = { 'No assembly has been selected yet.' => 'Es wurde noch kein Erzeugnis ausgewahlt.', 'No background job has been created yet.' => 'Es wurden noch keine Hintergrund-Jobs angelegt.', 'No bank account chosen!' => 'Kein Bankkonto ausgewählt!', + 'No bank account flagged for ZUGFeRD usage was found.' => 'Es wurde kein Bankkonto gefunden, das für Nutzung mit ZUGFeRD markiert ist.', 'No bank information has been entered in this customer\'s master data entry. You cannot create bank collections unless you enter bank information.' => 'Für diesen Kunden wurden in seinen Stammdaten keine Kontodaten hinterlegt. Solange dies nicht geschehen ist, können Sie keine Überweisungen für den Lieferanten anlegen.', 'No bank information has been entered in this vendor\'s master data entry. You cannot create bank transfers unless you enter bank information.' => 'Für diesen Lieferanten wurden in seinen Stammdaten keine Kontodaten hinterlegt. Solange dies nicht geschehen ist, können Sie keine Überweisungen für den Lieferanten anlegen.', 'No bins have been added to this warehouse yet.' => 'Es wurden zu diesem Lager noch keine Lagerplätze angelegt.', @@ -3272,6 +3273,7 @@ $self->{texts} = { 'The custom variable has been saved.' => 'Die benutzerdefinierte Variable wurde gespeichert.', 'The custom variable is in use and cannot be deleted.' => 'Die benutzerdefinierte Variable ist in Benutzung und kann nicht gelöscht werden.', 'The customer name is missing.' => 'Der Kundenname fehlt.', + 'The customer\'s bank account number (IBAN) is missing.' => 'Die Kontonummer (IBAN) des Kunden fehlt.', 'The database for user management and authentication does not exist. You can create let kivitendo create it with the following parameters:' => 'Die Datenbank für die Benutzeranmeldung existiert nicht. Sie können Sie von kivitendo automatisch mit den folgenden Parametern anlegen lassen:', 'The database host is missing.' => 'Der Datenbankhost fehlt.', 'The database name is missing.' => 'Der Datenbankname fehlt.', @@ -3808,6 +3810,7 @@ $self->{texts} = { 'Use default warehouse for assembly transfer' => 'Zum Fertigen Standardlager des Bestandteils verwenden', 'Use existing templates' => 'Vorhandene Druckvorlagen verwenden', 'Use fill up when calculating shipped quantities?' => 'Sollen nicht verlinkte Positionen abgeglichen werden?', + 'Use for ZUGFeRD' => 'Nutzung mit ZUGFeRD', 'Use linked items' => 'Verknüpfte Positionen verwenden', 'Use master default bin for Default Transfer, if no default bin for the part is configured' => 'Standardlagerplatz für Ein- / Auslagern über Standard-Lagerplatz, falls für die Ware kein expliziter Lagerplatz konfiguriert ist', 'Use settings from client configuration' => 'Einstellungen aus Mandantenkonfiguration folgen', diff --git a/sql/Pg-upgrade2/bank_account_flag_for_zugferd_usage.sql b/sql/Pg-upgrade2/bank_account_flag_for_zugferd_usage.sql new file mode 100644 index 000000000..d62d7ee1c --- /dev/null +++ b/sql/Pg-upgrade2/bank_account_flag_for_zugferd_usage.sql @@ -0,0 +1,14 @@ +-- @tag: bank_account_flag_for_zugferd_usage +-- @description: Bankkonto für die Nutzung mit ZUGFeRD markieren +-- @depends: release_3_5_5 +ALTER TABLE bank_accounts +ADD COLUMN use_for_zugferd BOOLEAN; + +UPDATE bank_accounts +SET use_for_zugferd = ( + SELECT COUNT(*) + FROM bank_accounts +) = 1; + +ALTER TABLE bank_accounts +ALTER COLUMN use_for_zugferd SET NOT NULL; diff --git a/templates/webpages/simple_system_setting/_bank_account_form.html b/templates/webpages/simple_system_setting/_bank_account_form.html index f3da6cc20..21e58fcff 100644 --- a/templates/webpages/simple_system_setting/_bank_account_form.html +++ b/templates/webpages/simple_system_setting/_bank_account_form.html @@ -31,6 +31,10 @@ [% LxERP.t8('Chart') %] [% P.chart.picker('object.chart_id', SELF.object.chart_id, type='AR_paid,AP_paid', category='A,L,Q', choose=1, style=style, "data-validate"="required", "data-title"=LxERP.t8("Chart")) %] + + [% LxERP.t8('Use for ZUGFeRD') %] + [% L.checkbox_tag('object.use_for_zugferd', checked = SELF.object.use_for_zugferd, for_submit=1) %] + [% LxERP.t8('Obsolete') %] [% L.checkbox_tag('object.obsolete', checked = SELF.object.obsolete, for_submit=1) %] -- 2.20.1