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