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
 
 434         $trans->[$i - 1]->{tax_amount} = $ref->{amount};
 
 438     my %taxid_taxkeys = ();
 
 440     if (scalar(@{$trans}) <= 2) {
 
 441       push @{ $form->{DATEV} }, $trans;
 
 445     for my $j (0 .. (scalar(@{$trans}) - 1)) {
 
 446       # for gl-bookings no split is allowed and there is no AR/AP account, so we always use the maximum value as a reference
 
 447       # for ap/ar bookings we can always search for AR/AP in link and use that
 
 448       if ( ( not $trans->[$j]->{'invoice'} and abs($trans->[$j]->{'amount'}) > abs($absumsatz) )
 
 449          or ($trans->[$j]->{'invoice'} and ($trans->[$j]->{'link'} eq 'AR' or $trans->[$j]->{'link'} eq 'AP'))) {
 
 450         $absumsatz     = $trans->[$j]->{'amount'};
 
 455     my $ml             = ($trans->[0]->{'umsatz'} > 0) ? 1 : -1;
 
 456     my $rounding_error = 0;
 
 459     for my $j (0 .. (scalar(@{$trans}) - 1)) {
 
 460       if (   ($j != $notsplitindex)
 
 461           && !$trans->[$j]->{is_tax}
 
 462           && (   $trans->[$j]->{'taxkey'} eq ""
 
 463               || $trans->[$j]->{'taxkey'} eq "0"
 
 464               || $trans->[$j]->{'taxkey'} eq "1"
 
 465               || $trans->[$j]->{'taxkey'} eq "10"
 
 466               || $trans->[$j]->{'taxkey'} eq "11")) {
 
 468         map { $new_trans{$_} = $trans->[$notsplitindex]->{$_}; } keys %{ $trans->[$notsplitindex] };
 
 470         $absumsatz               += $trans->[$j]->{'amount'};
 
 471         $new_trans{'amount'}      = $trans->[$j]->{'amount'} * (-1);
 
 472         $new_trans{'umsatz'}      = abs($trans->[$j]->{'amount'}) * $ml;
 
 473         $trans->[$j]->{'umsatz'}  = abs($trans->[$j]->{'amount'}) * $ml;
 
 475         push @{ $form->{DATEV} }, [ \%new_trans, $trans->[$j] ];
 
 477       } elsif (($j != $notsplitindex) && !$trans->[$j]->{is_tax}) {
 
 478         my %tax_info = $taxkeys->get_full_tax_info('transdate' => $trans->[$j]->{transdate});
 
 481         map { $new_trans{$_} = $trans->[$notsplitindex]->{$_}; } keys %{ $trans->[$notsplitindex] };
 
 483         my $tax_rate              = $tax_info{taxkeys}->{ $trans->[$j]->{'taxkey'} }->{taxrate};
 
 484         $new_trans{'net_amount'}  = $trans->[$j]->{'amount'} * -1;
 
 485         $new_trans{'tax_rate'}    = 1 + $tax_rate;
 
 487         if (!$trans->[$j]->{'invoice'}) {
 
 488           $new_trans{'amount'}      = $form->round_amount(-1 * ($trans->[$j]->{amount} + $trans->[$j]->{tax_amount}), 2);
 
 489           $new_trans{'umsatz'}      = abs($new_trans{'amount'}) * $ml;
 
 490           $trans->[$j]->{'umsatz'}  = $new_trans{'umsatz'};
 
 491           $absumsatz               += -1 * $new_trans{'amount'};
 
 494           my $unrounded             = $trans->[$j]->{'amount'} * (1 + $tax_rate) * -1 + $rounding_error;
 
 495           my $rounded               = $form->round_amount($unrounded, 2);
 
 497           $rounding_error           = $unrounded - $rounded;
 
 498           $new_trans{'amount'}      = $rounded;
 
 499           $new_trans{'umsatz'}      = abs($rounded) * $ml;
 
 500           $trans->[$j]->{'umsatz'}  = $new_trans{umsatz};
 
 501           $absumsatz               -= $rounded;
 
 504         push @{ $form->{DATEV} }, [ \%new_trans, $trans->[$j] ];
 
 505         push @taxed, $form->{DATEV}->[-1];
 
 511     while ((abs($absumsatz) >= 0.01) && (abs($absumsatz) < 1.00)) {
 
 512       if ($idx >= scalar @taxed) {
 
 513         last if (!$correction);
 
 519       my $transaction = $taxed[$idx]->[0];
 
 521       my $old_amount     = $transaction->{amount};
 
 522       my $old_correction = $correction;
 
 525       if (!$transaction->{diff}) {
 
 526         @possible_diffs = (0.01, -0.01);
 
 528         @possible_diffs = ($transaction->{diff});
 
 531       foreach my $diff (@possible_diffs) {
 
 532         my $net_amount = $form->round_amount(($transaction->{amount} + $diff) / $transaction->{tax_rate}, 2);
 
 533         next if ($net_amount != $transaction->{net_amount});
 
 535         $transaction->{diff}    = $diff;
 
 536         $transaction->{amount} += $diff;
 
 537         $transaction->{umsatz} += $diff;
 
 547     $absumsatz = $form->round_amount($absumsatz, 2);
 
 548     if (abs($absumsatz) >= (0.01 * (1 + scalar @taxed))) {
 
 549       push @errors, "Datev-Export fehlgeschlagen! Bei Transaktion $trans->[0]->{trans_id} ($absumsatz)\n";
 
 551     } elsif (abs($absumsatz) >= 0.01) {
 
 552       push @{ $form->{net_gross_differences} }, $absumsatz;
 
 553       $form->{sum_net_gross_differences} += $absumsatz;
 
 559   $form->error(join("<br>\n", @errors)) if (@errors);
 
 561   $main::lxdebug->leave_sub();
 
 564 sub make_kne_data_header {
 
 565   $main::lxdebug->enter_sub();
 
 567   my ($myconfig, $form, $fromto, $start_jahr) = @_;
 
 570   my $jahr = $start_jahr;
 
 577   my $header  = "\x1D\x181";
 
 578   $header    .= _fill($form->{datentraegernr}, 3, ' ', 'left');
 
 579   $header    .= ($fromto) ? "11" : "13"; # Anwendungsnummer
 
 580   $header    .= _fill($form->{dfvkz}, 2, '0');
 
 581   $header    .= _fill($form->{beraternr}, 7, '0');
 
 582   $header    .= _fill($form->{mandantennr}, 5, '0');
 
 583   $header    .= _fill($form->{abrechnungsnr} . $jahr, 6, '0');
 
 585   $fromto         =~ s/transdate|>=|and|\'|<=//g;
 
 586   my ($from, $to) =  split /   /, $fromto;
 
 591     my ($fday, $fmonth, $fyear) = split(/\./, $from);
 
 592     if (length($fmonth) < 2) {
 
 593       $fmonth = "0" . $fmonth;
 
 595     if (length($fday) < 2) {
 
 598     $from = $fday . $fmonth . substr($fyear, -2, 2);
 
 606     my ($tday, $tmonth, $tyear) = split(/\./, $to);
 
 607     if (length($tmonth) < 2) {
 
 608       $tmonth = "0" . $tmonth;
 
 610     if (length($tday) < 2) {
 
 613     $to = $tday . $tmonth . substr($tyear, -2, 2);
 
 621     $header .= $primanota;
 
 624   $header .= _fill($form->{passwort}, 4, '0');
 
 625   $header .= " " x 16;       # Anwendungsinfo
 
 626   $header .= " " x 16;       # Inputinfo
 
 630   my $versionssatz  = $form->{exporttype} == 0 ? "\xB5" . "1," : "\xB6" . "1,";
 
 632   my $dbh           = $form->get_standard_dbh($myconfig);
 
 633   my $query         = qq|SELECT accno FROM chart LIMIT 1|;
 
 634   my $ref           = selectfirst_hashref_query($form, $dbh, $query);
 
 636   $versionssatz    .= length $ref->{accno};
 
 637   $versionssatz    .= ",";
 
 638   $versionssatz    .= length $ref->{accno};
 
 639   $versionssatz    .= ",SELF" . "\x1C\x79";
 
 641   $header          .= $versionssatz;
 
 643   $main::lxdebug->leave_sub();
 
 649   $main::lxdebug->enter_sub();
 
 651   my ($date, $six) = @_;
 
 653   my ($day, $month, $year) = split(/\./, $date);
 
 656     $day = substr($day, 1, 1);
 
 658   if (length($month) < 2) {
 
 659     $month = "0" . $month;
 
 661   if (length($year) > 2) {
 
 662     $year = substr($year, -2, 2);
 
 666     $date = $day . $month . $year;
 
 668     $date = $day . $month;
 
 671   $main::lxdebug->leave_sub();
 
 676 sub trim_leading_zeroes {
 
 684 sub make_ed_versionset {
 
 685   $main::lxdebug->enter_sub();
 
 687   my ($header, $filename, $blockcount, $fromto) = @_;
 
 689   my $versionset  = "V" . substr($filename, 2, 5);
 
 690   $versionset    .= substr($header, 6, 22);
 
 693     $versionset .= "0000" . substr($header, 28, 19);
 
 695     my $datum = " " x 16;
 
 696     $versionset .= $datum . "001" . substr($header, 28, 4);
 
 699   $versionset .= _fill($blockcount, 5, '0');
 
 700   $versionset .= "001";
 
 702   $versionset .= substr($header, -12, 10) . "    ";
 
 703   $versionset .= " " x 53;
 
 705   $main::lxdebug->leave_sub();
 
 711   $main::lxdebug->enter_sub();
 
 713   my ($form, $fileno) = @_;
 
 715   my $ev_header  = _fill($form->{datentraegernr}, 3, ' ', 'left');
 
 717   $ev_header    .= _fill($form->{beraternr}, 7, ' ', 'left');
 
 718   $ev_header    .= _fill($form->{beratername}, 9, ' ', 'left');
 
 720   $ev_header    .= (_fill($fileno, 5, '0')) x 2;
 
 721   $ev_header    .= " " x 95;
 
 723   $main::lxdebug->leave_sub();
 
 728 sub kne_buchungsexport {
 
 729   $main::lxdebug->enter_sub();
 
 731   my ($myconfig, $form) = @_;
 
 735   my $export_path = _get_export_path() . "/";
 
 736   my $filename    = "ED00000";
 
 744   <body>Export in Bearbeitung<br>
 
 745   Buchungssätze verarbeitet:
 
 748   my ($fromto, $start_jahr) =
 
 749     &get_dates($form->{zeitraum}, $form->{monat},
 
 750                $form->{quartal},  $form->{transdatefrom},
 
 751                $form->{transdateto});
 
 752   _get_transactions($fromto);
 
 754   print qq|<br>2. Durchlauf:|;
 
 755   while (scalar(@{ $form->{DATEV} })) {
 
 758     my $ed_filename = $export_path . $filename;
 
 759     push(@filenames, $filename);
 
 760     my $header = &make_kne_data_header($myconfig, $form, $fromto, $start_jahr);
 
 762     my $kne_file = SL::DATEV::KNEFile->new();
 
 763     $kne_file->add_block($header);
 
 765     while (scalar(@{ $form->{DATEV} }) > 0) {
 
 766       my $transaction = shift @{ $form->{DATEV} };
 
 767       my $trans_lines = scalar(@{$transaction});
 
 769       if (($counter % 500) == 0) {
 
 779       my $buchungstext   = "";
 
 781       my $datevautomatik = 0;
 
 785       my $iconv          = $::locale->{iconv_utf8};
 
 786       my %umlaute = ($iconv->convert('ä') => 'ae',
 
 787                      $iconv->convert('ö') => 'oe',
 
 788                      $iconv->convert('ü') => 'ue',
 
 789                      $iconv->convert('Ä') => 'Ae',
 
 790                      $iconv->convert('Ö') => 'Oe',
 
 791                      $iconv->convert('Ü') => 'Ue',
 
 792                      $iconv->convert('ß') => 'sz');
 
 793       for (my $i = 0; $i < $trans_lines; $i++) {
 
 794         if ($trans_lines == 2) {
 
 795           if (abs($transaction->[$i]->{'amount'}) > abs($umsatz)) {
 
 796             $umsatz = $transaction->[$i]->{'amount'};
 
 799           if (abs($transaction->[$i]->{'umsatz'}) > abs($umsatz)) {
 
 800             $umsatz = $transaction->[$i]->{'umsatz'};
 
 803         if ($transaction->[$i]->{'datevautomatik'}) {
 
 806         if ($transaction->[$i]->{'taxkey'}) {
 
 807           $taxkey = $transaction->[$i]->{'taxkey'};
 
 809         if ($transaction->[$i]->{'charttax'}) {
 
 810           $charttax = $transaction->[$i]->{'charttax'};
 
 812         if ($transaction->[$i]->{'amount'} > 0) {
 
 819       # Umwandlung von Umlauten und Sonderzeichen in erlaubte Zeichen bei Textfeldern
 
 820       foreach my $umlaut (keys(%umlaute)) {
 
 821         $transaction->[$haben]->{'invnumber'} =~ s/${umlaut}/${umlaute{$umlaut}}/g;
 
 822         $transaction->[$haben]->{'name'}      =~ s/${umlaut}/${umlaute{$umlaut}}/g;
 
 825       $transaction->[$haben]->{'invnumber'} =~ s/[^0-9A-Za-z\$\%\&\*\+\-\/]//g;
 
 826       $transaction->[$haben]->{'name'}      =~ s/[^0-9A-Za-z\$\%\&\*\+\-\ \/]//g;
 
 828       $transaction->[$haben]->{'invnumber'} =  substr($transaction->[$haben]->{'invnumber'}, 0, 12);
 
 829       $transaction->[$haben]->{'name'}      =  substr($transaction->[$haben]->{'name'}, 0, 30);
 
 830       $transaction->[$haben]->{'invnumber'} =~ s/\ *$//;
 
 831       $transaction->[$haben]->{'name'}      =~ s/\ *$//;
 
 833       if ($trans_lines >= 2) {
 
 835         $gegenkonto = "a" . trim_leading_zeroes($transaction->[$haben]->{'accno'});
 
 836         $konto      = "e" . trim_leading_zeroes($transaction->[$soll]->{'accno'});
 
 837         if ($transaction->[$haben]->{'invnumber'} ne "") {
 
 838           $belegfeld1 = "\xBD" . $transaction->[$haben]->{'invnumber'} . "\x1C";
 
 841         $datum .= &datetofour($transaction->[$haben]->{'transdate'}, 0);
 
 842         $waehrung = "\xB3" . "EUR" . "\x1C";
 
 843         if ($transaction->[$haben]->{'name'} ne "") {
 
 844           $buchungstext = "\x1E" . $transaction->[$haben]->{'name'} . "\x1C";
 
 846         if ($transaction->[$haben]->{'duedate'} ne "") {
 
 847           $belegfeld2 = "\xBE" . &datetofour($transaction->[$haben]->{'duedate'}, 1) . "\x1C";
 
 851       $umsatz       = $kne_file->format_amount(abs($umsatz), 0);
 
 852       $umsatzsumme += $umsatz;
 
 853       $kne_file->add_block("+" . $umsatz);
 
 855       if (   ( $datevautomatik || $taxkey)
 
 856           && (!$datevautomatik || ($datevautomatik && ($charttax ne $taxkey)))) {
 
 857 #         $kne_file->add_block("\x6C" . (!$datevautomatik ? $taxkey : "4"));
 
 858         $kne_file->add_block("\x6C${taxkey}");
 
 861       $kne_file->add_block($gegenkonto);
 
 862       $kne_file->add_block($belegfeld1);
 
 863       $kne_file->add_block($belegfeld2);
 
 864       $kne_file->add_block($datum);
 
 865       $kne_file->add_block($konto);
 
 866       $kne_file->add_block($buchungstext);
 
 867       $kne_file->add_block($waehrung . "\x79");
 
 870     my $mandantenendsumme = "x" . $kne_file->format_amount($umsatzsumme / 100.0, 14) . "\x79\x7a";
 
 872     $kne_file->add_block($mandantenendsumme);
 
 875     open(ED, ">", $ed_filename) or die "can't open outputfile: $!\n";
 
 876     print(ED $kne_file->get_data());
 
 879     $ed_versionset[$fileno] = &make_ed_versionset($header, $filename, $kne_file->get_block_count(), $fromto);
 
 883   #Make EV Verwaltungsdatei
 
 884   my $ev_header = &make_ev_header($form, $fileno);
 
 885   my $ev_filename = $export_path . $evfile;
 
 886   push(@filenames, $evfile);
 
 887   open(EV, ">", $ev_filename) or die "can't open outputfile: EV01\n";
 
 888   print(EV $ev_header);
 
 890   foreach my $file (@ed_versionset) {
 
 891     print(EV $ed_versionset[$file]);
 
 894   print qq|<br>Done. <br>
 
 897   $main::lxdebug->leave_sub();
 
 899   return { 'download_token' => get_download_token_for_path($export_path), 'filenames' => \@filenames };
 
 902 sub kne_stammdatenexport {
 
 903   $main::lxdebug->enter_sub();
 
 905   my ($myconfig, $form) = @_;
 
 906   $form->{abrechnungsnr} = "99";
 
 911   <body>Export in Bearbeitung<br>
 
 916   my $export_path = _get_export_path() . "/";
 
 917   my $filename    = "ED00000";
 
 923   my $remaining_bytes = 256;
 
 924   my $total_bytes     = 256;
 
 925   my $buchungssatz    = "";
 
 927   my $ed_filename = $export_path . $filename;
 
 928   push(@filenames, $filename);
 
 929   open(ED, ">", $ed_filename) or die "can't open outputfile: $!\n";
 
 930   my $header = &make_kne_data_header($myconfig, $form, "");
 
 931   $remaining_bytes -= length($header);
 
 936   # connect to database
 
 937   my $dbh = $form->dbconnect($myconfig);
 
 939   my (@where, @values) = ((), ());
 
 940   if ($form->{accnofrom}) {
 
 941     push @where, 'c.accno >= ?';
 
 942     push @values, $form->{accnofrom};
 
 944   if ($form->{accnoto}) {
 
 945     push @where, 'c.accno <= ?';
 
 946     push @values, $form->{accnoto};
 
 949   my $where_str = @where ? ' WHERE ' . join(' AND ', map { "($_)" } @where) : '';
 
 951   my $query     = qq|SELECT c.accno, c.description
 
 956   my $sth = $dbh->prepare($query);
 
 957   $sth->execute(@values) || $form->dberror($query);
 
 959   while (my $ref = $sth->fetchrow_hashref("NAME_lc")) {
 
 960     if (($remaining_bytes - length("t" . $ref->{'accno'})) <= 6) {
 
 961       $fuellzeichen = ($blockcount * 256 - length($buchungssatz . $header));
 
 962       $buchungssatz .= "\x00" x $fuellzeichen;
 
 964       $total_bytes = ($blockcount) * 256;
 
 966     $buchungssatz .= "t" . $ref->{'accno'};
 
 967     $remaining_bytes = $total_bytes - length($buchungssatz . $header);
 
 968     $ref->{'description'} =~ s/[^0-9A-Za-z\$\%\&\*\+\-\/]//g;
 
 969     $ref->{'description'} = substr($ref->{'description'}, 0, 40);
 
 970     $ref->{'description'} =~ s/\ *$//;
 
 973         ($remaining_bytes - length("\x1E" . $ref->{'description'} . "\x1C\x79")
 
 976       $fuellzeichen = ($blockcount * 256 - length($buchungssatz . $header));
 
 977       $buchungssatz .= "\x00" x $fuellzeichen;
 
 979       $total_bytes = ($blockcount) * 256;
 
 981     $buchungssatz .= "\x1E" . $ref->{'description'} . "\x1C\x79";
 
 982     $remaining_bytes = $total_bytes - length($buchungssatz . $header);
 
 987   print(ED $buchungssatz);
 
 988   $fuellzeichen = 256 - (length($header . $buchungssatz . "z") % 256);
 
 989   my $dateiende = "\x00" x $fuellzeichen;
 
 991   print(ED $dateiende);
 
 994   #Make EV Verwaltungsdatei
 
 996     &make_ed_versionset($header, $filename, $blockcount, $fromto);
 
 998   my $ev_header = &make_ev_header($form, $fileno);
 
 999   my $ev_filename = $export_path . $evfile;
 
1000   push(@filenames, $evfile);
 
1001   open(EV, ">", $ev_filename) or die "can't open outputfile: EV01\n";
 
1002   print(EV $ev_header);
 
1004   foreach my $file (@ed_versionset) {
 
1005     print(EV $ed_versionset[$file]);
 
1012   print qq|<br>Done. <br>
 
1015   $main::lxdebug->leave_sub();
 
1017   return { 'download_token' => get_download_token_for_path($export_path), 'filenames' => \@filenames };