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