1 package SL::DATEV::CSV;
 
   7 use Scalar::Util qw(looks_like_number);
 
  11 use SL::Helper::DateTime;
 
  12 use SL::Locale::String qw(t8);
 
  13 use SL::Util qw(trim);
 
  16 use Rose::Object::MakeMethods::Generic (
 
  17   scalar => [ qw(datev_lines from to locked warnings) ],
 
  20 my @kivitendo_to_datev = (
 
  22                               kivi_datev_name => 'umsatz',
 
  23                               csv_header_name => t8('Transaction Value'),
 
  27                               input_check     => sub { my ($input) = @_; return (looks_like_number($input) && length($input) <= 13 && $input > 0) },
 
  28                               formatter       => \&_format_amount,
 
  29                               valid_check     => sub { my ($check) = @_; return ($check =~ m/^\d{1,10}(\,\d{1,2})?$/) },
 
  32                               kivi_datev_name => 'soll_haben_kennzeichen',
 
  33                               csv_header_name => t8('Debit/Credit Label'),
 
  38                               input_check     => sub { my ($check) = @_; return ($check =~ m/^(S|H)$/) },
 
  39                               formatter       => sub { my ($input) = @_; return $input eq 'H' ? 'H' : 'S' },
 
  40                               valid_check     => sub { my ($check) = @_; return ($check =~ m/^(S|H)$/) },
 
  43                               kivi_datev_name => 'waehrung',
 
  44                               csv_header_name => t8('Transaction Value Currency Code'),
 
  48                               input_check     => sub { my ($check) = @_; return ($check eq '' || $check =~ m/^[A-Z]{3}$/) },
 
  49                               valid_check     => sub { my ($check) = @_; return ($check =~ m/^[A-Z]{3}$/) },
 
  52                               kivi_datev_name => 'wechselkurs',
 
  53                               csv_header_name => t8('Exchange Rate'),
 
  57                               valid_check     => sub { my ($check) = @_; return ($check =~ m/^[0-9]*\.?[0-9]*$/) },
 
  60                               kivi_datev_name => 'not yet implemented',
 
  61                               csv_header_name => t8('Base Transaction Value'),
 
  64                               kivi_datev_name => 'not yet implemented',
 
  65                               csv_header_name => t8('Base Transaction Value Currency Code'),
 
  68                               kivi_datev_name => 'konto',
 
  69                               csv_header_name => t8('Account'),
 
  73                               input_check     => sub { my ($check) = @_; return ($check =~ m/^[0-9]{4,9}$/) },
 
  76                               kivi_datev_name => 'gegenkonto',
 
  77                               csv_header_name => t8('Contra Account'),
 
  81                               input_check     => sub { my ($check) = @_; return ($check =~ m/^[0-9]{4,9}$/) },
 
  84                               kivi_datev_name => 'buchungsschluessel',
 
  85                               csv_header_name => t8('Posting Key'),
 
  89                               input_check     => sub { my ($check) = @_; return ($check =~ m/^[0-9]{0,2}$/) },
 
  92                               kivi_datev_name => 'datum',
 
  93                               csv_header_name => t8('Invoice Date'),
 
  97                               input_check     => sub { my ($check) = @_; return (ref (DateTime->from_kivitendo($check)) eq 'DateTime') },
 
  98                               formatter       => sub { my ($input) = @_; return DateTime->from_kivitendo($input)->strftime('%d%m') },
 
  99                               valid_check     => sub { my ($check) = @_; return ($check =~ m/^[0-9]{4}$/) },
 
 102                               kivi_datev_name => 'belegfeld1',
 
 103                               csv_header_name => t8('Invoice Field 1'),
 
 107                               input_check     => sub { return 1 unless $::instance_conf->get_datev_export_format eq 'cp1252';
 
 108                                                        my ($text) = @_; check_encoding($text); },
 
 109                               valid_check     => sub { return 1 if     $::instance_conf->get_datev_export_format eq 'cp1252';
 
 110                                                        my ($text) = @_; check_encoding($text); },
 
 111                               formatter       => sub { my ($input) = @_; return substr($input, 0, 12) },
 
 114                               kivi_datev_name => 'belegfeld2',
 
 115                               csv_header_name => t8('Invoice Field 2'),
 
 119                               input_check     => sub { my ($check) = @_; return 1 unless $check; return (ref (DateTime->from_kivitendo($check)) eq 'DateTime') },
 
 120                               formatter       => sub { my ($input) = @_; return '' unless $input; return trim(DateTime->from_kivitendo($input)->strftime('%e%m%y')) },
 
 121                               valid_check     => sub { my ($check) = @_; return 1 unless $check; return ($check =~ m/^[0-9]{5,6}$/) },
 
 124                               kivi_datev_name => 'not yet implemented',
 
 125                               csv_header_name => t8('Discount'),
 
 129                               kivi_datev_name => 'buchungstext',
 
 130                               csv_header_name => t8('Posting Text'),
 
 134                               input_check     => sub { return 1 unless $::instance_conf->get_datev_export_format eq 'cp1252';
 
 135                                                        my ($text) = @_; check_encoding($text); },
 
 136                               valid_check     => sub { return 1 if     $::instance_conf->get_datev_export_format eq 'cp1252';
 
 137                                                        my ($text) = @_; check_encoding($text); },
 
 138                               formatter       => sub { my ($input) = @_; return substr($input, 0, 60) },
 
 141                               kivi_datev_name => 'not yet implemented',
 
 144                               kivi_datev_name => 'not yet implemented',
 
 147                               kivi_datev_name => 'not yet implemented',
 
 150                               kivi_datev_name => 'not yet implemented',
 
 153                               kivi_datev_name => 'not yet implemented',
 
 156                               kivi_datev_name => 'not yet implemented',
 
 157                               csv_header_name => t8('Link to invoice'),
 
 158                               max_length      => 210, # DMS Application shortcut and GUID
 
 160                                                       # "8DB85C02-4CC3-FF3E-06D7-7F87EEECCF3A".
 
 163                               kivi_datev_name => 'not yet implemented',
 
 166                               kivi_datev_name => 'not yet implemented',
 
 169                               kivi_datev_name => 'not yet implemented',
 
 172                               kivi_datev_name => 'not yet implemented',
 
 175                               kivi_datev_name => 'not yet implemented',
 
 178                               kivi_datev_name => 'not yet implemented',
 
 181                               kivi_datev_name => 'not yet implemented',
 
 184                               kivi_datev_name => 'not yet implemented',
 
 187                               kivi_datev_name => 'not yet implemented',
 
 190                               kivi_datev_name => 'not yet implemented',
 
 193                               kivi_datev_name => 'not yet implemented',
 
 196                               kivi_datev_name => 'not yet implemented',
 
 199                               kivi_datev_name => 'not yet implemented',
 
 202                               kivi_datev_name => 'not yet implemented',
 
 205                               kivi_datev_name => 'not yet implemented',
 
 208                               kivi_datev_name => 'not yet implemented',
 
 211                               kivi_datev_name => 'kost1',
 
 212                               csv_header_name => t8('Cost Center'),
 
 216                               input_check     => sub { my ($text) = @_; return 1 unless $text; check_encoding($text);  },
 
 217                               formatter       => sub { my ($input) = @_; return substr($input, 0, 8) },
 
 220                               kivi_datev_name => 'kost2',
 
 221                               csv_header_name => t8('Cost Center'),
 
 225                               input_check     => sub { my ($text) = @_; return 1 unless $text; check_encoding($text);  },
 
 226                               formatter       => sub { my ($input) = @_; return substr($input, 0, 8) },
 
 229                               kivi_datev_name => 'not yet implemented',
 
 230                               csv_header_name => t8('KOST Quantity'),
 
 233                               valid_check     => sub { my ($check) = @_; return ($check =~ m/^[0-9]{0,9}$/) },
 
 236                               kivi_datev_name => 'ustid',
 
 237                               csv_header_name => t8('EU Member State and VAT ID Number'),
 
 243                                                        return 1 if ('' eq $ustid);
 
 244                                                        return SL::VATIDNr->validate($ustid);
 
 246                               formatter       => sub { my ($input) = @_; $input =~ s/\s//g; return $input },
 
 249                                                        return 1 if ('' eq $ustid);
 
 250                                                        return SL::VATIDNr->validate($ustid);
 
 254                               kivi_datev_name => 'not yet implemented',
 
 257                               kivi_datev_name => 'not yet implemented',
 
 260                               kivi_datev_name => 'not yet implemented',
 
 263                               kivi_datev_name => 'not yet implemented',
 
 266                               kivi_datev_name => 'not yet implemented',
 
 269                               kivi_datev_name => 'not yet implemented',
 
 272                               kivi_datev_name => 'not yet implemented',
 
 275                               kivi_datev_name => 'not yet implemented',
 
 278                               kivi_datev_name => 'not yet implemented',
 
 281                               kivi_datev_name => 'not yet implemented',
 
 284                               kivi_datev_name => 'not yet implemented',
 
 287                               kivi_datev_name => 'not yet implemented',
 
 290                               kivi_datev_name => 'not yet implemented',
 
 293                               kivi_datev_name => 'not yet implemented',
 
 296                               kivi_datev_name => 'not yet implemented',
 
 299                               kivi_datev_name => 'not yet implemented',
 
 302                               kivi_datev_name => 'not yet implemented',
 
 305                               kivi_datev_name => 'not yet implemented',
 
 308                               kivi_datev_name => 'not yet implemented',
 
 311                               kivi_datev_name => 'not yet implemented',
 
 314                               kivi_datev_name => 'not yet implemented',
 
 317                               kivi_datev_name => 'not yet implemented',
 
 320                               kivi_datev_name => 'not yet implemented',
 
 323                               kivi_datev_name => 'not yet implemented',
 
 326                               kivi_datev_name => 'not yet implemented',
 
 329                               kivi_datev_name => 'not yet implemented',
 
 332                               kivi_datev_name => 'not yet implemented',
 
 335                               kivi_datev_name => 'not yet implemented',
 
 338                               kivi_datev_name => 'not yet implemented',
 
 341                               kivi_datev_name => 'not yet implemented',
 
 344                               kivi_datev_name => 'not yet implemented',
 
 347                               kivi_datev_name => 'not yet implemented',
 
 350                               kivi_datev_name => 'not yet implemented',
 
 353                               kivi_datev_name => 'not yet implemented',
 
 356                               kivi_datev_name => 'not yet implemented',
 
 359                               kivi_datev_name => 'not yet implemented',
 
 362                               kivi_datev_name => 'not yet implemented',
 
 365                               kivi_datev_name => 'not yet implemented',
 
 368                               kivi_datev_name => 'not yet implemented',
 
 371                               kivi_datev_name => 'not yet implemented',
 
 374                               kivi_datev_name => 'not yet implemented',
 
 377                               kivi_datev_name => 'not yet implemented',
 
 380                               kivi_datev_name => 'not yet implemented',
 
 383                               kivi_datev_name => 'not yet implemented',
 
 386                               kivi_datev_name => 'not yet implemented',
 
 389                               kivi_datev_name => 'not yet implemented',
 
 392                               kivi_datev_name => 'not yet implemented',
 
 395                               kivi_datev_name => 'not yet implemented',
 
 398                               kivi_datev_name => 'not yet implemented',
 
 401                               kivi_datev_name => 'not yet implemented',
 
 404                               kivi_datev_name => 'not yet implemented',
 
 407                               kivi_datev_name => 'not yet implemented',
 
 410                               kivi_datev_name => 'not yet implemented',
 
 413                               kivi_datev_name => 'not yet implemented',
 
 416                               kivi_datev_name => 'not yet implemented',
 
 419                               kivi_datev_name => 'not yet implemented',
 
 422                               kivi_datev_name => 'not yet implemented',
 
 425                               kivi_datev_name => 'not yet implemented',
 
 428                               kivi_datev_name => 'not yet implemented',
 
 431                               kivi_datev_name => 'not yet implemented',
 
 434                               kivi_datev_name => 'not yet implemented',
 
 437                               kivi_datev_name => 'not yet implemented',
 
 440                               kivi_datev_name => 'not yet implemented',
 
 443                               kivi_datev_name => 'not yet implemented',
 
 446                               kivi_datev_name => 'not yet implemented',
 
 449                               kivi_datev_name => 'not yet implemented',
 
 452                               kivi_datev_name => 'not yet implemented',
 
 455                               kivi_datev_name => 'not yet implemented',
 
 458                               kivi_datev_name => 'not yet implemented',
 
 461                               kivi_datev_name => 'not yet implemented',
 
 464                               kivi_datev_name => 'not yet implemented',
 
 467                               kivi_datev_name => 'not yet implemented',
 
 470                               kivi_datev_name => 'not yet implemented',
 
 473                               kivi_datev_name => 'locked',
 
 474                               csv_header_name => t8('Lock'),
 
 478                               valid_check     => sub { my ($check) = @_; return ($check =~ m/^(0|1)$/) },
 
 481                               kivi_datev_name => 'leistungsdatum',
 
 482                               csv_header_name => t8('Payment Date'),
 
 486                               input_check     => sub { my ($check) = @_; return  1 if ('' eq $check); return (ref (DateTime->from_kivitendo($check)) eq 'DateTime') },
 
 487                               formatter       => sub { my ($input) = @_; return '' if ('' eq $input); return DateTime->from_kivitendo($input)->strftime('%d%m%Y') },
 
 488                               valid_check     => sub { my ($check) = @_; return  1 if ('' eq $check); return ($check =~ m/^[0-9]{8}$/) },
 
 491                               kivi_datev_name => 'not yet implemented',
 
 494                               kivi_datev_name => 'not yet implemented',
 
 497                               kivi_datev_name => 'not yet implemented',
 
 500                               kivi_datev_name => 'not yet implemented',
 
 503                               kivi_datev_name => 'not yet implemented',
 
 511   croak(t8('We need a valid from date'))      unless (ref $data{from} eq 'DateTime');
 
 512   croak(t8('We need a valid to date'))        unless (ref $data{to}   eq 'DateTime');
 
 513   croak(t8('We need a array of datev_lines')) unless (ref $data{datev_lines} eq 'ARRAY');
 
 515   my $obj = bless {}, $class;
 
 516   $obj->$_($data{$_}) for keys %data;
 
 522   return undef unless $test;
 
 524     encode('Windows-1252', $test, Encode::FB_CROAK|Encode::LEAVE_SRC);
 
 536   # we can safely set these defaults
 
 537   # TODO get length_of_accounts from DATEV.pm
 
 538   my $today              = DateTime->now_local;
 
 539   my $created_on         = $today->ymd('') . $today->hms('') . '000';
 
 540   my $length_of_accounts = length(SL::DB::Manager::Chart->get_first(where => [charttype => 'A'])->accno) // 4;
 
 541   my $default_curr       = SL::DB::Default->get_default_currency;
 
 543   # datev metadata and the string length limits
 
 545   my %meta_datev_to_valid_length = (
 
 551   my $datev = SL::DB::Manager::Datev->get_first();
 
 553   while (my ($k, $v) = each %meta_datev_to_valid_length) {
 
 554     next unless $datev->{$k};
 
 555     $meta_datev{$k} = substr $datev->{$k}, 0, $v;
 
 559     "EXTF", "510", 21, "Buchungsstapel", 7, $created_on, "", "ki",
 
 560     "kivitendo-datev", "", $meta_datev{beraternr}, $meta_datev{mandantennr},
 
 561     $self->first_day_of_fiscal_year->ymd(''), $length_of_accounts,
 
 562     $self->from->ymd(''), $self->to->ymd(''), "", "", 1, "", $self->locked,
 
 563     $default_curr, "", "", "",""
 
 565   push @header, [ @header_row_1 ];
 
 567   # second header row, just the column names
 
 568   push @header, [ map { $_->{csv_header_name} } @kivitendo_to_datev ];
 
 576   my (@array_of_datev, @warnings);
 
 578   foreach my $row (@{ $self->datev_lines }) {
 
 579     my @current_datev_row;
 
 581     # 1. check all datev_lines and see if we have a defined value
 
 582     # 2. if we don't have a defined value set a default if exists
 
 584     foreach my $column (@kivitendo_to_datev) {
 
 585       if ($column->{kivi_datev_name} eq 'not yet implemented') {
 
 586         push @current_datev_row, '';
 
 589       my $data = $row->{$column->{kivi_datev_name}};
 
 590       if (!defined $data) {
 
 591         if (defined $column->{default}) {
 
 592           $data = $column->{default};
 
 594           die 'No sensible value or a sensible default found for the entry: ' . $column->{kivi_datev_name};
 
 597       # checkpoint a: no undefined data. All strict checks now!
 
 598       if (exists $column->{input_check} && !$column->{input_check}->($data)) {
 
 599         die t8("Wrong field value '#1' for field '#2' for the transaction with amount '#3'",
 
 600                 $data, $column->{kivi_datev_name}, $row->{umsatz});
 
 602       # checkpoint b: we can safely format the input
 
 603       if ($column->{formatter}) {
 
 604         $data = $column->{formatter}->($data);
 
 606       # checkpoint c: all soft checks now, will pop up as a user warning
 
 607       if (exists $column->{valid_check} && !$column->{valid_check}->($data)) {
 
 608         push @warnings, t8("Wrong field value '#1' for field '#2' for the transaction" .
 
 609                            " with amount '#3'", $data, $column->{kivi_datev_name}, $row->{umsatz});
 
 611       push @current_datev_row, $data;
 
 613     push @array_of_datev, \@current_datev_row;
 
 615   $self->warnings(\@warnings);
 
 616   return \@array_of_datev;
 
 622   $::form->format_amount({ numberformat => '1000,00' }, @_);
 
 625 sub first_day_of_fiscal_year {
 
 626   $_[0]->to->clone->truncate(to => 'year');
 
 637 SL::DATEV::CSV - kivitendo DATEV CSV Specification
 
 641   use SL::DATEV qw(:CONSTANTS);
 
 644   my $startdate = DateTime->new(year => 2014, month => 9, day => 1);
 
 645   my $enddate   = DateTime->new(year => 2014, month => 9, day => 31);
 
 646   my $datev = SL::DATEV->new(
 
 647     exporttype => DATEV_ET_BUCHUNGEN,
 
 648     format     => DATEV_FORMAT_CSV,
 
 652   $datev->generate_datev_data;
 
 654   my $datev_csv = SL::DATEV::CSV->new(datev_lines  => $datev->generate_datev_lines,
 
 655                                       from         => $datev->from,
 
 657                                       locked       => $datev->locked,
 
 659   $datev_csv->header;   # returns the required 2 rows of header ($aref = [ ["row1" ..], [ "row2" .. ] ]) as array of array
 
 660   $datev_csv->lines;    # returns an array_ref of rows of array_refs soll uns die ein Arrayref von Zeilen zurückgeben, die jeweils Arrayrefs sind
 
 661   $datev_csv->warnings; # returns warnings
 
 664   # The above object methods can be directly chained to a CSV export function, like this:
 
 665   my $csv_file = IO::File->new($somewhere_in_filesystem)') or die "Can't open: $!";
 
 666   $csv->print($csv_file, $_) for @{ $datev_csv->header };
 
 667   $csv->print($csv_file, $_) for @{ $datev_csv->lines  };
 
 669   $self->{warnings} = $datev_csv->warnings;
 
 676 The parsing of the DATEV CSV is index based, therefore the correct
 
 677 column must be present at the corresponding index, i.e.:
 
 679  Field Name   : Debit/Credit Label
 
 680  Valid Values : 'S' or 'H'
 
 683 The columns in C<@kivi_datev> are in the correct order and the
 
 684 specific attributes are defined as a key value hash list for each entry.
 
 686 The key names are the english translation according to the DATEV specs
 
 687 (Leitfaden DATEV englisch).
 
 689 The two attributes C<max_length> and C<type> are also set as specified
 
 692 To link the structure to kivitendo data, each entry has the attribute C<kivi_datev_name>
 
 693 which is by convention the key name as generated by DATEV->generate_datev_data.
 
 694 A value of C<'not yet implemented'> indicates that this field has no
 
 695 corresponding kivitendo data and will be given an empty value by DATEV->csv_buchungsexport.
 
 700 This is an excerpt of the DATEV Format 2015 Specification for CSV-Header
 
 705 The filename is subject to the following restrictions:
 
 706 1. The filename must begin with the prefix DTVF_ or EXTF_.
 
 707 2. The filename must end with .csv.
 
 709 When exporting from or importing into DATEV applications, the filename is
 
 710 marked with the prefix "DTVF_" (DATEV Format).
 
 711 The prefix "DTVF_" is reserved for DATEV applications.
 
 712 If you are using a third-party application to create a file in the DATEV format
 
 713 that you want to import using batch processing, use the prefix "EXTF_"
 
 716 =head2 File Structure
 
 718 The file structure of the text file exported/imported is defined as follows
 
 720 Line 1: Header (serves to assist in the interpretation of the following data)
 
 722 Line 2: Headline (headline of the user data)
 
 724 Line 3 – n: Records (user data)
 
 726 For an valid example file take a look at doc/DATEV-2015/EXTF_Buchungsstapel.csv
 
 729 =head2 Detailed Description
 
 731 Line 1 must contain 11 fields.
 
 733 Line 2 must contain 26 fields.
 
 735 Line 3 - n:  must contain 116 fields, a smaller subset is mandatory.
 
 743 Constructor for CSV-DATEV export.
 
 744 Checks mandantory params as described in section synopsis.
 
 748 Helper function, returns true if a string is not empty and cp1252 encoded
 
 749 For example some arabic utf-8 like  ݐ  will return false
 
 753 Mostly all other header information are constants or metadata loaded
 
 754 from SL::DB::Datev.pm.
 
 756 Returns the first two entries for the header (see above: File Structure)
 
 759 =item kivitendo_to_datev
 
 761 Returns the data structure C<@datev_data> as an array
 
 765 Lightweight wrapper for form->format_amount.
 
 766 Expects a number in kivitendo database format and returns the same number
 
 769 =item first_day_of_fiscal_year
 
 771 Takes a look at $self->to to  determine the first day of the fiscal year.
 
 775 Generates the CSV-Format data for the CSV DATEV export and returns
 
 776 an 2-dimensional array as an array_ref.
 
 777 May additionally return a second array_ref with warnings.
 
 779 Requires the same date fields as the constructor for a valid DATEV header.
 
 781 Furthermore we assume that the first day of the fiscal year is
 
 782 the first of January and we cannot guarantee that our data in kivitendo
 
 783 is locked, that means a booking cannot be modified after a defined (vat tax)
 
 785 Some validity checks (max_length and regex) will be done if the
 
 786 data structure contains them and the field is defined.
 
 788 To add or alter the structure of the data take a look at the C<@kivitendo_to_datev> structure.
 
 794 One can circumevent the check of the warnings.quite easily,
 
 795 becaus warnings are generated after the call to lines:
 
 798   die if @{ $datev_csv->warnings };
 
 799   somethin_with($datev_csv->lines);
 
 802   my $lines = $datev_csv->lines;
 
 803   die if @{ $datev_csv->warnings };
 
 804   somethin_with($lines);