Revision 4076 hat bei den Funktionen quote und unquote dafür gesorgt, dass nur "1...
[kivitendo-erp.git] / SL / DATEV.pm
1 #=====================================================================
2 # Lx-Office ERP
3 # Copyright (c) 2004
4 #
5 #  Author: Philip Reetz
6 #   Email: p.reetz@linet-services.de
7 #     Web: http://www.lx-office.org
8 #
9 #
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
14 #
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #======================================================================
23 #
24 # Datev export module
25 #======================================================================
26
27 package DATEV;
28
29 use SL::DBUtils;
30 use SL::DATEV::KNEFile;
31
32 use Data::Dumper;
33 use File::Path;
34 use Time::HiRes qw(gettimeofday);
35
36 sub _get_export_path {
37   $main::lxdebug->enter_sub();
38
39   my ($a, $b) = gettimeofday();
40   my $path    = get_path_for_download_token("${a}-${b}-${$}");
41
42   mkpath($path) unless (-d $path);
43
44   $main::lxdebug->leave_sub();
45
46   return $path;
47 }
48
49 sub get_path_for_download_token {
50   $main::lxdebug->enter_sub();
51
52   my $token = shift;
53   my $path;
54
55   if ($token =~ m|^(\d+)-(\d+)-(\d+)$|) {
56     $path = "${main::userspath}/datev-export-${1}-${2}-${3}";
57   }
58
59   $main::lxdebug->leave_sub();
60
61   return $path;
62 }
63
64 sub get_download_token_for_path {
65   $main::lxdebug->enter_sub();
66
67   my $path = shift;
68   my $token;
69
70   if ($path =~ m|.*datev-export-(\d+)-(\d+)-(\d+)/?$|) {
71     $token = "${1}-${2}-${3}";
72   }
73
74   $main::lxdebug->leave_sub();
75
76   return $token;
77 }
78
79 sub clean_temporary_directories {
80   $main::lxdebug->enter_sub();
81
82   foreach my $path (glob "${main::userspath}/datev-export-*") {
83     next unless (-d $path);
84
85     my $mtime = (stat($path))[9];
86     next if ((time() - $mtime) < 8 * 60 * 60);
87
88     rmtree $path;
89   }
90
91   $main::lxdebug->leave_sub();
92 }
93
94 sub _fill {
95   $main::lxdebug->enter_sub();
96
97   my $text      = shift;
98   my $field_len = shift;
99   my $fill_char = shift;
100   my $alignment = shift || 'right';
101
102   my $text_len  = length $text;
103
104   if ($field_len < $text_len) {
105     $text = substr $text, 0, $field_len;
106
107   } elsif ($field_len > $text_len) {
108     my $filler = ($fill_char) x ($field_len - $text_len);
109     $text      = $alignment eq 'right' ? $filler . $text : $text . $filler;
110   }
111
112   $main::lxdebug->leave_sub();
113
114   return $text;
115 }
116
117 sub get_datev_stamm {
118   $main::lxdebug->enter_sub();
119
120   my ($self, $myconfig, $form) = @_;
121
122   # connect to database
123   my $dbh = $form->dbconnect($myconfig);
124
125   $query = qq|SELECT * FROM datev|;
126   $sth   = $dbh->prepare($query);
127   $sth->execute || $form->dberror($query);
128
129   my $ref = $sth->fetchrow_hashref(NAME_lc);
130
131   map { $form->{$_} = $ref->{$_} } keys %$ref;
132
133   $sth->finish;
134   $dbh->disconnect;
135   $main::lxdebug->leave_sub();
136 }
137
138 sub save_datev_stamm {
139   $main::lxdebug->enter_sub();
140
141   my ($self, $myconfig, $form) = @_;
142
143   # connect to database
144   my $dbh = $form->dbconnect_noauto($myconfig);
145
146   $query = qq|DELETE FROM datev|;
147   $dbh->do($query) || $form->dberror($query);
148
149   $query = qq|INSERT INTO datev
150               (beraternr, beratername, dfvkz, mandantennr, datentraegernr, abrechnungsnr) VALUES
151               (|
152     . $dbh->quote($form->{beraternr}) . qq|,|
153     . $dbh->quote($form->{beratername}) . qq|,|
154     . $dbh->quote($form->{dfvkz}) . qq|,
155               |
156     . $dbh->quote($form->{mandantennr}) . qq|,|
157     . $dbh->quote($form->{datentraegernr}) . qq|,|
158     . $dbh->quote($form->{abrechnungsnr}) . qq|)|;
159   $sth = $dbh->prepare($query);
160   $sth->execute || $form->dberror($query);
161   $sth->finish;
162
163   $dbh->commit;
164   $dbh->disconnect;
165   $main::lxdebug->leave_sub();
166 }
167
168 sub kne_export {
169   $main::lxdebug->enter_sub();
170
171   my ($self, $myconfig, $form) = @_;
172   my $result;
173
174   if ($form->{exporttype} == 0) {
175     $result = kne_buchungsexport($myconfig, $form);
176   } else {
177     $result = kne_stammdatenexport($myconfig, $form);
178   }
179
180   $main::lxdebug->leave_sub();
181
182   return $result;
183 }
184
185 sub obe_export {
186   $main::lxdebug->enter_sub();
187
188   my ($self, $myconfig, $form) = @_;
189
190   # connect to database
191   my $dbh = $form->dbconnect_noauto($myconfig);
192   $dbh->commit;
193   $dbh->disconnect;
194   $main::lxdebug->leave_sub();
195 }
196
197 sub get_dates {
198   $main::lxdebug->enter_sub();
199
200   my ($zeitraum, $monat, $quartal, $transdatefrom, $transdateto) = @_;
201
202   $fromto = "transdate >= ";
203
204   my @a = localtime;
205   $a[5] += 1900;
206   $jahr = $a[5];
207   if ($zeitraum eq "monat") {
208   SWITCH: {
209       $monat eq "1" && do {
210         $form->{fromdate} = "1.1.$jahr";
211         $form->{todate}   = "31.1.$jahr";
212         last SWITCH;
213       };
214       $monat eq "2" && do {
215         $form->{fromdate} = "1.2.$jahr";
216
217         #this works from 1901 to 2099, 1900 and 2100 fail.
218         $leap = ($jahr % 4 == 0) ? "29" : "28";
219         $form->{todate} = "$leap.2.$jahr";
220         last SWITCH;
221       };
222       $monat eq "3" && do {
223         $form->{fromdate} = "1.3.$jahr";
224         $form->{todate}   = "31.3.$jahr";
225         last SWITCH;
226       };
227       $monat eq "4" && do {
228         $form->{fromdate} = "1.4.$jahr";
229         $form->{todate}   = "30.4.$jahr";
230         last SWITCH;
231       };
232       $monat eq "5" && do {
233         $form->{fromdate} = "1.5.$jahr";
234         $form->{todate}   = "31.5.$jahr";
235         last SWITCH;
236       };
237       $monat eq "6" && do {
238         $form->{fromdate} = "1.6.$jahr";
239         $form->{todate}   = "30.6.$jahr";
240         last SWITCH;
241       };
242       $monat eq "7" && do {
243         $form->{fromdate} = "1.7.$jahr";
244         $form->{todate}   = "31.7.$jahr";
245         last SWITCH;
246       };
247       $monat eq "8" && do {
248         $form->{fromdate} = "1.8.$jahr";
249         $form->{todate}   = "31.8.$jahr";
250         last SWITCH;
251       };
252       $monat eq "9" && do {
253         $form->{fromdate} = "1.9.$jahr";
254         $form->{todate}   = "30.9.$jahr";
255         last SWITCH;
256       };
257       $monat eq "10" && do {
258         $form->{fromdate} = "1.10.$jahr";
259         $form->{todate}   = "31.10.$jahr";
260         last SWITCH;
261       };
262       $monat eq "11" && do {
263         $form->{fromdate} = "1.11.$jahr";
264         $form->{todate}   = "30.11.$jahr";
265         last SWITCH;
266       };
267       $monat eq "12" && do {
268         $form->{fromdate} = "1.12.$jahr";
269         $form->{todate}   = "31.12.$jahr";
270         last SWITCH;
271       };
272     }
273     $fromto .=
274       "'" . $form->{fromdate} . "' and transdate <= '" . $form->{todate} . "'";
275   }
276
277   elsif ($zeitraum eq "quartal") {
278     if ($quartal == 1) {
279       $fromto .=
280         "'01.01." . $jahr . "' and transdate <= '31.03." . $jahr . "'";
281     } elsif ($quartal == 2) {
282       $fromto .=
283         "'01.04." . $jahr . "' and transdate <= '30.06." . $jahr . "'";
284     } elsif ($quartal == 3) {
285       $fromto .=
286         "'01.07." . $jahr . "' and transdate <= '30.09." . $jahr . "'";
287     } elsif ($quartal == 4) {
288       $fromto .=
289         "'01.10." . $jahr . "' and transdate <= '31.12." . $jahr . "'";
290     }
291   }
292
293   elsif ($zeitraum eq "zeit") {
294     $fromto            .= "'" . $transdatefrom . "' and transdate <= '" . $transdateto . "'";
295     my ($yy, $mm, $dd)  = $main::locale->parse_date(\%main::myconfig, $transdatefrom);
296     $jahr               = $yy;
297   }
298
299   $main::lxdebug->leave_sub();
300
301   return ($fromto, $jahr);
302 }
303
304 sub _get_transactions {
305   $main::lxdebug->enter_sub();
306
307   my $fromto   =  shift;
308
309   my $myconfig =  \%main::myconfig;
310   my $form     =  $main::form;
311
312   my $dbh      =  $form->get_standard_dbh($myconfig);
313
314   my @errors   = ();
315
316   $fromto      =~ s/transdate/ac\.transdate/g;
317
318   my %taxes    =  selectall_as_map($form, $dbh, qq|SELECT id, rate FROM tax|, 'id', 'rate');
319
320   my $query    =
321     qq|SELECT ac.oid, ac.transdate, ac.trans_id,ar.id, ac.amount, ac.taxkey,
322          ar.invnumber, ar.duedate, ar.amount as umsatz,
323          ct.name,
324          c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id,
325          t.chart_id, t.rate, t.id AS taxid, t.taxkey AS taxtaxkey
326        FROM acc_trans ac,ar ar, customer ct, chart c
327        LEFT JOIN tax t ON (t.chart_id = c.id)
328        WHERE $fromto
329          AND (ac.trans_id = ar.id)
330          AND (ac.trans_id = ar.id)
331          AND (ar.customer_id = ct.id)
332          AND (ac.chart_id = c.id)
333
334        UNION ALL
335
336        SELECT ac.oid, ac.transdate, ac.trans_id,ap.id, ac.amount, ac.taxkey,
337          ap.invnumber, ap.duedate, ap.amount as umsatz,
338          ct.name,
339          c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id,
340          t.chart_id, t.rate, t.id AS taxid, t.taxkey AS taxtaxkey
341        FROM acc_trans ac, ap ap, vendor ct, chart c
342        LEFT JOIN tax t ON (t.chart_id = c.id)
343        WHERE $fromto
344          AND (ac.trans_id = ap.id)
345          AND (ap.vendor_id = ct.id)
346          AND (ac.chart_id = c.id)
347
348        UNION ALL
349
350        SELECT ac.oid, ac.transdate, ac.trans_id,gl.id, ac.amount, ac.taxkey,
351          gl.reference AS invnumber, gl.transdate AS duedate, ac.amount as umsatz,
352          gl.description AS name,
353          c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id,
354          t.chart_id, t.rate, t.id AS taxid, t.taxkey AS taxtaxkey
355        FROM acc_trans ac, gl gl, chart c
356        LEFT JOIN tax t ON (t.chart_id = c.id)
357        WHERE $fromto
358          AND (ac.trans_id = gl.id)
359          AND (ac.chart_id = c.id)
360
361        ORDER BY trans_id, oid|;
362
363   my $sth     = prepare_execute_query($form, $dbh, $query);
364
365   my @splits;
366   my $counter = 0;
367   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
368     $counter++;
369     if (($counter % 500) == 0) {
370       print("$counter ");
371     }
372
373     my $trans    = [ $ref ];
374
375     my $count    = $ref->{amount};
376     my $firstrun = 1;
377
378     while (abs($count) > 0.01 || $firstrun) {
379       my $ref2 = $sth->fetchrow_hashref(NAME_lc);
380       last unless ($ref2);
381
382       push @{ $trans }, $ref2;
383
384       $count    += $ref2->{amount};
385       $firstrun  = 0;
386     }
387
388     my %taxid_taxkeys = ();
389     my $absumsatz     = 0;
390     if (scalar(@{$trans}) <= 2) {
391       push @{ $form->{DATEV} }, $trans;
392       next;
393     }
394
395     for my $j (0 .. (scalar(@{$trans}) - 1)) {
396       if (abs($trans->[$j]->{'amount'}) > abs($absumsatz)) {
397         $absumsatz     = $trans->[$j]->{'amount'};
398         $notsplitindex = $j;
399       }
400       if (($trans->[$j]->{'taxtaxkey'}) && ($trans->[$j]->{'taxid'})) {
401         $taxid_taxkeys{$trans->[$j]->{'taxtaxkey'}}     = $trans->[$j]->{'taxid'};
402       }
403     }
404
405     my $ml = ($trans->[0]->{'umsatz'} > 0) ? 1 : -1;
406     for my $j (0 .. (scalar(@{$trans}) - 1)) {
407       if (   ($j != $notsplitindex)
408           && ($trans->[$j]->{'chart_id'}  eq "")
409           && (   $trans->[$j]->{'taxkey'} eq ""
410               || $trans->[$j]->{'taxkey'} eq "0"
411               || $trans->[$j]->{'taxkey'} eq "1"
412               || $trans->[$j]->{'taxkey'} eq "10"
413               || $trans->[$j]->{'taxkey'} eq "11")) {
414         my %new_trans = ();
415         map { $new_trans{$_} = $trans->[$notsplitindex]->{$_}; } keys %{ $trans->[$notsplitindex] };
416
417         $absumsatz               += $trans->[$j]->{'amount'};
418         $new_trans{'amount'}      = $trans->[$j]->{'amount'} * (-1);
419         $new_trans{'umsatz'}      = abs($trans->[$j]->{'amount'}) * $ml;
420         $trans->[$j]->{'umsatz'}  = abs($trans->[$j]->{'amount'}) * $ml;
421
422         push @splits, [ \%new_trans, $trans->[$j] ];
423         push @{ $form->{DATEV} }, $splits[-1];
424
425       } elsif (($j != $notsplitindex) && ($trans->[$j]->{'chart_id'} eq "")) {
426         $absumsatz += ($trans->[$j]->{'amount'} * (1 + $taxes{ $taxid_taxkeys{$trans->[$j]->{'taxkey'}} }));
427
428         my %new_trans = ();
429         map { $new_trans{$_} = $trans->[$notsplitindex]->{$_}; } keys %{ $trans->[$notsplitindex] };
430
431         my $tax_rate             = 1 + $taxes{ $taxid_taxkeys{$trans->[$j]->{'taxkey'}} };
432         $new_trans{'amount'}     = $form->round_amount(($trans->[$j]->{'amount'} * $tax_rate * -1), 2);
433         $new_trans{'umsatz'}     = abs($form->round_amount(($trans->[$j]->{'amount'} * $tax_rate), 2)) * $ml;
434         $trans->[$j]->{'umsatz'} = abs($form->round_amount(($trans->[$j]->{'amount'} * $tax_rate), 2)) * $ml;
435
436         push @splits, [ \%new_trans, $trans->[$j] ];
437         push @{ $form->{DATEV} }, $splits[-1];
438       }
439     }
440
441     if (abs($absumsatz) > 0.01) {
442       push @errors, "Datev-Export fehlgeschlagen! Bei Transaktion $trans->[0]->{trans_id} ($absumsatz)\n";
443     }
444   }
445
446   $sth->finish();
447
448   $form->error(join("<br>\n", @errors)) if (@errors);
449
450   $main::lxdebug->leave_sub();
451 }
452
453 sub make_kne_data_header {
454   $main::lxdebug->enter_sub();
455
456   my ($myconfig, $form, $fromto, $start_jahr) = @_;
457
458   my $jahr = $start_jahr;
459   if (!$jahr) {
460     my @a = localtime;
461     $jahr = $a[5];
462   }
463
464   #Header
465   my $header  = "\x1D\x181";
466   $header    .= _fill($form->{datentraegernr}, 3, '0');
467   $header    .= ($fromto) ? "11" : "13"; # Anwendungsnummer
468   $header    .= _fill($form->{dfvkz}, 2, '0');
469   $header    .= _fill($form->{beraternr}, 7, '0');
470   $header    .= _fill($form->{mandantennr}, 5, '0');
471   $header    .= _fill($form->{abrechnungsnr} . $jahr, 6, '0');
472
473   $fromto         =~ s/transdate|>=|and|\'|<=//g;
474   my ($from, $to) =  split /   /, $fromto;
475   $from           =~ s/ //g;
476   $to             =~ s/ //g;
477
478   if ($from ne "") {
479     my ($fday, $fmonth, $fyear) = split(/\./, $from);
480     if (length($fmonth) < 2) {
481       $fmonth = "0" . $fmonth;
482     }
483     if (length($fday) < 2) {
484       $fday = "0" . $fday;
485     }
486     $from = $fday . $fmonth . substr($fyear, -2, 2);
487   } else {
488     $from = "";
489   }
490
491   $header .= $from;
492
493   if ($to ne "") {
494     my ($tday, $tmonth, $tyear) = split(/\./, $to);
495     if (length($tmonth) < 2) {
496       $tmonth = "0" . $tmonth;
497     }
498     if (length($tday) < 2) {
499       $tday = "0" . $tday;
500     }
501     $to = $tday . $tmonth . substr($tyear, -2, 2);
502   } else {
503     $to = "";
504   }
505   $header .= $to;
506
507   if ($fromto ne "") {
508     $primanota = "001";
509     $header .= $primanota;
510   }
511
512   $header .= _fill($form->{passwort}, 4, '0');
513   $header .= " " x 16;       # Anwendungsinfo
514   $header .= " " x 16;       # Inputinfo
515   $header .= "\x79";
516
517   #Versionssatz
518   my $versionssatz  = $form->{exporttype} == 0 ? "\xB5" . "1," : "\xB6" . "1,";
519
520   my $dbh           = $form->get_standard_dbh($myconfig);
521   my $query         = qq|SELECT accno FROM chart LIMIT 1|;
522   my $ref           = selectfirst_hashref_query($form, $dbh, $query);
523
524   $versionssatz    .= length $ref->{accno};
525   $versionssatz    .= ",";
526   $versionssatz    .= length $ref->{accno};
527   $versionssatz    .= ",SELF" . "\x1C\x79";
528
529   $header          .= $versionssatz;
530
531   $main::lxdebug->leave_sub();
532
533   return $header;
534 }
535
536 sub datetofour {
537   $main::lxdebug->enter_sub();
538
539   my ($date, $six) = @_;
540
541   ($day, $month, $year) = split(/\./, $date);
542
543   if ($day =~ /^0/) {
544     $day = substr($day, 1, 1);
545   }
546   if (length($month) < 2) {
547     $month = "0" . $month;
548   }
549   if (length($year) > 2) {
550     $year = substr($year, -2, 2);
551   }
552
553   if ($six) {
554     $date = $day . $month . $year;
555   } else {
556     $date = $day . $month;
557   }
558
559   $main::lxdebug->leave_sub();
560
561   return $date;
562 }
563
564 sub trim_leading_zeroes {
565   my $str = shift;
566
567   $str =~ s/^0+//g;
568
569   return $str;
570 }
571
572 sub make_ed_versionset {
573   $main::lxdebug->enter_sub();
574
575   my ($header, $filename, $blockcount, $fromto) = @_;
576
577   my $versionset  = "V" . substr($filename, 2, 5);
578   $versionset    .= substr($header, 6, 22);
579
580   if ($fromto ne "") {
581     $versionset .= "0000" . substr($header, 28, 19);
582   } else {
583     $datum = " " x 16;
584     $versionset .= $datum . "001" . substr($header, 28, 4);
585   }
586
587   $versionset .= _fill($blockcount, 5, '0');
588   $versionset .= "001";
589   $versionset .= " 1";
590   $versionset .= substr($header, -12, 10) . "    ";
591   $versionset .= " " x 53;
592
593   $main::lxdebug->leave_sub();
594
595   return $versionset;
596 }
597
598 sub make_ev_header {
599   $main::lxdebug->enter_sub();
600
601   my ($form, $fileno) = @_;
602
603   my $ev_header  = _fill($form->{datentraegernr}, 3, ' ', 'left');
604   $ev_header    .= "   ";
605   $ev_header    .= _fill($form->{beraternr}, 7, ' ', 'left');
606   $ev_header    .= _fill($form->{beratername}, 9, ' ', 'left');
607   $ev_header    .= " ";
608   $ev_header    .= (_fill($fileno, 5, '0')) x 2;
609   $ev_header    .= " " x 95;
610
611   $main::lxdebug->leave_sub();
612
613   return $ev_header;
614 }
615
616 sub kne_buchungsexport {
617   $main::lxdebug->enter_sub();
618
619   my ($myconfig, $form) = @_;
620
621   my @filenames;
622
623   my $export_path = _get_export_path() . "/";
624   my $filename    = "ED00000";
625   my $evfile      = "EV01";
626   my @ed_versionsets;
627   my $fileno = 0;
628
629   $form->header;
630   print qq|
631   <html>
632   <body>Export in Bearbeitung<br>
633   Buchungss&auml;tze verarbeitet:
634 |;
635
636   ($fromto, $start_jahr) =
637     &get_dates($form->{zeitraum}, $form->{monat},
638                $form->{quartal},  $form->{transdatefrom},
639                $form->{transdateto});
640   _get_transactions($fromto);
641   my $counter = 0;
642   print qq|<br>2. Durchlauf:|;
643   while (scalar(@{ $form->{DATEV} })) {
644     my $umsatzsumme = 0;
645     $filename++;
646     my $ed_filename = $export_path . $filename;
647     push(@filenames, $filename);
648     $header = &make_kne_data_header($myconfig, $form, $fromto, $start_jahr);
649
650     my $kne_file = SL::DATEV::KNEFile->new();
651     $kne_file->add_block($header);
652
653     while (scalar(@{ $form->{DATEV} }) > 0) {
654       $transaction = shift @{ $form->{DATEV} };
655       $trans_lines = scalar(@{$transaction});
656       $counter++;
657       if (($counter % 500) == 0) {
658         print("$counter ");
659       }
660
661       my $umsatz         = 0;
662       my $gegenkonto     = "";
663       my $konto          = "";
664       my $belegfeld1     = "";
665       my $datum          = "";
666       my $waehrung       = "";
667       my $buchungstext   = "";
668       my $belegfeld2     = "";
669       my $datevautomatik = 0;
670       my $taxkey         = 0;
671       my $charttax       = 0;
672       my %umlaute = ('ä' => 'ae',
673                      'ö' => 'oe',
674                      'ü' => 'ue',
675                      'Ä' => 'Ae',
676                      'Ö' => 'Oe',
677                      'Ü' => 'Ue',
678                      'ß' => 'sz');
679       for (my $i = 0; $i < $trans_lines; $i++) {
680         if ($trans_lines == 2) {
681           if (abs($transaction->[$i]->{'amount'}) > abs($umsatz)) {
682             $umsatz = $transaction->[$i]->{'amount'};
683           }
684         } else {
685           if (abs($transaction->[$i]->{'umsatz'}) > abs($umsatz)) {
686             $umsatz = $transaction->[$i]->{'umsatz'};
687           }
688         }
689         if ($transaction->[$i]->{'datevautomatik'}) {
690           $datevautomatik = 1;
691         }
692         if ($transaction->[$i]->{'taxkey'}) {
693           $taxkey = $transaction->[$i]->{'taxkey'};
694         }
695         if ($transaction->[$i]->{'charttax'}) {
696           $charttax = $transaction->[$i]->{'charttax'};
697         }
698         if (   ($transaction->[$i]->{'id'} eq $transaction->[$i]->{'chart_id'})
699             && ($trans_lines > 2)) {
700           undef($transaction->[$i]);
701         } elsif ($transaction->[$i]->{'amount'} > 0) {
702           $haben = $i;
703         } else {
704           $soll = $i;
705         }
706       }
707
708       # Umwandlung von Umlauten und Sonderzeichen in erlaubte Zeichen bei Textfeldern
709       foreach $umlaut (keys(%umlaute)) {
710         $transaction->[$haben]->{'invnumber'} =~ s/${umlaut}/${umlaute{$umlaut}}/g;
711         $transaction->[$haben]->{'name'}      =~ s/${umlaut}/${umlaute{$umlaut}}/g;
712       }
713
714       $transaction->[$haben]->{'invnumber'} =~ s/[^0-9A-Za-z\$\%\&\*\+\-\/]//g;
715       $transaction->[$haben]->{'name'}      =~ s/[^0-9A-Za-z\$\%\&\*\+\-\ \/]//g;
716
717       $transaction->[$haben]->{'invnumber'} =  substr($transaction->[$haben]->{'invnumber'}, 0, 12);
718       $transaction->[$haben]->{'name'}      =  substr($transaction->[$haben]->{'name'}, 0, 30);
719       $transaction->[$haben]->{'invnumber'} =~ s/\ *$//;
720       $transaction->[$haben]->{'name'}      =~ s/\ *$//;
721
722       if ($trans_lines >= 2) {
723
724         $gegenkonto = "a" . trim_leading_zeroes($transaction->[$haben]->{'accno'});
725         $konto      = "e" . trim_leading_zeroes($transaction->[$soll]->{'accno'});
726         if ($transaction->[$haben]->{'invnumber'} ne "") {
727           $belegfeld1 = "\xBD" . $transaction->[$haben]->{'invnumber'} . "\x1C";
728         }
729         $datum = "d";
730         $datum .= &datetofour($transaction->[$haben]->{'transdate'}, 0);
731         $waehrung = "\xB3" . "EUR" . "\x1C";
732         if ($transaction->[$haben]->{'name'} ne "") {
733           $buchungstext = "\x1E" . $transaction->[$haben]->{'name'} . "\x1C";
734         }
735         if ($transaction->[$haben]->{'duedate'} ne "") {
736           $belegfeld2 = "\xBE" . &datetofour($transaction->[$haben]->{'duedate'}, 1) . "\x1C";
737         }
738       }
739
740       $umsatz       = $kne_file->format_amount(abs($umsatz), 0);
741       $umsatzsumme += $umsatz;
742       $kne_file->add_block("+" . $umsatz);
743
744       if (   ( $datevautomatik || $taxkey)
745           && (!$datevautomatik || ($datevautomatik && ($charttax ne $taxkey)))) {
746         $kne_file->add_block("\x6C" . (!$datevautomatik ? $taxkey : "4"));
747       }
748
749       $kne_file->add_block($gegenkonto);
750       $kne_file->add_block($belegfeld1);
751       $kne_file->add_block($belegfeld2);
752       $kne_file->add_block($datum);
753       $kne_file->add_block($konto);
754       $kne_file->add_block($buchungstext);
755       $kne_file->add_block($waehrung . "\x79");
756     }
757
758     my $mandantenendsumme = "x" . $kne_file->format_amount($umsatzsumme / 100.0, 14) . "\x79\x7a";
759
760     $kne_file->add_block($mandantenendsumme);
761     $kne_file->flush();
762
763     open(ED, "> $ed_filename") or die "can't open outputfile: $!\n";
764     print(ED $kne_file->get_data());
765     close(ED);
766
767     $ed_versionset[$fileno] = &make_ed_versionset($header, $filename, $kne_file->get_block_count(), $fromto);
768     $fileno++;
769   }
770
771   #Make EV Verwaltungsdatei
772   $ev_header = &make_ev_header($form, $fileno);
773   $ev_filename = $export_path . $evfile;
774   push(@filenames, $evfile);
775   open(EV, "> $ev_filename") or die "can't open outputfile: EV01\n";
776   print(EV $ev_header);
777
778   foreach $file (@ed_versionset) {
779     print(EV $ed_versionset[$file]);
780   }
781   close(EV);
782   print qq|<br>Done. <br>
783 |;
784   ###
785   $main::lxdebug->leave_sub();
786
787   return { 'download_token' => get_download_token_for_path($export_path), 'filenames' => \@filenames };
788 }
789
790 sub kne_stammdatenexport {
791   $main::lxdebug->enter_sub();
792
793   my ($myconfig, $form) = @_;
794   $form->{abrechnungsnr} = "99";
795
796   $form->header;
797   print qq|
798   <html>
799   <body>Export in Bearbeitung<br>
800 |;
801
802   my @filenames;
803
804   my $export_path = _get_export_path() . "/";
805   my $filename    = "ED00000";
806   my $evfile      = "EV01";
807   my @ed_versionsets;
808   my $fileno          = 1;
809   my $i               = 0;
810   my $blockcount      = 1;
811   my $remaining_bytes = 256;
812   my $total_bytes     = 256;
813   my $buchungssatz    = "";
814   $filename++;
815   my $ed_filename = $export_path . $filename;
816   push(@filenames, $filename);
817   open(ED, "> $ed_filename") or die "can't open outputfile: $!\n";
818   $header = &make_kne_data_header($myconfig, $form, "");
819   $remaining_bytes -= length($header);
820
821   # connect to database
822   my $dbh = $form->dbconnect($myconfig);
823
824   my (@where, @values) = ((), ());
825   if ($form->{accnofrom}) {
826     push @where, 'c.accno >= ?';
827     push @values, $form->{accnofrom};
828   }
829   if ($form->{accnoto}) {
830     push @where, 'c.accno <= ?';
831     push @values, $form->{accnoto};
832   }
833
834   my $where_str = ' WHERE ' . join(' AND ', map { "($_)" } @where) if (scalar @where);
835
836   my $query     = qq|SELECT c.accno, c.description
837                      FROM chart c
838                      $where_str
839                      ORDER BY c.accno|;
840
841   my $sth = $dbh->prepare($query);
842   $sth->execute(@values) || $form->dberror($query);
843
844   while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
845     if (($remaining_bytes - length("t" . $ref->{'accno'})) <= 6) {
846       $fuellzeichen = ($blockcount * 256 - length($buchungssatz . $header));
847       $buchungssatz .= "\x00" x $fuellzeichen;
848       $blockcount++;
849       $total_bytes = ($blockcount) * 256;
850     }
851     $buchungssatz .= "t" . $ref->{'accno'};
852     $remaining_bytes = $total_bytes - length($buchungssatz . $header);
853     $ref->{'description'} =~ s/[^0-9A-Za-z\$\%\&\*\+\-\/]//g;
854     $ref->{'description'} = substr($ref->{'description'}, 0, 40);
855     $ref->{'description'} =~ s/\ *$//;
856
857     if (
858         ($remaining_bytes - length("\x1E" . $ref->{'description'} . "\x1C\x79")
859         ) <= 6
860       ) {
861       $fuellzeichen = ($blockcount * 256 - length($buchungssatz . $header));
862       $buchungssatz .= "\x00" x $fuellzeichen;
863       $blockcount++;
864       $total_bytes = ($blockcount) * 256;
865     }
866     $buchungssatz .= "\x1E" . $ref->{'description'} . "\x1C\x79";
867     $remaining_bytes = $total_bytes - length($buchungssatz . $header);
868   }
869
870   $sth->finish;
871   print(ED $header);
872   print(ED $buchungssatz);
873   $fuellzeichen = 256 - (length($header . $buchungssatz . "z") % 256);
874   $dateiende = "\x00" x $fuellzeichen;
875   print(ED "z");
876   print(ED $dateiende);
877   close(ED);
878
879   #Make EV Verwaltungsdatei
880   $ed_versionset[0] =
881     &make_ed_versionset($header, $filename, $blockcount, $fromto);
882
883   $ev_header = &make_ev_header($form, $fileno);
884   $ev_filename = $export_path . $evfile;
885   push(@filenames, $evfile);
886   open(EV, "> $ev_filename") or die "can't open outputfile: EV01\n";
887   print(EV $ev_header);
888
889   foreach $file (@ed_versionset) {
890     print(EV $ed_versionset[$file]);
891   }
892   close(EV);
893
894   $dbh->disconnect;
895   ###
896
897   print qq|<br>Done. <br>
898 |;
899
900   $main::lxdebug->leave_sub();
901
902   return { 'download_token' => get_download_token_for_path($export_path), 'filenames' => \@filenames };
903 }
904
905 1;