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 => 'document_guid',
157 csv_header_name => t8('Link to invoice'),
158 max_length => 210, # DMS Application shortcut and GUID
160 # "8DB85C02-4CC3-FF3E-06D7-7F87EEECCF3A".
163 input_check => sub { my ($check) = @_; return 1 unless $check;
164 my @guids = split(/,/,$check);
165 foreach my $guid (@guids) {
166 return unless ($guid =~ m/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/);
169 formatter => sub { my ($input) = @_; return '' unless $input;
170 my @guids = split (/,/,$input);
171 my $first = shift @guids;
172 my $bedi = 'BEDI "' . $first . '"';
173 foreach my $guid (@guids) {
174 $bedi .= ',"' . $guid . '"';
180 kivi_datev_name => 'not yet implemented',
183 kivi_datev_name => 'not yet implemented',
186 kivi_datev_name => 'not yet implemented',
189 kivi_datev_name => 'not yet implemented',
192 kivi_datev_name => 'not yet implemented',
195 kivi_datev_name => 'not yet implemented',
198 kivi_datev_name => 'not yet implemented',
201 kivi_datev_name => 'not yet implemented',
204 kivi_datev_name => 'not yet implemented',
207 kivi_datev_name => 'not yet implemented',
210 kivi_datev_name => 'not yet implemented',
213 kivi_datev_name => 'not yet implemented',
216 kivi_datev_name => 'not yet implemented',
219 kivi_datev_name => 'not yet implemented',
222 kivi_datev_name => 'not yet implemented',
225 kivi_datev_name => 'not yet implemented',
228 kivi_datev_name => 'kost1',
229 csv_header_name => t8('Cost Center'),
233 input_check => sub { my ($text) = @_; return 1 unless $text; check_encoding($text); },
234 formatter => sub { my ($input) = @_; return substr($input, 0, 8) },
237 kivi_datev_name => 'kost2',
238 csv_header_name => t8('Cost Center'),
242 input_check => sub { my ($text) = @_; return 1 unless $text; check_encoding($text); },
243 formatter => sub { my ($input) = @_; return substr($input, 0, 8) },
246 kivi_datev_name => 'not yet implemented',
247 csv_header_name => t8('KOST Quantity'),
250 valid_check => sub { my ($check) = @_; return ($check =~ m/^[0-9]{0,9}$/) },
253 kivi_datev_name => 'ustid',
254 csv_header_name => t8('EU Member State and VAT ID Number'),
260 return 1 if ('' eq $ustid);
261 return SL::VATIDNr->validate($ustid);
263 formatter => sub { my ($input) = @_; $input =~ s/\s//g; return $input },
266 return 1 if ('' eq $ustid);
267 return SL::VATIDNr->validate($ustid);
271 kivi_datev_name => 'not yet implemented',
274 kivi_datev_name => 'not yet implemented',
277 kivi_datev_name => 'not yet implemented',
280 kivi_datev_name => 'not yet implemented',
283 kivi_datev_name => 'not yet implemented',
286 kivi_datev_name => 'not yet implemented',
289 kivi_datev_name => 'not yet implemented',
292 kivi_datev_name => 'not yet implemented',
295 kivi_datev_name => 'not yet implemented',
298 kivi_datev_name => 'not yet implemented',
301 kivi_datev_name => 'not yet implemented',
304 kivi_datev_name => 'not yet implemented',
307 kivi_datev_name => 'not yet implemented',
310 kivi_datev_name => 'not yet implemented',
313 kivi_datev_name => 'not yet implemented',
316 kivi_datev_name => 'not yet implemented',
319 kivi_datev_name => 'not yet implemented',
322 kivi_datev_name => 'not yet implemented',
325 kivi_datev_name => 'not yet implemented',
328 kivi_datev_name => 'not yet implemented',
331 kivi_datev_name => 'not yet implemented',
334 kivi_datev_name => 'not yet implemented',
337 kivi_datev_name => 'not yet implemented',
340 kivi_datev_name => 'not yet implemented',
343 kivi_datev_name => 'not yet implemented',
346 kivi_datev_name => 'not yet implemented',
349 kivi_datev_name => 'not yet implemented',
352 kivi_datev_name => 'not yet implemented',
355 kivi_datev_name => 'not yet implemented',
358 kivi_datev_name => 'not yet implemented',
361 kivi_datev_name => 'not yet implemented',
364 kivi_datev_name => 'not yet implemented',
367 kivi_datev_name => 'not yet implemented',
370 kivi_datev_name => 'not yet implemented',
373 kivi_datev_name => 'not yet implemented',
376 kivi_datev_name => 'not yet implemented',
379 kivi_datev_name => 'not yet implemented',
382 kivi_datev_name => 'not yet implemented',
385 kivi_datev_name => 'not yet implemented',
388 kivi_datev_name => 'not yet implemented',
391 kivi_datev_name => 'not yet implemented',
394 kivi_datev_name => 'not yet implemented',
397 kivi_datev_name => 'not yet implemented',
400 kivi_datev_name => 'not yet implemented',
403 kivi_datev_name => 'not yet implemented',
406 kivi_datev_name => 'not yet implemented',
409 kivi_datev_name => 'not yet implemented',
412 kivi_datev_name => 'not yet implemented',
415 kivi_datev_name => 'not yet implemented',
418 kivi_datev_name => 'not yet implemented',
421 kivi_datev_name => 'not yet implemented',
424 kivi_datev_name => 'not yet implemented',
427 kivi_datev_name => 'not yet implemented',
430 kivi_datev_name => 'not yet implemented',
433 kivi_datev_name => 'not yet implemented',
436 kivi_datev_name => 'not yet implemented',
439 kivi_datev_name => 'not yet implemented',
442 kivi_datev_name => 'not yet implemented',
445 kivi_datev_name => 'not yet implemented',
448 kivi_datev_name => 'not yet implemented',
451 kivi_datev_name => 'not yet implemented',
454 kivi_datev_name => 'not yet implemented',
457 kivi_datev_name => 'not yet implemented',
460 kivi_datev_name => 'not yet implemented',
463 kivi_datev_name => 'not yet implemented',
466 kivi_datev_name => 'not yet implemented',
469 kivi_datev_name => 'not yet implemented',
472 kivi_datev_name => 'not yet implemented',
475 kivi_datev_name => 'not yet implemented',
478 kivi_datev_name => 'not yet implemented',
481 kivi_datev_name => 'not yet implemented',
484 kivi_datev_name => 'not yet implemented',
487 kivi_datev_name => 'not yet implemented',
490 kivi_datev_name => 'locked',
491 csv_header_name => t8('Lock'),
495 valid_check => sub { my ($check) = @_; return ($check =~ m/^(0|1)$/) },
498 kivi_datev_name => 'leistungsdatum',
499 csv_header_name => t8('Payment Date'),
503 input_check => sub { my ($check) = @_; return 1 if ('' eq $check); return (ref (DateTime->from_kivitendo($check)) eq 'DateTime') },
504 formatter => sub { my ($input) = @_; return '' if ('' eq $input); return DateTime->from_kivitendo($input)->strftime('%d%m%Y') },
505 valid_check => sub { my ($check) = @_; return 1 if ('' eq $check); return ($check =~ m/^[0-9]{8}$/) },
508 kivi_datev_name => 'not yet implemented',
510 # DATEV Prüfprogramm says: Only 116 fields are allowed
512 # kivi_datev_name => 'not yet implemented',
515 # kivi_datev_name => 'not yet implemented',
518 # kivi_datev_name => 'not yet implemented',
521 # kivi_datev_name => 'not yet implemented',
529 croak(t8('We need a valid from date')) unless (ref $data{from} eq 'DateTime');
530 croak(t8('We need a valid to date')) unless (ref $data{to} eq 'DateTime');
531 croak(t8('We need a array of datev_lines')) unless (ref $data{datev_lines} eq 'ARRAY');
533 my $obj = bless {}, $class;
534 $obj->$_($data{$_}) for keys %data;
540 return undef unless $test;
542 encode('Windows-1252', $test, Encode::FB_CROAK|Encode::LEAVE_SRC);
554 # we can safely set these defaults
555 # TODO get length_of_accounts from DATEV.pm
556 my $today = DateTime->now_local;
557 my $created_on = $today->ymd('') . $today->hms('') . '000';
558 my $length_of_accounts = length(SL::DB::Manager::Chart->get_first(where => [charttype => 'A'])->accno) // 4;
559 my $default_curr = SL::DB::Default->get_default_currency;
561 # datev metadata and the string length limits
563 my %meta_datev_to_valid_length = (
569 my $datev = SL::DB::Manager::Datev->get_first();
571 while (my ($k, $v) = each %meta_datev_to_valid_length) {
572 next unless $datev->{$k};
573 $meta_datev{$k} = substr $datev->{$k}, 0, $v;
575 my $coa = $::instance_conf->get_coa eq 'Germany-DATEV-SKR03EU' ? '03'
576 : $::instance_conf->get_coa eq 'Germany-DATEV-SKR04EU' ? '04'
580 "EXTF", "510", 21, "Buchungsstapel", 7, $created_on, "", "ki",
581 "kivitendo-datev", "", $meta_datev{beraternr}, $meta_datev{mandantennr},
582 $self->first_day_of_fiscal_year->ymd(''), $length_of_accounts,
583 $self->from->ymd(''), $self->to->ymd(''), "", "", 1, "", $self->locked,
584 $default_curr, "", "", "","", $coa, "", "", "", ""
586 push @header, [ @header_row_1 ];
588 # second header row, just the column names
589 push @header, [ map { $_->{csv_header_name} } @kivitendo_to_datev ];
597 my (@array_of_datev, @warnings);
599 foreach my $row (@{ $self->datev_lines }) {
600 my @current_datev_row;
602 # 1. check all datev_lines and see if we have a defined value
603 # 2. if we don't have a defined value set a default if exists
605 foreach my $column (@kivitendo_to_datev) {
606 if ($column->{kivi_datev_name} eq 'not yet implemented') {
607 push @current_datev_row, '';
610 my $data = $row->{$column->{kivi_datev_name}};
611 if (!defined $data) {
612 if (defined $column->{default}) {
613 $data = $column->{default};
615 die 'No sensible value or a sensible default found for the entry: ' . $column->{kivi_datev_name};
618 # checkpoint a: no undefined data. All strict checks now!
619 if (exists $column->{input_check} && !$column->{input_check}->($data)) {
620 die t8("Wrong field value '#1' for field '#2' for the transaction with amount '#3'",
621 $data, $column->{kivi_datev_name}, $row->{umsatz});
623 # checkpoint b: we can safely format the input
624 if ($column->{formatter}) {
625 $data = $column->{formatter}->($data);
627 # checkpoint c: all soft checks now, will pop up as a user warning
628 if (exists $column->{valid_check} && !$column->{valid_check}->($data)) {
629 push @warnings, t8("Wrong field value '#1' for field '#2' for the transaction" .
630 " with amount '#3'", $data, $column->{kivi_datev_name}, $row->{umsatz});
632 push @current_datev_row, $data;
634 push @array_of_datev, \@current_datev_row;
636 $self->warnings(\@warnings);
637 return \@array_of_datev;
643 $::form->format_amount({ numberformat => '1000,00' }, @_);
646 sub first_day_of_fiscal_year {
647 $_[0]->to->clone->truncate(to => 'year');
658 SL::DATEV::CSV - kivitendo DATEV CSV Specification
662 use SL::DATEV qw(:CONSTANTS);
665 my $startdate = DateTime->new(year => 2014, month => 9, day => 1);
666 my $enddate = DateTime->new(year => 2014, month => 9, day => 31);
667 my $datev = SL::DATEV->new(
668 exporttype => DATEV_ET_BUCHUNGEN,
669 format => DATEV_FORMAT_CSV,
673 $datev->generate_datev_data;
675 my $datev_csv = SL::DATEV::CSV->new(datev_lines => $datev->generate_datev_lines,
676 from => $datev->from,
678 locked => $datev->locked,
680 $datev_csv->header; # returns the required 2 rows of header ($aref = [ ["row1" ..], [ "row2" .. ] ]) as array of array
681 $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
682 $datev_csv->warnings; # returns warnings
685 # The above object methods can be directly chained to a CSV export function, like this:
686 my $csv_file = IO::File->new($somewhere_in_filesystem)') or die "Can't open: $!";
687 $csv->print($csv_file, $_) for @{ $datev_csv->header };
688 $csv->print($csv_file, $_) for @{ $datev_csv->lines };
690 $self->{warnings} = $datev_csv->warnings;
697 The parsing of the DATEV CSV is index based, therefore the correct
698 column must be present at the corresponding index, i.e.:
700 Field Name : Debit/Credit Label
701 Valid Values : 'S' or 'H'
704 The columns in C<@kivi_datev> are in the correct order and the
705 specific attributes are defined as a key value hash list for each entry.
707 The key names are the english translation according to the DATEV specs
708 (Leitfaden DATEV englisch).
710 The two attributes C<max_length> and C<type> are also set as specified
713 To link the structure to kivitendo data, each entry has the attribute C<kivi_datev_name>
714 which is by convention the key name as generated by DATEV->generate_datev_data.
715 A value of C<'not yet implemented'> indicates that this field has no
716 corresponding kivitendo data and will be given an empty value by DATEV->csv_buchungsexport.
721 This is an excerpt of the DATEV Format 2015 Specification for CSV-Header
726 The filename is subject to the following restrictions:
727 1. The filename must begin with the prefix DTVF_ or EXTF_.
728 2. The filename must end with .csv.
730 When exporting from or importing into DATEV applications, the filename is
731 marked with the prefix "DTVF_" (DATEV Format).
732 The prefix "DTVF_" is reserved for DATEV applications.
733 If you are using a third-party application to create a file in the DATEV format
734 that you want to import using batch processing, use the prefix "EXTF_"
737 =head2 File Structure
739 The file structure of the text file exported/imported is defined as follows
741 Line 1: Header (serves to assist in the interpretation of the following data)
743 Line 2: Headline (headline of the user data)
745 Line 3 – n: Records (user data)
747 For an valid example file take a look at doc/DATEV-2015/EXTF_Buchungsstapel.csv
750 =head2 Detailed Description
752 Line 1 must contain 11 fields.
754 Line 2 must contain 26 fields.
756 Line 3 - n: must contain 116 fields, a smaller subset is mandatory.
764 Constructor for CSV-DATEV export.
765 Checks mandantory params as described in section synopsis.
769 Helper function, returns true if a string is not empty and cp1252 encoded
770 For example some arabic utf-8 like ݐ will return false
774 Mostly all other header information are constants or metadata loaded
775 from SL::DB::Datev.pm.
777 Returns the first two entries for the header (see above: File Structure)
780 =item kivitendo_to_datev
782 Returns the data structure C<@datev_data> as an array
786 Lightweight wrapper for form->format_amount.
787 Expects a number in kivitendo database format and returns the same number
790 =item first_day_of_fiscal_year
792 Takes a look at $self->to to determine the first day of the fiscal year.
796 Generates the CSV-Format data for the CSV DATEV export and returns
797 an 2-dimensional array as an array_ref.
798 May additionally return a second array_ref with warnings.
800 Requires the same date fields as the constructor for a valid DATEV header.
802 Furthermore we assume that the first day of the fiscal year is
803 the first of January and we cannot guarantee that our data in kivitendo
804 is locked, that means a booking cannot be modified after a defined (vat tax)
806 Some validity checks (max_length and regex) will be done if the
807 data structure contains them and the field is defined.
809 To add or alter the structure of the data take a look at the C<@kivitendo_to_datev> structure.
815 One can circumevent the check of the warnings.quite easily,
816 becaus warnings are generated after the call to lines:
819 die if @{ $datev_csv->warnings };
820 somethin_with($datev_csv->lines);
823 my $lines = $datev_csv->lines;
824 die if @{ $datev_csv->warnings };
825 somethin_with($lines);