epic-s6ts
[kivitendo-erp.git] / SL / GoBD.pm
index e2a3b96..9c95a0a 100644 (file)
@@ -19,6 +19,7 @@ use List::UtilsBy qw(partition_by sort_by);
 use SL::DB::Helper::ALL; # since we work on meta data, we need everything
 use SL::DB::Helper::Mappings;
 use SL::Locale::String qw(t8);
+use SL::Version;
 
 use Rose::Object::MakeMethods::Generic (
   scalar                  => [ qw(from to writer company location) ],
@@ -44,7 +45,7 @@ my %column_titles = (
      description    => t8('Description'),
    },
    customer_vendor => {
-     id             => t8('ID'),
+     id             => t8('ID (lit)'),
      name           => t8('Name'),
      department_1   => t8('Department 1'),
      department_2   => t8('Department 2'),
@@ -71,21 +72,26 @@ my %datev_column_defs = (
   amount            => { type => 'Rose::DB::Object::Metadata::Column::Numeric', text => t8('Amount'), },
   credit_accname    => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Credit Account Name'), },
   credit_accno      => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Credit Account'), },
+  credit_amount     => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Credit Amount'), },
+  credit_tax        => { type => 'Rose::DB::Object::Metadata::Column::Numeric', text => t8('Credit Tax (lit)'), },
   debit_accname     => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Debit Account Name'), },
   debit_accno       => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Debit Account'), },
+  debit_amount      => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Debit Amount'), },
+  debit_tax         => { type => 'Rose::DB::Object::Metadata::Column::Numeric', text => t8('Debit Tax (lit)'), },
   invnumber         => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Reference'), },
   name              => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Name'), },
   notes             => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Notes'), },
-  tax               => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Tax'), },
+  tax               => { type => 'Rose::DB::Object::Metadata::Column::Numeric', text => t8('Tax'), },
   taxdescription    => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('tax_taxdescription'), },
   taxkey            => { type => 'Rose::DB::Object::Metadata::Column::Integer', text => t8('Taxkey'), },
   tax_accname       => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Tax Account Name'), },
   tax_accno         => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Tax Account'), },
-  transdate         => { type => 'Rose::DB::Object::Metadata::Column::Date',    text => t8('Invoice Date'), },
+  transdate         => { type => 'Rose::DB::Object::Metadata::Column::Date',    text => t8('Transdate'), },
   vcnumber          => { type => 'Rose::DB::Object::Metadata::Column::Text',    text => t8('Customer/Vendor Number'), },
   customer_id       => { type => 'Rose::DB::Object::Metadata::Column::Integer', text => t8('Customer (database ID)'), },
   vendor_id         => { type => 'Rose::DB::Object::Metadata::Column::Integer', text => t8('Vendor (database ID)'), },
   itime             => { type => 'Rose::DB::Object::Metadata::Column::Date',    text => t8('Create Date'), },
+  gldate            => { type => 'Rose::DB::Object::Metadata::Column::Date',    text => t8('Gldate'), },
 );
 
 my @datev_columns = qw(
@@ -93,11 +99,11 @@ my @datev_columns = qw(
   customer_id vendor_id
   name           vcnumber
   transdate    invnumber      amount
-  debit_accno  debit_accname
-  credit_accno credit_accname
+  debit_accno  debit_accname debit_amount debit_tax
+  credit_accno credit_accname credit_amount credit_tax
   taxdescription tax
   tax_accno    tax_accname    taxkey
-  notes itime
+  notes itime gldate
 );
 
 # rows in this listing are tiers.
@@ -412,7 +418,11 @@ sub do_datev_csv_export {
 
   my $datev = SL::DATEV->new(from => $self->from, to => $self->to);
 
-  $datev->_get_transactions(from_to => $datev->fromto);
+  $datev->generate_datev_data(from_to => $datev->fromto);
+
+  if ($datev->errors) {
+    die [ $datev->errors ];
+  }
 
   for my $transaction (@{ $datev->{DATEV} }) {
     for my $entry (@{ $transaction }) {
@@ -438,24 +448,36 @@ sub do_datev_csv_export {
     my $is_payment     = any { $_->{link} =~ m{A[PR]_paid} } @{ $transaction };
 
     my ($soll, $haben) = map { $transaction->[$_] } ($transaction->[0]->{amount} > 0 ? (1, 0) : (0, 1));
-    my $tax            = defined($soll->{tax_amount}) ? $soll : defined($haben->{tax_amount}) ? $haben : {};
+    my $tax            = defined($soll->{tax_accno}) ? $soll : defined($haben->{tax_accno}) ? $haben : {};
     my $amount         = defined($soll->{net_amount}) ? $soll : $haben;
     $haben->{notes}    = ($haben->{memo} || $soll->{memo}) if $haben->{memo} || $soll->{memo};
     $haben->{notes}  //= '';
     $haben->{notes}    =  SL::HTML::Util->strip($haben->{notes});
 
+    my $tax_amount = defined $amount->{net_amount} ? abs($amount->{amount}) - abs($amount->{net_amount}) : 0;
+
+    $tax = {} if abs($tax_amount) < 0.001;
+
     my %row            = (
       amount           => $::form->format_amount($myconfig, abs($amount->{amount}),5),
       debit_accno      => $soll->{accno},
       debit_accname    => $soll->{accname},
+      debit_amount     => $::form->format_amount($myconfig, abs(-$soll->{amount}),5),
+      debit_tax        => $soll->{tax_accno} ? $::form->format_amount($myconfig, $tax_amount, 5) : 0,
       credit_accno     => $haben->{accno},
       credit_accname   => $haben->{accname},
-      tax              => defined $amount->{net_amount} ? $::form->format_amount($myconfig, abs($amount->{amount}) - abs($amount->{net_amount}), 5) : 0,
+      credit_amount    => $::form->format_amount($myconfig, abs($haben->{amount}),5),,
+      credit_tax       => $haben->{tax_accno} ? $::form->format_amount($myconfig, $tax_amount, 5) : 0,
+      tax              => $::form->format_amount($myconfig, $tax_amount, 5),
       notes            => $haben->{notes},
       (map { ($_ => $tax->{$_})                    } qw(taxkey tax_accname tax_accno taxdescription)),
-      (map { ($_ => ($haben->{$_} // $soll->{$_})) } qw(trans_id invnumber name vcnumber transdate itime customer_id vendor_id)),
+      (map { ($_ => ($haben->{$_} // $soll->{$_})) } qw(trans_id invnumber name vcnumber transdate gldate itime customer_id vendor_id)),
     );
 
+#     if ($row{debit_amount} + $row{debit_tax} - ($row{credit_amount} + $row{credit_tax}) > 0.005) {
+#       $::lxdebug->dump(0,  "broken taxes", [ $transaction, \%row,  $row{debit_amount} + $row{debit_tax}, $row{credit_amount} + $row{credit_tax} ]);
+#     }
+
     _normalize_cell($_) for values %row; # see CAVEATS
 
     $csv->print($fh, [ map { $row{$_} } @datev_columns ]);
@@ -528,7 +550,7 @@ sub do_csv_export {
   my $query = "SELECT " . join(', ', @select_tokens) . " FROM $table $where_clause";
 
   my $sth = $::form->get_standard_dbh->prepare($query);
-  $sth->execute(@values) or die "error executing query $query: " . $sth->errstr;
+  $sth->execute(@values) or $::form->dberror($query);
 
   while (my $row = $sth->fetch) {
     for my $keep_col (@{ $known_tables{$table}{keep} || [] }) {
@@ -558,7 +580,7 @@ sub tag {
 
 sub make_comment {
   my $gobd_version  = API_VERSION();
-  my $kivi_version  = $::form->read_version;
+  my $kivi_version  = SL::Version->get_version;
   my $person        = $::myconfig{name};
   my $contact       = join ', ',
     (t8("Email") . ": $::myconfig{email}" ) x!! $::myconfig{email},
@@ -595,6 +617,8 @@ sub all_tables {
 sub _normalize_cell {
   $_[0] =~ s/\r\n/ /g;
   $_[0] =~ s/,/;/g;
+  $_[0] =~ s/"/'/g;
+  $_[0] =~ s/!/./g;
   $_[0]
 }
 
@@ -748,6 +772,23 @@ on that symbol no matter what surrounds or preceeds it.
 
 =item *
 
+Oh and of course C<TextEncapsulator> is also not allowed in data. It's just
+stripped at the beginning and end of data.
+
+=item *
+
+And the character "!" is used internally as a warning signal and must not be
+present in the data as well.
+
+=item *
+
+C<VariableLength> data is truncated on import to 512 bytes (Note: it said
+characters, but since they are mutilating data into a single byte encoding
+anyway, they most likely meant bytes). The auditor recommends splitting into
+multiple columns.
+
+=item *
+
 Despite the standard specifying UTF-8 as a valid encoding the IDEA software
 will just downgrade everything to latin1.