1 #=====================================================================
6 # Email: p.reetz@linet-services.de
7 # Web: http://www.lx-office.org
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.
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 #======================================================================
25 #======================================================================
33 use SL::DATEV::KNEFile;
38 use List::Util qw(max);
39 use Time::HiRes qw(gettimeofday);
41 sub _get_export_path {
42 $main::lxdebug->enter_sub();
44 my ($a, $b) = gettimeofday();
45 my $path = get_path_for_download_token("${a}-${b}-${$}");
47 mkpath($path) unless (-d $path);
49 $main::lxdebug->leave_sub();
54 sub get_path_for_download_token {
55 $main::lxdebug->enter_sub();
60 if ($token =~ m|^(\d+)-(\d+)-(\d+)$|) {
61 $path = $::lx_office_conf{paths}->{userspath} . "/datev-export-${1}-${2}-${3}";
64 $main::lxdebug->leave_sub();
69 sub get_download_token_for_path {
70 $main::lxdebug->enter_sub();
75 if ($path =~ m|.*datev-export-(\d+)-(\d+)-(\d+)/?$|) {
76 $token = "${1}-${2}-${3}";
79 $main::lxdebug->leave_sub();
84 sub clean_temporary_directories {
85 $main::lxdebug->enter_sub();
87 foreach my $path (glob($::lx_office_conf{paths}->{userspath} . "/datev-export-*")) {
88 next unless (-d $path);
90 my $mtime = (stat($path))[9];
91 next if ((time() - $mtime) < 8 * 60 * 60);
96 $main::lxdebug->leave_sub();
100 $main::lxdebug->enter_sub();
103 my $field_len = shift;
104 my $fill_char = shift;
105 my $alignment = shift || 'right';
107 my $text_len = length $text;
109 if ($field_len < $text_len) {
110 $text = substr $text, 0, $field_len;
112 } elsif ($field_len > $text_len) {
113 my $filler = ($fill_char) x ($field_len - $text_len);
114 $text = $alignment eq 'right' ? $filler . $text : $text . $filler;
117 $main::lxdebug->leave_sub();
122 sub get_datev_stamm {
123 $main::lxdebug->enter_sub();
125 my ($self, $myconfig, $form) = @_;
127 # connect to database
128 my $dbh = $form->dbconnect($myconfig);
130 my $query = qq|SELECT * FROM datev|;
131 my $sth = $dbh->prepare($query);
132 $sth->execute || $form->dberror($query);
134 my $ref = $sth->fetchrow_hashref("NAME_lc");
136 map { $form->{$_} = $ref->{$_} } keys %$ref;
140 $main::lxdebug->leave_sub();
143 sub save_datev_stamm {
144 $main::lxdebug->enter_sub();
146 my ($self, $myconfig, $form) = @_;
148 # connect to database
149 my $dbh = $form->dbconnect_noauto($myconfig);
151 my $query = qq|DELETE FROM datev|;
152 $dbh->do($query) || $form->dberror($query);
154 $query = qq|INSERT INTO datev
155 (beraternr, beratername, dfvkz, mandantennr, datentraegernr, abrechnungsnr) VALUES
157 . $dbh->quote($form->{beraternr}) . qq|,|
158 . $dbh->quote($form->{beratername}) . qq|,|
159 . $dbh->quote($form->{dfvkz}) . qq|,
161 . $dbh->quote($form->{mandantennr}) . qq|,|
162 . $dbh->quote($form->{datentraegernr}) . qq|,|
163 . $dbh->quote($form->{abrechnungsnr}) . qq|)|;
164 my $sth = $dbh->prepare($query);
165 $sth->execute || $form->dberror($query);
170 $main::lxdebug->leave_sub();
174 $main::lxdebug->enter_sub();
176 my ($self, $myconfig, $form) = @_;
179 if ($form->{exporttype} == 0) {
180 $result = kne_buchungsexport($myconfig, $form);
182 $result = kne_stammdatenexport($myconfig, $form);
185 $main::lxdebug->leave_sub();
191 $main::lxdebug->enter_sub();
193 my ($self, $myconfig, $form) = @_;
195 # connect to database
196 my $dbh = $form->dbconnect_noauto($myconfig);
199 $main::lxdebug->leave_sub();
203 $main::lxdebug->enter_sub();
205 my ($zeitraum, $monat, $quartal, $transdatefrom, $transdateto) = @_;
206 my ($fromto, $jahr, $leap);
208 my $form = $main::form;
210 $fromto = "transdate >= ";
215 if ($zeitraum eq "monat") {
217 $monat eq "1" && do {
218 $form->{fromdate} = "1.1.$jahr";
219 $form->{todate} = "31.1.$jahr";
222 $monat eq "2" && do {
223 $form->{fromdate} = "1.2.$jahr";
225 #this works from 1901 to 2099, 1900 and 2100 fail.
226 $leap = ($jahr % 4 == 0) ? "29" : "28";
227 $form->{todate} = "$leap.2.$jahr";
230 $monat eq "3" && do {
231 $form->{fromdate} = "1.3.$jahr";
232 $form->{todate} = "31.3.$jahr";
235 $monat eq "4" && do {
236 $form->{fromdate} = "1.4.$jahr";
237 $form->{todate} = "30.4.$jahr";
240 $monat eq "5" && do {
241 $form->{fromdate} = "1.5.$jahr";
242 $form->{todate} = "31.5.$jahr";
245 $monat eq "6" && do {
246 $form->{fromdate} = "1.6.$jahr";
247 $form->{todate} = "30.6.$jahr";
250 $monat eq "7" && do {
251 $form->{fromdate} = "1.7.$jahr";
252 $form->{todate} = "31.7.$jahr";
255 $monat eq "8" && do {
256 $form->{fromdate} = "1.8.$jahr";
257 $form->{todate} = "31.8.$jahr";
260 $monat eq "9" && do {
261 $form->{fromdate} = "1.9.$jahr";
262 $form->{todate} = "30.9.$jahr";
265 $monat eq "10" && do {
266 $form->{fromdate} = "1.10.$jahr";
267 $form->{todate} = "31.10.$jahr";
270 $monat eq "11" && do {
271 $form->{fromdate} = "1.11.$jahr";
272 $form->{todate} = "30.11.$jahr";
275 $monat eq "12" && do {
276 $form->{fromdate} = "1.12.$jahr";
277 $form->{todate} = "31.12.$jahr";
282 "'" . $form->{fromdate} . "' and transdate <= '" . $form->{todate} . "'";
285 elsif ($zeitraum eq "quartal") {
288 "'01.01." . $jahr . "' and transdate <= '31.03." . $jahr . "'";
289 } elsif ($quartal == 2) {
291 "'01.04." . $jahr . "' and transdate <= '30.06." . $jahr . "'";
292 } elsif ($quartal == 3) {
294 "'01.07." . $jahr . "' and transdate <= '30.09." . $jahr . "'";
295 } elsif ($quartal == 4) {
297 "'01.10." . $jahr . "' and transdate <= '31.12." . $jahr . "'";
301 elsif ($zeitraum eq "zeit") {
302 $fromto .= "'" . $transdatefrom . "' and transdate <= '" . $transdateto . "'";
303 my ($yy, $mm, $dd) = $main::locale->parse_date(\%main::myconfig, $transdatefrom);
307 $main::lxdebug->leave_sub();
309 return ($fromto, $jahr);
315 return $value < 0 ? -1
320 sub _get_transactions {
321 $main::lxdebug->enter_sub();
325 my $myconfig = \%main::myconfig;
326 my $form = $main::form;
328 my $dbh = $form->get_standard_dbh($myconfig);
333 $form->{net_gross_differences} = [];
334 $form->{sum_net_gross_differences} = 0;
336 $fromto =~ s/transdate/ac\.transdate/g;
338 my $taxkeys = Taxkeys->new();
339 my $filter = ''; # Useful for debugging purposes
341 my %all_taxchart_ids = selectall_as_map($form, $dbh, qq|SELECT DISTINCT chart_id, TRUE AS is_set FROM tax|, 'chart_id', 'is_set');
344 qq|SELECT ac.acc_trans_id, ac.transdate, ac.trans_id,ar.id, ac.amount, ac.taxkey,
345 ar.invnumber, ar.duedate, ar.amount as umsatz,
347 c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id, c.link,
350 LEFT JOIN ar ON (ac.trans_id = ar.id)
351 LEFT JOIN customer ct ON (ar.customer_id = ct.id)
352 LEFT JOIN chart c ON (ac.chart_id = c.id)
353 WHERE (ar.id IS NOT NULL)
359 SELECT ac.acc_trans_id, ac.transdate, ac.trans_id,ap.id, ac.amount, ac.taxkey,
360 ap.invnumber, ap.duedate, ap.amount as umsatz,
362 c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id, c.link,
365 LEFT JOIN ap ON (ac.trans_id = ap.id)
366 LEFT JOIN vendor ct ON (ap.vendor_id = ct.id)
367 LEFT JOIN chart c ON (ac.chart_id = c.id)
368 WHERE (ap.id IS NOT NULL)
374 SELECT ac.acc_trans_id, ac.transdate, ac.trans_id,gl.id, ac.amount, ac.taxkey,
375 gl.reference AS invnumber, gl.transdate AS duedate, ac.amount as umsatz,
376 gl.description AS name,
377 c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id, c.link,
380 LEFT JOIN gl ON (ac.trans_id = gl.id)
381 LEFT JOIN chart c ON (ac.chart_id = c.id)
382 WHERE (gl.id IS NOT NULL)
386 ORDER BY trans_id, acc_trans_id|;
388 my $sth = prepare_execute_query($form, $dbh, $query);
392 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
394 if (($counter % 500) == 0) {
398 my $trans = [ $ref ];
400 my $count = $ref->{amount};
402 my $subcent = abs($count) < 0.02;
404 while (abs($count) > 0.01 || $firstrun || ($subcent && abs($count) > 0.005)) {
405 my $ref2 = $sth->fetchrow_hashref("NAME_lc");
408 if ($ref2->{trans_id} != $trans->[0]->{trans_id}) {
409 $form->error("Unbalanced ledger! old trans_id " . $trans->[0]->{trans_id} . " new trans_id " . $ref2->{trans_id} . " count $count");
413 push @{ $trans }, $ref2;
415 $count += $ref2->{amount};
419 foreach my $i (0 .. scalar(@{ $trans }) - 1) {
420 my $ref = $trans->[$i];
421 my $prev_ref = 0 < $i ? $trans->[$i - 1] : undef;
422 if ( $all_taxchart_ids{$ref->{id}}
423 && ($ref->{link} =~ m/(?:AP_tax|AR_tax)/)
424 && ( ($prev_ref && $prev_ref->{taxkey} && (_sign($ref->{amount}) == _sign($prev_ref->{amount})))
425 || $ref->{invoice})) {
429 if ( !$ref->{invoice} # we have a non-invoice booking (=gl)
430 && $ref->{is_tax} # that has "is_tax" set
431 && !($prev_ref->{is_tax}) # previous line wasn't is_tax
432 && (_sign($ref->{amount}) == _sign($prev_ref->{amount}))) { # and sign same as previous sign
433 $trans->[$i - 1]->{tax_amount} = $ref->{amount};
437 my %taxid_taxkeys = ();
439 if (scalar(@{$trans}) <= 2) {
440 push @{ $form->{DATEV} }, $trans;
444 for my $j (0 .. (scalar(@{$trans}) - 1)) {
445 # for gl-bookings no split is allowed and there is no AR/AP account, so we always use the maximum value as a reference
446 # for ap/ar bookings we can always search for AR/AP in link and use that
447 if ( ( not $trans->[$j]->{'invoice'} and abs($trans->[$j]->{'amount'}) > abs($absumsatz) )
448 or ($trans->[$j]->{'invoice'} and ($trans->[$j]->{'link'} eq 'AR' or $trans->[$j]->{'link'} eq 'AP'))) {
449 $absumsatz = $trans->[$j]->{'amount'};
454 my $ml = ($trans->[0]->{'umsatz'} > 0) ? 1 : -1;
455 my $rounding_error = 0;
458 for my $j (0 .. (scalar(@{$trans}) - 1)) {
459 if ( ($j != $notsplitindex)
460 && !$trans->[$j]->{is_tax}
461 && ( $trans->[$j]->{'taxkey'} eq ""
462 || $trans->[$j]->{'taxkey'} eq "0"
463 || $trans->[$j]->{'taxkey'} eq "1"
464 || $trans->[$j]->{'taxkey'} eq "10"
465 || $trans->[$j]->{'taxkey'} eq "11")) {
467 map { $new_trans{$_} = $trans->[$notsplitindex]->{$_}; } keys %{ $trans->[$notsplitindex] };
469 $absumsatz += $trans->[$j]->{'amount'};
470 $new_trans{'amount'} = $trans->[$j]->{'amount'} * (-1);
471 $new_trans{'umsatz'} = abs($trans->[$j]->{'amount'}) * $ml;
472 $trans->[$j]->{'umsatz'} = abs($trans->[$j]->{'amount'}) * $ml;
474 push @{ $form->{DATEV} }, [ \%new_trans, $trans->[$j] ];
476 } elsif (($j != $notsplitindex) && !$trans->[$j]->{is_tax}) {
477 my %tax_info = $taxkeys->get_full_tax_info('transdate' => $trans->[$j]->{transdate});
480 map { $new_trans{$_} = $trans->[$notsplitindex]->{$_}; } keys %{ $trans->[$notsplitindex] };
482 my $tax_rate = $tax_info{taxkeys}->{ $trans->[$j]->{'taxkey'} }->{taxrate};
483 $new_trans{'net_amount'} = $trans->[$j]->{'amount'} * -1;
484 $new_trans{'tax_rate'} = 1 + $tax_rate;
486 if (!$trans->[$j]->{'invoice'}) {
487 $new_trans{'amount'} = $form->round_amount(-1 * ($trans->[$j]->{amount} + $trans->[$j]->{tax_amount}), 2);
488 $new_trans{'umsatz'} = abs($new_trans{'amount'}) * $ml;
489 $trans->[$j]->{'umsatz'} = $new_trans{'umsatz'};
490 $absumsatz += -1 * $new_trans{'amount'};
493 my $unrounded = $trans->[$j]->{'amount'} * (1 + $tax_rate) * -1 + $rounding_error;
494 my $rounded = $form->round_amount($unrounded, 2);
496 $rounding_error = $unrounded - $rounded;
497 $new_trans{'amount'} = $rounded;
498 $new_trans{'umsatz'} = abs($rounded) * $ml;
499 $trans->[$j]->{'umsatz'} = $new_trans{umsatz};
500 $absumsatz -= $rounded;
503 push @{ $form->{DATEV} }, [ \%new_trans, $trans->[$j] ];
504 push @taxed, $form->{DATEV}->[-1];
510 while ((abs($absumsatz) >= 0.01) && (abs($absumsatz) < 1.00)) {
511 if ($idx >= scalar @taxed) {
512 last if (!$correction);
518 my $transaction = $taxed[$idx]->[0];
520 my $old_amount = $transaction->{amount};
521 my $old_correction = $correction;
524 if (!$transaction->{diff}) {
525 @possible_diffs = (0.01, -0.01);
527 @possible_diffs = ($transaction->{diff});
530 foreach my $diff (@possible_diffs) {
531 my $net_amount = $form->round_amount(($transaction->{amount} + $diff) / $transaction->{tax_rate}, 2);
532 next if ($net_amount != $transaction->{net_amount});
534 $transaction->{diff} = $diff;
535 $transaction->{amount} += $diff;
536 $transaction->{umsatz} += $diff;
546 $absumsatz = $form->round_amount($absumsatz, 2);
547 if (abs($absumsatz) >= (0.01 * (1 + scalar @taxed))) {
548 push @errors, "Datev-Export fehlgeschlagen! Bei Transaktion $trans->[0]->{trans_id} ($absumsatz)\n";
550 } elsif (abs($absumsatz) >= 0.01) {
551 push @{ $form->{net_gross_differences} }, $absumsatz;
552 $form->{sum_net_gross_differences} += $absumsatz;
558 $form->error(join("<br>\n", @errors)) if (@errors);
560 $main::lxdebug->leave_sub();
563 sub make_kne_data_header {
564 $main::lxdebug->enter_sub();
566 my ($myconfig, $form, $fromto, $start_jahr) = @_;
569 my $jahr = $start_jahr;
576 my $header = "\x1D\x181";
577 $header .= _fill($form->{datentraegernr}, 3, ' ', 'left');
578 $header .= ($fromto) ? "11" : "13"; # Anwendungsnummer
579 $header .= _fill($form->{dfvkz}, 2, '0');
580 $header .= _fill($form->{beraternr}, 7, '0');
581 $header .= _fill($form->{mandantennr}, 5, '0');
582 $header .= _fill($form->{abrechnungsnr} . $jahr, 6, '0');
584 $fromto =~ s/transdate|>=|and|\'|<=//g;
585 my ($from, $to) = split / /, $fromto;
590 my ($fday, $fmonth, $fyear) = split(/\./, $from);
591 if (length($fmonth) < 2) {
592 $fmonth = "0" . $fmonth;
594 if (length($fday) < 2) {
597 $from = $fday . $fmonth . substr($fyear, -2, 2);
605 my ($tday, $tmonth, $tyear) = split(/\./, $to);
606 if (length($tmonth) < 2) {
607 $tmonth = "0" . $tmonth;
609 if (length($tday) < 2) {
612 $to = $tday . $tmonth . substr($tyear, -2, 2);
620 $header .= $primanota;
623 $header .= _fill($form->{passwort}, 4, '0');
624 $header .= " " x 16; # Anwendungsinfo
625 $header .= " " x 16; # Inputinfo
629 my $versionssatz = $form->{exporttype} == 0 ? "\xB5" . "1," : "\xB6" . "1,";
631 my $dbh = $form->get_standard_dbh($myconfig);
632 my $query = qq|SELECT accno FROM chart LIMIT 1|;
633 my $ref = selectfirst_hashref_query($form, $dbh, $query);
635 $versionssatz .= length $ref->{accno};
636 $versionssatz .= ",";
637 $versionssatz .= length $ref->{accno};
638 $versionssatz .= ",SELF" . "\x1C\x79";
640 $header .= $versionssatz;
642 $main::lxdebug->leave_sub();
648 $main::lxdebug->enter_sub();
650 my ($date, $six) = @_;
652 my ($day, $month, $year) = split(/\./, $date);
655 $day = substr($day, 1, 1);
657 if (length($month) < 2) {
658 $month = "0" . $month;
660 if (length($year) > 2) {
661 $year = substr($year, -2, 2);
665 $date = $day . $month . $year;
667 $date = $day . $month;
670 $main::lxdebug->leave_sub();
675 sub trim_leading_zeroes {
683 sub make_ed_versionset {
684 $main::lxdebug->enter_sub();
686 my ($header, $filename, $blockcount, $fromto) = @_;
688 my $versionset = "V" . substr($filename, 2, 5);
689 $versionset .= substr($header, 6, 22);
692 $versionset .= "0000" . substr($header, 28, 19);
694 my $datum = " " x 16;
695 $versionset .= $datum . "001" . substr($header, 28, 4);
698 $versionset .= _fill($blockcount, 5, '0');
699 $versionset .= "001";
701 $versionset .= substr($header, -12, 10) . " ";
702 $versionset .= " " x 53;
704 $main::lxdebug->leave_sub();
710 $main::lxdebug->enter_sub();
712 my ($form, $fileno) = @_;
714 my $ev_header = _fill($form->{datentraegernr}, 3, ' ', 'left');
716 $ev_header .= _fill($form->{beraternr}, 7, ' ', 'left');
717 $ev_header .= _fill($form->{beratername}, 9, ' ', 'left');
719 $ev_header .= (_fill($fileno, 5, '0')) x 2;
720 $ev_header .= " " x 95;
722 $main::lxdebug->leave_sub();
727 sub kne_buchungsexport {
728 $main::lxdebug->enter_sub();
730 my ($myconfig, $form) = @_;
734 my $export_path = _get_export_path() . "/";
735 my $filename = "ED00000";
743 <body>Export in Bearbeitung<br>
744 Buchungssätze verarbeitet:
747 my ($fromto, $start_jahr) =
748 &get_dates($form->{zeitraum}, $form->{monat},
749 $form->{quartal}, $form->{transdatefrom},
750 $form->{transdateto});
751 _get_transactions($fromto);
753 print qq|<br>2. Durchlauf:|;
754 while (scalar(@{ $form->{DATEV} })) {
757 my $ed_filename = $export_path . $filename;
758 push(@filenames, $filename);
759 my $header = &make_kne_data_header($myconfig, $form, $fromto, $start_jahr);
761 my $kne_file = SL::DATEV::KNEFile->new();
762 $kne_file->add_block($header);
764 while (scalar(@{ $form->{DATEV} }) > 0) {
765 my $transaction = shift @{ $form->{DATEV} };
766 my $trans_lines = scalar(@{$transaction});
768 if (($counter % 500) == 0) {
778 my $buchungstext = "";
780 my $datevautomatik = 0;
784 my $iconv = $::locale->{iconv_utf8};
785 my %umlaute = ($iconv->convert('ä') => 'ae',
786 $iconv->convert('ö') => 'oe',
787 $iconv->convert('ü') => 'ue',
788 $iconv->convert('Ä') => 'Ae',
789 $iconv->convert('Ö') => 'Oe',
790 $iconv->convert('Ü') => 'Ue',
791 $iconv->convert('ß') => 'sz');
792 for (my $i = 0; $i < $trans_lines; $i++) {
793 if ($trans_lines == 2) {
794 if (abs($transaction->[$i]->{'amount'}) > abs($umsatz)) {
795 $umsatz = $transaction->[$i]->{'amount'};
798 if (abs($transaction->[$i]->{'umsatz'}) > abs($umsatz)) {
799 $umsatz = $transaction->[$i]->{'umsatz'};
802 if ($transaction->[$i]->{'datevautomatik'}) {
805 if ($transaction->[$i]->{'taxkey'}) {
806 $taxkey = $transaction->[$i]->{'taxkey'};
808 if ($transaction->[$i]->{'charttax'}) {
809 $charttax = $transaction->[$i]->{'charttax'};
811 if ($transaction->[$i]->{'amount'} > 0) {
818 # Umwandlung von Umlauten und Sonderzeichen in erlaubte Zeichen bei Textfeldern
819 foreach my $umlaut (keys(%umlaute)) {
820 $transaction->[$haben]->{'invnumber'} =~ s/${umlaut}/${umlaute{$umlaut}}/g;
821 $transaction->[$haben]->{'name'} =~ s/${umlaut}/${umlaute{$umlaut}}/g;
824 $transaction->[$haben]->{'invnumber'} =~ s/[^0-9A-Za-z\$\%\&\*\+\-\/]//g;
825 $transaction->[$haben]->{'name'} =~ s/[^0-9A-Za-z\$\%\&\*\+\-\ \/]//g;
827 $transaction->[$haben]->{'invnumber'} = substr($transaction->[$haben]->{'invnumber'}, 0, 12);
828 $transaction->[$haben]->{'name'} = substr($transaction->[$haben]->{'name'}, 0, 30);
829 $transaction->[$haben]->{'invnumber'} =~ s/\ *$//;
830 $transaction->[$haben]->{'name'} =~ s/\ *$//;
832 if ($trans_lines >= 2) {
834 $gegenkonto = "a" . trim_leading_zeroes($transaction->[$haben]->{'accno'});
835 $konto = "e" . trim_leading_zeroes($transaction->[$soll]->{'accno'});
836 if ($transaction->[$haben]->{'invnumber'} ne "") {
837 $belegfeld1 = "\xBD" . $transaction->[$haben]->{'invnumber'} . "\x1C";
840 $datum .= &datetofour($transaction->[$haben]->{'transdate'}, 0);
841 $waehrung = "\xB3" . "EUR" . "\x1C";
842 if ($transaction->[$haben]->{'name'} ne "") {
843 $buchungstext = "\x1E" . $transaction->[$haben]->{'name'} . "\x1C";
845 if ($transaction->[$haben]->{'duedate'} ne "") {
846 $belegfeld2 = "\xBE" . &datetofour($transaction->[$haben]->{'duedate'}, 1) . "\x1C";
850 $umsatz = $kne_file->format_amount(abs($umsatz), 0);
851 $umsatzsumme += $umsatz;
852 $kne_file->add_block("+" . $umsatz);
854 # Dies ist die einzige Stelle die datevautomatik auswertet. Was soll gesagt werden?
855 # Im Prinzip hat jeder acc_trans Eintrag einen Steuerschlüssel, außer, bei gewissen Fällen
856 # wie: Kreditorenbuchung mit negativen Vorzeichen, SEPA-Export oder Rechnungen die per
857 # Skript angelegt werden.
858 # Also falls ein Steuerschlüssel da ist und NICHT datevautomatik diesen Block hinzufügen.
859 # Oder aber datevautomatik ist WAHR, aber der Steuerschlüssel in der acc_trans weicht
860 # von dem in der Chart ab: Also wahrscheinlich Programmfehler (NULL übergeben, statt
861 # DATEV-Steuerschlüssel) oder der Steuerschlüssel des Kontos weicht WIRKLICH von dem Eintrag in der
862 # acc_trans ab. Gibt es für diesen Fall eine plausiblen Grund?
864 if ( ( $datevautomatik || $taxkey)
865 && (!$datevautomatik || ($datevautomatik && ($charttax ne $taxkey)))) {
866 # $kne_file->add_block("\x6C" . (!$datevautomatik ? $taxkey : "4"));
867 $kne_file->add_block("\x6C${taxkey}");
870 $kne_file->add_block($gegenkonto);
871 $kne_file->add_block($belegfeld1);
872 $kne_file->add_block($belegfeld2);
873 $kne_file->add_block($datum);
874 $kne_file->add_block($konto);
875 $kne_file->add_block($buchungstext);
876 $kne_file->add_block($waehrung . "\x79");
879 my $mandantenendsumme = "x" . $kne_file->format_amount($umsatzsumme / 100.0, 14) . "\x79\x7a";
881 $kne_file->add_block($mandantenendsumme);
884 open(ED, ">", $ed_filename) or die "can't open outputfile: $!\n";
885 print(ED $kne_file->get_data());
888 $ed_versionset[$fileno] = &make_ed_versionset($header, $filename, $kne_file->get_block_count(), $fromto);
892 #Make EV Verwaltungsdatei
893 my $ev_header = &make_ev_header($form, $fileno);
894 my $ev_filename = $export_path . $evfile;
895 push(@filenames, $evfile);
896 open(EV, ">", $ev_filename) or die "can't open outputfile: EV01\n";
897 print(EV $ev_header);
899 foreach my $file (@ed_versionset) {
900 print(EV $ed_versionset[$file]);
903 print qq|<br>Done. <br>
906 $main::lxdebug->leave_sub();
908 return { 'download_token' => get_download_token_for_path($export_path), 'filenames' => \@filenames };
911 sub kne_stammdatenexport {
912 $main::lxdebug->enter_sub();
914 my ($myconfig, $form) = @_;
915 $form->{abrechnungsnr} = "99";
920 <body>Export in Bearbeitung<br>
925 my $export_path = _get_export_path() . "/";
926 my $filename = "ED00000";
932 my $remaining_bytes = 256;
933 my $total_bytes = 256;
934 my $buchungssatz = "";
936 my $ed_filename = $export_path . $filename;
937 push(@filenames, $filename);
938 open(ED, ">", $ed_filename) or die "can't open outputfile: $!\n";
939 my $header = &make_kne_data_header($myconfig, $form, "");
940 $remaining_bytes -= length($header);
945 # connect to database
946 my $dbh = $form->dbconnect($myconfig);
948 my (@where, @values) = ((), ());
949 if ($form->{accnofrom}) {
950 push @where, 'c.accno >= ?';
951 push @values, $form->{accnofrom};
953 if ($form->{accnoto}) {
954 push @where, 'c.accno <= ?';
955 push @values, $form->{accnoto};
958 my $where_str = @where ? ' WHERE ' . join(' AND ', map { "($_)" } @where) : '';
960 my $query = qq|SELECT c.accno, c.description
965 my $sth = $dbh->prepare($query);
966 $sth->execute(@values) || $form->dberror($query);
968 while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
969 if (($remaining_bytes - length("t" . $ref->{'accno'})) <= 6) {
970 $fuellzeichen = ($blockcount * 256 - length($buchungssatz . $header));
971 $buchungssatz .= "\x00" x $fuellzeichen;
973 $total_bytes = ($blockcount) * 256;
975 $buchungssatz .= "t" . $ref->{'accno'};
976 $remaining_bytes = $total_bytes - length($buchungssatz . $header);
977 $ref->{'description'} =~ s/[^0-9A-Za-z\$\%\&\*\+\-\/]//g;
978 $ref->{'description'} = substr($ref->{'description'}, 0, 40);
979 $ref->{'description'} =~ s/\ *$//;
982 ($remaining_bytes - length("\x1E" . $ref->{'description'} . "\x1C\x79")
985 $fuellzeichen = ($blockcount * 256 - length($buchungssatz . $header));
986 $buchungssatz .= "\x00" x $fuellzeichen;
988 $total_bytes = ($blockcount) * 256;
990 $buchungssatz .= "\x1E" . $ref->{'description'} . "\x1C\x79";
991 $remaining_bytes = $total_bytes - length($buchungssatz . $header);
996 print(ED $buchungssatz);
997 $fuellzeichen = 256 - (length($header . $buchungssatz . "z") % 256);
998 my $dateiende = "\x00" x $fuellzeichen;
1000 print(ED $dateiende);
1003 #Make EV Verwaltungsdatei
1005 &make_ed_versionset($header, $filename, $blockcount, $fromto);
1007 my $ev_header = &make_ev_header($form, $fileno);
1008 my $ev_filename = $export_path . $evfile;
1009 push(@filenames, $evfile);
1010 open(EV, ">", $ev_filename) or die "can't open outputfile: EV01\n";
1011 print(EV $ev_header);
1013 foreach my $file (@ed_versionset) {
1014 print(EV $ed_versionset[$file]);
1021 print qq|<br>Done. <br>
1024 $main::lxdebug->leave_sub();
1026 return { 'download_token' => get_download_token_for_path($export_path), 'filenames' => \@filenames };