Fixt #348 DatevExport kommt mit bestimmten Zeichen im Buchungstext nicht klar
authorJan Büren <jan@kivitendo.de>
Thu, 29 Nov 2018 13:45:33 +0000 (14:45 +0100)
committerJan Büren <jan@kivitendo.de>
Thu, 29 Nov 2018 13:45:33 +0000 (14:45 +0100)
In der Mandantenkonfiguration befindet sich jetzt eine Einstellung,
welche die Kodierung des DATEV-Exports steuert. DATEV erwartet CP1252.
kivitendo kann diese Kodierung so vom kivitendo Nutzer einfordern, alternativ nicht
vorhandenen Zeichen versuchen zu ersetzen oder die DATEV-Erwartung ignorieren
und UTF-8 liefern. Voreingestellt ist CP1252 mit Ersetzungen

SL/DATEV.pm
SL/DATEV/CSV.pm
SL/DB/MetaSetup/Default.pm
locale/de/all
sql/Pg-upgrade2/datev_export_format.sql [new file with mode: 0644]
templates/webpages/client_config/_datev_check_configuration.html
templates/webpages/client_config/form.html

index ebff4fc..51fe4bd 100644 (file)
@@ -35,6 +35,7 @@ use SL::DATEV::KNEFile;
 use SL::DATEV::CSV;
 use SL::DB;
 use SL::HTML::Util ();
+use SL::Iconv;
 use SL::Locale::String qw(t8);
 
 use Data::Dumper;
@@ -393,12 +394,32 @@ sub csv_export {
                 eol          => "\r\n",
               }) or die "Cannot use CSV: ".Text::CSV_XS->error_diag();
 
-    my $csv_file = IO::File->new($self->export_path . '/' . $filename, '>:encoding(cp1252)') or die "Can't open: $!";
+    # get encoding from defaults - use cp1252 if DATEV strict export is used
+    my $enc = ($::instance_conf->get_datev_export_format eq 'cp1252') ? 'cp1252' : 'utf-8';
+    my $csv_file = IO::File->new($self->export_path . '/' . $filename, ">:encoding($enc)") or die "Can't open: $!";
+
     $csv->print($csv_file, $_) for @{ $datev_csv->header };
     $csv->print($csv_file, $_) for @{ $datev_csv->lines  };
     $csv_file->close;
     $self->{warnings} = $datev_csv->warnings;
 
+    # convert utf-8 to cp1252//translit if set
+    if ($::instance_conf->get_datev_export_format eq 'cp1252-translit') {
+
+      my $filename_translit = "EXTF_DATEV_kivitendo_translit" . $self->from->ymd() . '-' . $self->to->ymd() . ".csv";
+      open my $fh_in,  '<:encoding(UTF-8)',  $self->export_path . '/' . $filename or die "could not open $filename for reading: $!";
+      open my $fh_out, '>', $self->export_path . '/' . $filename_translit         or die "could not open $filename_translit for writing: $!";
+
+      my $converter = SL::Iconv->new("utf-8", "cp1252//translit");
+
+      print $fh_out $converter->convert($_) while <$fh_in>;
+      close $fh_in;
+      close $fh_out;
+
+      unlink $self->export_path . '/' . $filename or warn "Could not unlink $filename: $!";
+      $filename = $filename_translit;
+    }
+
     return { download_token => $self->download_token, filenames => $filename };
 
   } elsif ($self->exporttype == DATEV_ET_STAMM) {
index 1f31f16..e24755b 100644 (file)
@@ -103,7 +103,10 @@ my @kivitendo_to_datev = (
                               max_length      => 12,
                               type            => 'Text',
                               default         => '',
-                              input_check     => sub { my ($text) = @_; check_encoding($text); },
+                              input_check     => sub { return 1 unless $::instance_conf->get_datev_export_format eq 'cp1252';
+                                                       my ($text) = @_; check_encoding($text); },
+                              valid_check     => sub { return 1 if     $::instance_conf->get_datev_export_format eq 'cp1252';
+                                                       my ($text) = @_; check_encoding($text); },
                               formatter       => sub { my ($input) = @_; return substr($input, 0, 12) },
                             },
                             {
@@ -127,8 +130,10 @@ my @kivitendo_to_datev = (
                               max_length      => 60,
                               type            => 'Text',
                               default         => '',
-                              input_check     => sub { my ($text) = @_; return 1 unless $text; check_encoding($text);  },
-                              formatter       => sub { my ($input) = @_; return substr($input, 0, 60) },
+                              input_check     => sub { return 1 unless $::instance_conf->get_datev_export_format eq 'cp1252';
+                                                       my ($text) = @_; check_encoding($text); },
+                              valid_check     => sub { return 1 if     $::instance_conf->get_datev_export_format eq 'cp1252';
+                                                       my ($text) = @_; check_encoding($text); },
                             },  # pos 14
                             {
                               kivi_datev_name => 'not yet implemented',
index 1dff040..b948198 100644 (file)
@@ -45,6 +45,7 @@ __PACKAGE__->meta->columns(
   datev_check_on_gl_transaction             => { type => 'boolean', default => 'true' },
   datev_check_on_purchase_invoice           => { type => 'boolean', default => 'true' },
   datev_check_on_sales_invoice              => { type => 'boolean', default => 'true' },
+  datev_export_format                       => { type => 'enum', check_in => [ 'cp1252', 'cp1252-translit', 'utf-8' ], db_type => 'datev_export_format_enum', default => 'cp1252-translit' },
   disabled_price_sources                    => { type => 'array' },
   doc_delete_printfiles                     => { type => 'boolean', default => 'false' },
   doc_files                                 => { type => 'boolean', default => 'false' },
index e99d817..0967e47 100755 (executable)
@@ -844,8 +844,9 @@ $self->{texts} = {
   'DATEV - Export Assistent'    => 'DATEV-Exportassistent',
   'DATEV Angaben'               => 'DATEV-Angaben',
   'DATEV Export'                => 'DATEV-Export',
-  'DATEV check configuration'   => 'Einstellungen für DATEV-Prüfung',
   'DATEV check returned errors:' => 'Die DATEV Prüfung dieser Buchung ergab Fehler:',
+  'DATEV configuration'         => 'Einstellungen für DATEV',
+  'DATEV expects the encoding to be Western Europe conform (LATIN-1, cp1252). By setting this to "Strict and halt" the DATEV export halts with a error if there is a single character in "Posting Text" which is not LATIN-1 encodeable. By setting this to "Strict but replace" kivitendo will replace the character with a similar one and the export will simply warn about those fields. By setting this to relaxed (UTF-8) the DATEV export encoding will be in kivitendo (UTF-8) encoded and the external import program has to handle this (this may work for DATEV deriviates or future versions of DATEV). Background details: For example turkish characters (Ç) are not valid cp1252 charactes and armenian characters like "Գեղարդ" are probably not replaceable in cp1252' => 'DATEV erwartet westeuropäische Zeichenkodierung (LATIN-1, cp1252). Die Einstellung "Strikt und Abbruch" erlaubt keine nicht kodiebaren Zeichen im DATEV-Export und bricht diesen mit einer Fehlermeldung ab. Die Einstellung "Strikt mit Ersetzungen" versucht ähnliche Zeichen (bspw. c statt ć) zu verwenden und gibt zusätzlich eine Warnung beim DATEV-Expport aus. Die Einstellung "Lax (UTF-8)" ignoriert diese Anforderung und übergibt die Daten im kivitendo-konformen UTF-8 Format. Letzteres kann für zukünftige DATEV-Version oder DATEV-kompatible Alternativen interessant sein. Hintergrund-Info: Beispielsweise sind schon türkische Zeichen (Ç) nicht mehr im westeuropäischen Zeichensätze enthalten und armenische Zeiche wie "Գեղարդ" könnnen sicherlich überhaupt nicht mit Zeichen in cp1252 ersetzt werden.',
   'DATEX - Export Assistent'    => 'DATEV-Exportassistent',
   'DELETED'                     => 'Gelöscht',
   'DFV-Kennzeichen'             => 'DFV-Kennzeichen',
@@ -882,6 +883,7 @@ $self->{texts} = {
   'Date missing!'               => 'Datum fehlt!',
   'Date the payment is due in full' => 'Das Datum, bis die Rechnung in voller Höhe bezahlt werden muss',
   'Date the payment is due with discount' => 'Das Datum, bis die Rechnung unter Abzug von Skonto bezahlt werden kann',
+  'Datev export encoding'       => 'DATEV-Export Kodierung',
   'Datevautomatik'              => 'Datev-Automatik',
   'Datum von'                   => 'Datum von',
   'Deactivate by default'       => 'Deaktiviert als Voreinstellung',
@@ -2530,6 +2532,7 @@ $self->{texts} = {
   'Reference / Invoice Number'  => 'Referenz / Rechnungsnummer',
   'Reference day'               => 'Referenztag',
   'Reference missing!'          => 'Referenz fehlt!',
+  'Relaxed (UTF-8)'             => 'Lax (UTF-8)',
   'Release From Stock'          => 'Lagerausgang',
   'Remaining'                   => 'Rest',
   'Remaining Amount'            => 'abzurechnender Betrag',
@@ -2991,6 +2994,8 @@ $self->{texts} = {
   'Storno (one letter abbreviation)' => 'S',
   'Storno Invoice'              => 'Stornorechnung',
   'Street'                      => 'Straße',
+  'Strict and halt'             => 'Strikt und Abbruch',
+  'Strict but replace'          => 'Strikt mit Ersetzungen',
   'Style the picture with the following CSS code' => 'Bildeigenschaft mit folgendem CSS-Style versehen',
   'Stylesheet'                  => 'Stilvorlage',
   'Sub function blocks'         => 'Unterfunktionsblöcke',
diff --git a/sql/Pg-upgrade2/datev_export_format.sql b/sql/Pg-upgrade2/datev_export_format.sql
new file mode 100644 (file)
index 0000000..c4d79f7
--- /dev/null
@@ -0,0 +1,8 @@
+-- @tag: datev_export_format
+-- @description: Setzt die ausgehende Formatierung des DATEV-Exports
+-- @depends: release_3_5_1
+
+CREATE TYPE datev_export_format_enum AS ENUM ('cp1252', 'cp1252-translit', 'utf-8');
+
+ALTER TABLE defaults ADD COLUMN datev_export_format datev_export_format_enum default 'cp1252-translit';
+
index ec1b433..dc99c21 100644 (file)
    <td>[% L.yes_no_tag('defaults.datev_check_on_gl_transaction', SELF.defaults.datev_check_on_gl_transaction) %]</td>
    <td>[% LxERP.t8('Perform check when a gl transaction is posted?') %]</td>
   </tr>
+  <tr>
+   <td align="right">[% LxERP.t8('Datev export encoding') %]</td>
+   <td>[% L.select_tag('defaults.datev_export_format', [ [ 'cp1252', LxERP.t8('Strict and halt') ],[ 'cp1252-translit', LxERP.t8('Strict but replace') ],[ 'utf-8', LxERP.t8('Relaxed (UTF-8)') ]  ], default=SELF.defaults.datev_export_format) %]
+   <td>[% LxERP.t8('DATEV expects the encoding to be Western Europe conform (LATIN-1, cp1252). By setting this to "Strict and halt" the DATEV export halts with a error if there is a single character in "Posting Text" which is not LATIN-1 encodeable. By setting this to "Strict but replace" kivitendo will replace the character with a similar one and the export will simply warn about those fields. By setting this to relaxed (UTF-8) the DATEV export encoding will be in kivitendo (UTF-8) encoded and the external import program has to handle this (this may work for DATEV deriviates or future versions of DATEV). Background details: For example turkish characters (Ç) are not valid cp1252 charactes and armenian characters like "Գեղարդ" are probably not replaceable in cp1252') %]</td>
+  </tr>
  </table>
 </div>
index f0d2621..dcb5576 100644 (file)
@@ -84,7 +84,7 @@ $(function() {
    <li><a href="#default_accounts">[% LxERP.t8('Default Accounts') %]</a></li>
    <li><a href="#posting_configuration">[% LxERP.t8('Posting Configuration') %]</a></li>
    [% IF FORM.feature_datev %]
-     <li><a href="#datev_check_configuration">[% LxERP.t8('DATEV check configuration') %]</a></li>
+     <li><a href="#datev_check_configuration">[% LxERP.t8('DATEV configuration') %]</a></li>
    [% END %]
    <li><a href="#orders_deleteable">[% LxERP.t8('Orders / Delivery Orders deleteable') %]</a></li>
 [%- IF INSTANCE_CONF.get_doc_storage %]