{ 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' },
],
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;
# <ram:IncludedSupplyChainTradeLineItem>
}
+sub _specified_trade_settlement_payment_means {
+ my ($self, %params) = @_;
+
+ # <ram:SpecifiedTradeSettlementPaymentMeans>
+ $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;
+ # </ram:SpecifiedTradeSettlementPaymentMeans>
+}
+
sub _taxes {
my ($self, %params) = @_;
$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);
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) {
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();
# <rsm:CrossIndustryInvoice>
- $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;
# </rsm:CrossIndustryInvoice>
return $output;
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' ]);
'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.',
'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.',
'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',
--- /dev/null
+-- @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;
<th align="right">[% LxERP.t8('Chart') %]</th>
<td>[% 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")) %]</td>
</tr>
+ <tr>
+ <th align="right">[% LxERP.t8('Use for ZUGFeRD') %]</th>
+ <td>[% L.checkbox_tag('object.use_for_zugferd', checked = SELF.object.use_for_zugferd, for_submit=1) %]</td>
+ </tr>
<tr>
<th align="right">[% LxERP.t8('Obsolete') %]</th>
<td>[% L.checkbox_tag('object.obsolete', checked = SELF.object.obsolete, for_submit=1) %]</td>