ZUGFeRD: Validierung diverser nötiger Parameter
authorMoritz Bunkus <m.bunkus@linet-services.de>
Fri, 28 Feb 2020 16:13:34 +0000 (17:13 +0100)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Fri, 28 Feb 2020 16:24:00 +0000 (17:24 +0100)
SL/DB/Helper/ZUGFeRD.pm
SL/X.pm
bin/mozilla/io.pl
locale/de/all

index 5cee13a..b129de4 100644 (file)
@@ -15,7 +15,7 @@ use SL::Helper::UNECERecommendation20;
 
 use Carp;
 use Encode qw(encode);
-use List::MoreUtils qw(pairwise);
+use List::MoreUtils qw(any pairwise);
 use List::Util qw(first sum);
 use Template;
 use XML::Writer;
@@ -515,9 +515,42 @@ sub _supply_chain_trade_transaction {
   #   </rsm:SupplyChainTradeTransaction>
 }
 
+sub _validate_data {
+  my ($self) = @_;
+
+  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 (!$::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.'));
+  }
+
+  if ($::instance_conf->get_address_country && !SL::Helper::ISO3166::map_name_to_alpha_2_code($::instance_conf->get_address_country)) {
+    SL::X::ZUGFeRDValidation->throw(message => $prefix . $::locale->text('The country from the company\'s address in the client configuration cannot be mapped to an ISO 3166-1 alpha 2 code.'));
+  }
+
+  if ($self->customer->country && !SL::Helper::ISO3166::map_name_to_alpha_2_code($self->customer->country)) {
+    SL::X::ZUGFeRDValidation->throw(message => $prefix . $::locale->text('The country from the customer\'s address cannot be mapped to an ISO 3166-1 alpha 2 code.'));
+  }
+
+  if (!SL::Helper::ISO4217::map_currency_name_to_code($self->currency->name)) {
+    SL::X::ZUGFeRDValidation->throw(message => $prefix . $::locale->text('The currency "#1" cannot be mapped to an ISO 4217 currency code.', $self->currency->name));
+  }
+
+  my $failed_unit = first { !SL::Helper::UNECERecommendation20::map_name_to_code($_) } map { $_->unit } @{ $self->items };
+  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));
+  }
+}
+
 sub create_zugferd_data {
   my ($self) = @_;
 
+  _validate_data($self);
+
   my %ptc_data = $self->calculate_prices_and_taxes;
   my $output   = '';
   my $xml      = XML::Writer->new(
diff --git a/SL/X.pm b/SL/X.pm
index 756793b..b8ae60a 100644 (file)
--- a/SL/X.pm
+++ b/SL/X.pm
@@ -27,6 +27,9 @@ use Exception::Class (
   'SL::X::DBUtilsError' => {
     isa                 => 'SL::X::DBError',
   },
+  'SL::X::ZUGFeRDValidation' => {
+    isa                 => 'SL::X::Base',
+  },
 );
 
 1;
index 1ee0d2c..0cde59c 100644 (file)
@@ -2122,17 +2122,23 @@ sub _maybe_attach_zugferd_data {
 
   return if !$record || !$record->can('create_pdf_a_print_options') || !$record->can('create_zugferd_data');
 
-  my $xmlfile = File::Temp->new;
-  $xmlfile->print($record->create_zugferd_data);
-  $xmlfile->close;
-
-  $form->{TEMPLATE_DRIVER_OPTIONS}->{pdf_a}           = $record->create_pdf_a_print_options(zugferd_xmp_data => $record->create_zugferd_xmp_data);
-  $form->{TEMPLATE_DRIVER_OPTIONS}->{pdf_attachments} = [
-    { source       => $xmlfile,
-      name         => 'ZUGFeRD-invoice.xml',
-      description  => $::locale->text('ZUGFeRD invoice'),
-      relationship => '/Alternative',
-      mime_type    => 'text/xml',
-    }
-  ];
+  eval {
+    my $xmlfile = File::Temp->new;
+    $xmlfile->print($record->create_zugferd_data);
+    $xmlfile->close;
+
+    $form->{TEMPLATE_DRIVER_OPTIONS}->{pdf_a}           = $record->create_pdf_a_print_options(zugferd_xmp_data => $record->create_zugferd_xmp_data);
+    $form->{TEMPLATE_DRIVER_OPTIONS}->{pdf_attachments} = [
+      { source       => $xmlfile,
+        name         => 'ZUGFeRD-invoice.xml',
+        description  => $::locale->text('ZUGFeRD invoice'),
+        relationship => '/Alternative',
+        mime_type    => 'text/xml',
+      }
+    ];
+  };
+
+  if (my $e = SL::X::ZUGFeRDValidation->caught) {
+    $::form->error($e->message);
+  }
 }
index 143a02d..550a53e 100755 (executable)
@@ -2133,6 +2133,7 @@ $self->{texts} = {
   'On Order'                    => 'Ist bestellt',
   'On the next page the type of all variables can be set.' => 'Auf der folgenden Seite können die Typen aller Variablen gesetzt werden.',
   'One of the columns "qty" or "target_qty" must be given. If "target_qty" is given, the quantity to transfer for each transfer will be calculate, so that the quantity for this part, warehouse and bin will result in the given "target_qty" after each transfer.' => 'Eine der Spalten "qty" oder "target_qty" muss angegeben werden. Wird "target_qty" angegeben, so wird die zu bewegende Menge für jede Lagerbewegung so berechnet, dass die Lagermenge für diesen Artikel, Lager und Lagerplatz nach jeder Lagerbewegung der angegebenen Zielmenge entspricht.',
+  'One of the units used (#1) cannot be mapped to a known unit code from the UN/ECE Recommendation 20 list.' => 'Eine der verwendeten Einheiten (#1) kann keinem der bekannten Einheiten-Codes aus der Liste UN/ECE Recommendation 20 zugeordnet werden.',
   'One or more Perl modules missing' => 'Ein oder mehr Perl-Module fehlen',
   'Onhand only sets the quantity in master data, not in inventory. This is only a legacy info field and will be overwritten as soon as a inventory transfer happens.' => 'Das Import-Feld Auf Lager setzt nur die Menge in den Stammdaten, nicht im Lagerbereich. Dies ist historisch gewachsen nur ein Informationsfeld was mit dem tatsächlichen Wert überschrieben wird, sobald eine wirkliche Lagerbewegung stattfindet (DB-Trigger).',
   'Only Price'                  => 'Nur Preis',
@@ -3183,7 +3184,9 @@ $self->{texts} = {
   'The SQL query can be parameterized with variables named as follows: <%name%>.' => 'Die SQL-Abfrage kann mittels Variablen wie folgt parametrisiert werden: <%Variablenname%>.',
   'The SQL query does not contain any parameter that need to be configured.' => 'Die SQL-Abfrage enthält keine Parameter, die angegeben werden müssten.',
   'The URL is missing.'         => 'URL fehlt',
+  'The VAT registration number is missing in the client configuration.' => 'Die Umsatzsteuer-ID-Nummer fehlt in der Mandantenkonfiguration.',
   'The WebDAV feature has been used.' => 'Das WebDAV-Feature wurde benutzt.',
+  'The ZUGFeRD invoice data cannot be generated because the data validation failed.' => 'Die ZUGFeRD-Rechnungsdaten können nicht erzeugt werden, da die Validierung fehlschlug.',
   'The ZUGFeRD notes have been saved.' => 'Die ZUGFeRD-Notizen wurden gespeichert.',
   'The abbreviation is missing.' => 'Abkürzung fehlt',
   'The access rights a user has within a client instance is still governed by his group membership.' => 'Welche Zugriffsrechte ein Benutzer innerhalb eines Mandanten hat, wird weiterhin über Gruppenmitgliedschaften geregelt.',
@@ -3237,6 +3240,7 @@ $self->{texts} = {
   'The columns &quot;Dunning Duedate&quot;, &quot;Total Fees&quot; and &quot;Interest&quot; show data for the previous dunning created for this invoice.' => 'Die Spalten &quot;Zahlbar bis&quot;, &quot;Kumulierte Gebühren&quot; und &quot;Zinsen&quot; zeigen Daten der letzten für diese Rechnung erzeugten Mahnung.',
   'The combination of database host, port and name is not unique.' => 'Die Kombination aus Datenbankhost, -port und -name ist nicht eindeutig.',
   'The command is missing.'     => 'Der Befehl fehlt.',
+  'The company\'s address information is incomplete in the client configuration.' => 'Die Firmenadresse in der Mandantenkonfiguration ist unvollständig.',
   'The connection to the LDAP server cannot be encrypted (SSL/TLS startup failure). Please check config/kivitendo.conf.' => 'Die Verbindung zum LDAP-Server kann nicht verschlüsselt werden (Fehler bei SSL/TLS-Initialisierung). Bitte überprüfen Sie die Angaben in config/kivitendo.conf.',
   'The connection to the authentication database failed:' => 'Die Verbindung zur Authentifizierungsdatenbank schlug fehl:',
   'The connection to the configured client database "#1" on host "#2:#3" failed.' => 'Die Verbindung zur konfigurierten Datenbank "#1" auf Host "#2:#3" schlug fehl.',
@@ -3246,8 +3250,11 @@ $self->{texts} = {
   'The connection to the template database failed:' => 'Die Verbindung zur Vorlagendatenbank schlug fehl:',
   'The connection was established successfully.' => 'Die Verbindung zur Datenbank wurde erfolgreich hergestellt.',
   'The contact person attribute "birthday" is converted from a free-form text field into a date field.' => 'Das Kontaktpersonenfeld "Geburtstag" wird von einem freien Textfeld auf ein Datumsfeld umgestellt.',
+  'The country from the company\'s address in the client configuration cannot be mapped to an ISO 3166-1 alpha 2 code.' => 'Das Land der Firmenadresse in der Mandantenkonfiguration kann keinem der bekannten ISO 3166-1 Alpha 2-Codes zugeordnet werden.',
+  'The country from the customer\'s address cannot be mapped to an ISO 3166-1 alpha 2 code.' => 'Das Land aus der Kunden-Rechnungsadresse kann keinem der bekannten ISO 3166-1 Alpha 2-Codes zugeordnet werden.',
   'The creation of the authentication database failed:' => 'Das Anlegen der Authentifizierungsdatenbank schlug fehl:',
   'The credentials (username & password) for connecting database are wrong.' => 'Die Daten (Benutzername & Passwort) für das Login zur Datenbank sind falsch.',
+  'The currency "#1" cannot be mapped to an ISO 4217 currency code.' => 'Die Währung "#1" kann keinem der bekannten ISO 4217-Codes zugeordnet werden.',
   'The custom data export has been deleted.' => 'Der benutzerdefinierte Datenexport wurde gelöscht.',
   'The custom data export has been saved.' => 'Der benutzerdefinierte Datenexport wurde gespeichert.',
   'The custom variable has been created.' => 'Die benutzerdefinierte Variable wurde erfasst.',