Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision e20f3f0d

Von Moritz Bunkus vor mehr als 15 Jahren hinzugefügt

  • ID e20f3f0dac8c4b362af0e6594de9581d05d0f6c4
  • Vorgänger eb0c10b0
  • Nachfolger 450a0e2f

Diverse Bugfixes im DATEV-Export

  • Bessere Berechnung der Bruttobeträge aus den gespeicherten Nettobeträgen
  • Erkennen weiterer Sonderfälle
  • Bessere Konformität mit DATEV-KNE-Formatsbeschreibung
  • Umlaute werden auch bei Nicht-ISO-8859-Codierungen richtig ersetzt.
  • Die SQL-Queries haben fälschlicherweise gewisse Zeilen aus acc_trans mehrfach zurückgegeben.

Unterschiede anzeigen:

SL/DATEV.pm
26 26

  
27 27
package DATEV;
28 28

  
29
use List::Util qw(max);
30

  
29 31
use SL::DBUtils;
30 32
use SL::DATEV::KNEFile;
33
use SL::Taxkeys;
31 34

  
32 35
use Data::Dumper;
33 36
use File::Path;
......
301 304
  return ($fromto, $jahr);
302 305
}
303 306

  
307
sub _sign {
308
  my $value = shift;
309

  
310
  return $value < 0 ? -1
311
    :    $value > 0 ?  1
312
    :                  0;
313
}
314

  
304 315
sub _get_transactions {
305 316
  $main::lxdebug->enter_sub();
306 317

  
......
315 326

  
316 327
  $fromto      =~ s/transdate/ac\.transdate/g;
317 328

  
318
  my %taxes    =  selectall_as_map($form, $dbh, qq|SELECT id, rate FROM tax|, 'id', 'rate');
329
  my $taxkeys  = Taxkeys->new();
330
  my $filter   = '';            # Useful for debugging purposes
331

  
332
  my %all_taxchart_ids = selectall_as_map($form, $dbh, qq|SELECT DISTINCT chart_id, TRUE AS is_set FROM tax|, 'chart_id', 'is_set');
319 333

  
320 334
  my $query    =
321 335
    qq|SELECT ac.oid, ac.transdate, ac.trans_id,ar.id, ac.amount, ac.taxkey,
322 336
         ar.invnumber, ar.duedate, ar.amount as umsatz,
323 337
         ct.name,
324
         c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id,
325
         t.chart_id, t.rate, t.id AS taxid, t.taxkey AS taxtaxkey
326
       FROM acc_trans ac,ar ar, customer ct, chart c
327
       LEFT JOIN tax t ON (t.chart_id = c.id)
328
       WHERE $fromto
329
         AND (ac.trans_id = ar.id)
330
         AND (ac.trans_id = ar.id)
331
         AND (ar.customer_id = ct.id)
332
         AND (ac.chart_id = c.id)
338
         c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id, c.link,
339
         ar.invoice
340
       FROM acc_trans ac
341
       LEFT JOIN ar          ON (ac.trans_id    = ar.id)
342
       LEFT JOIN customer ct ON (ar.customer_id = ct.id)
343
       LEFT JOIN chart c     ON (ac.chart_id    = c.id)
344
       WHERE (ar.id IS NOT NULL)
345
         AND $fromto
346
         $filter
333 347

  
334 348
       UNION ALL
335 349

  
336 350
       SELECT ac.oid, ac.transdate, ac.trans_id,ap.id, ac.amount, ac.taxkey,
337 351
         ap.invnumber, ap.duedate, ap.amount as umsatz,
338 352
         ct.name,
339
         c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id,
340
         t.chart_id, t.rate, t.id AS taxid, t.taxkey AS taxtaxkey
341
       FROM acc_trans ac, ap ap, vendor ct, chart c
342
       LEFT JOIN tax t ON (t.chart_id = c.id)
343
       WHERE $fromto
344
         AND (ac.trans_id = ap.id)
345
         AND (ap.vendor_id = ct.id)
346
         AND (ac.chart_id = c.id)
353
         c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id, c.link,
354
         ap.invoice
355
       FROM acc_trans ac
356
       LEFT JOIN ap        ON (ac.trans_id  = ap.id)
357
       LEFT JOIN vendor ct ON (ap.vendor_id = ct.id)
358
       LEFT JOIN chart c   ON (ac.chart_id  = c.id)
359
       WHERE (ap.id IS NOT NULL)
360
         AND $fromto
361
         $filter
347 362

  
348 363
       UNION ALL
349 364

  
350 365
       SELECT ac.oid, ac.transdate, ac.trans_id,gl.id, ac.amount, ac.taxkey,
351 366
         gl.reference AS invnumber, gl.transdate AS duedate, ac.amount as umsatz,
352 367
         gl.description AS name,
353
         c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id,
354
         t.chart_id, t.rate, t.id AS taxid, t.taxkey AS taxtaxkey
355
       FROM acc_trans ac, gl gl, chart c
356
       LEFT JOIN tax t ON (t.chart_id = c.id)
357
       WHERE $fromto
358
         AND (ac.trans_id = gl.id)
359
         AND (ac.chart_id = c.id)
368
         c.accno, c.taxkey_id as charttax, c.datevautomatik, c.id, c.link,
369
         FALSE AS invoice
370
       FROM acc_trans ac
371
       LEFT JOIN gl      ON (ac.trans_id  = gl.id)
372
       LEFT JOIN chart c ON (ac.chart_id  = c.id)
373
       WHERE (gl.id IS NOT NULL)
374
         AND $fromto
375
         $filter
360 376

  
361 377
       ORDER BY trans_id, oid|;
362 378

  
363
  my $sth     = prepare_execute_query($form, $dbh, $query);
379
  my $sth = prepare_execute_query($form, $dbh, $query);
364 380

  
365
  my @splits;
366 381
  my $counter = 0;
367 382
  while (my $ref = $sth->fetchrow_hashref(NAME_lc)) {
368 383
    $counter++;
......
379 394
      my $ref2 = $sth->fetchrow_hashref(NAME_lc);
380 395
      last unless ($ref2);
381 396

  
397
      if ($ref2->{trans_id} != $trans->[0]->{trans_id}) {
398
        $form->error("Unbalanced ledger! old trans_id " . $trans->[0]->{trans_id} . " new trans_id " . $ref2->{trans_id} . " count $count");
399
        exit 1;
400
      }
401

  
382 402
      push @{ $trans }, $ref2;
383 403

  
384 404
      $count    += $ref2->{amount};
385 405
      $firstrun  = 0;
386 406
    }
387 407

  
408
    foreach my $i (0 .. scalar(@{ $trans }) - 1) {
409
      my $ref        = $trans->[$i];
410
      my $prev_ref   = 0 < $i ? $trans->[$i - 1] : undef;
411
      if (   $all_taxchart_ids{$ref->{id}}
412
          && ($ref->{link} =~ m/(?:AP_tax|AR_tax)/)
413
          && (   ($prev_ref && $prev_ref->{taxkey} && (_sign($ref->{amount}) == _sign($prev_ref->{amount})))
414
              || $ref->{invoice})) {
415
        $ref->{is_tax} = 1;
416
      }
417

  
418
      if (   !$ref->{invoice}
419
          &&  $ref->{is_tax}
420
          && !($prev_ref->{is_tax})
421
          &&  (_sign($ref->{amount}) == _sign($prev_ref->{amount}))) {
422
        $trans->[$i - 1]->{tax_amount} = $ref->{amount};
423
      }
424
    }
425

  
388 426
    my %taxid_taxkeys = ();
389 427
    my $absumsatz     = 0;
390 428
    if (scalar(@{$trans}) <= 2) {
......
397 435
        $absumsatz     = $trans->[$j]->{'amount'};
398 436
        $notsplitindex = $j;
399 437
      }
400
      if (($trans->[$j]->{'taxtaxkey'}) && ($trans->[$j]->{'taxid'})) {
401
        $taxid_taxkeys{$trans->[$j]->{'taxtaxkey'}}     = $trans->[$j]->{'taxid'};
402
      }
403 438
    }
404 439

  
405
    my $ml = ($trans->[0]->{'umsatz'} > 0) ? 1 : -1;
440
    my $ml             = ($trans->[0]->{'umsatz'} > 0) ? 1 : -1;
441
    my $rounding_error = 0;
442

  
406 443
    for my $j (0 .. (scalar(@{$trans}) - 1)) {
407 444
      if (   ($j != $notsplitindex)
408
          && ($trans->[$j]->{'chart_id'}  eq "")
445
          && !$trans->[$j]->{is_tax}
409 446
          && (   $trans->[$j]->{'taxkey'} eq ""
410 447
              || $trans->[$j]->{'taxkey'} eq "0"
411 448
              || $trans->[$j]->{'taxkey'} eq "1"
......
419 456
        $new_trans{'umsatz'}      = abs($trans->[$j]->{'amount'}) * $ml;
420 457
        $trans->[$j]->{'umsatz'}  = abs($trans->[$j]->{'amount'}) * $ml;
421 458

  
422
        push @splits, [ \%new_trans, $trans->[$j] ];
423
        push @{ $form->{DATEV} }, $splits[-1];
459
        push @{ $form->{DATEV} }, [ \%new_trans, $trans->[$j] ];
424 460

  
425
      } elsif (($j != $notsplitindex) && ($trans->[$j]->{'chart_id'} eq "")) {
426
        $absumsatz += ($trans->[$j]->{'amount'} * (1 + $taxes{ $taxid_taxkeys{$trans->[$j]->{'taxkey'}} }));
461
      } elsif (($j != $notsplitindex) && !$trans->[$j]->{is_tax}) {
462
        my %tax_info = $taxkeys->get_full_tax_info('transdate' => $trans->[$j]->{transdate});
427 463

  
428 464
        my %new_trans = ();
429 465
        map { $new_trans{$_} = $trans->[$notsplitindex]->{$_}; } keys %{ $trans->[$notsplitindex] };
430 466

  
431
        my $tax_rate             = 1 + $taxes{ $taxid_taxkeys{$trans->[$j]->{'taxkey'}} };
432
        $new_trans{'amount'}     = $form->round_amount(($trans->[$j]->{'amount'} * $tax_rate * -1), 2);
433
        $new_trans{'umsatz'}     = abs($form->round_amount(($trans->[$j]->{'amount'} * $tax_rate), 2)) * $ml;
434
        $trans->[$j]->{'umsatz'} = abs($form->round_amount(($trans->[$j]->{'amount'} * $tax_rate), 2)) * $ml;
467
        my $tax_rate              = $tax_info{taxkeys}->{ $trans->[$j]->{'taxkey'} }->{taxrate};
468
        $new_trans{'net_amount'}  = $trans->[$j]->{'amount'} * -1;
469
        $new_trans{'tax_rate'}    = 1 + $tax_rate;
435 470

  
436
        push @splits, [ \%new_trans, $trans->[$j] ];
437
        push @{ $form->{DATEV} }, $splits[-1];
471
        if (!$trans->[$j]->{'invoice'}) {
472
          $new_trans{'amount'}      = $form->round_amount(-1 * ($trans->[$j]->{amount} + $trans->[$j]->{tax_amount}), 2);
473
          $new_trans{'umsatz'}      = abs($new_trans{'amount'}) * $ml;
474
          $trans->[$j]->{'umsatz'}  = $new_trans{'umsatz'};
475
          $absumsatz               += -1 * $new_trans{'amount'};
476

  
477
        } else {
478
          my $unrounded             = $trans->[$j]->{'amount'} * (1 + $tax_rate) * -1; # + $rounding_error;
479
          my $rounded               = $form->round_amount($unrounded, 2);
480
          $rounding_error          += $unrounded - $rounded;
481
          $new_trans{'amount'}      = $rounded;
482
          $new_trans{'umsatz'}      = abs($form->round_amount(($trans->[$j]->{'amount'} * (1 + $tax_rate)), 2)) * $ml;
483
          $trans->[$j]->{'umsatz'}  = abs($form->round_amount(($trans->[$j]->{'amount'} * (1 + $tax_rate)), 2)) * $ml;
484
          $absumsatz               += $form->round_amount($trans->[$j]->{'amount'} + $trans->[$j]->{'amount'} * $tax_rate, 2);
485
        }
486

  
487
        push @{ $form->{DATEV} }, [ \%new_trans, $trans->[$j] ];
488
      }
489
    }
490

  
491
    my $idx        = 0;
492
    my $correction = 0;
493
    while (abs($absumsatz) >= 0.01) {
494
      if ($idx >= scalar @taxed) {
495
        last if (!$correction);
496

  
497
        $correction = 0;
498
        $idx        = 0;
438 499
      }
500

  
501
      my $transaction = $taxed[$idx]->[0];
502

  
503
      my $old_amount     = $transaction->{amount};
504
      my $old_correction = $correction;
505
      my @possible_diffs;
506

  
507
      if (!$transaction->{diff}) {
508
        @possible_diffs = (0.01, -0.01);
509
      } else {
510
        @possible_diffs = ($transaction->{diff});
511
      }
512

  
513
      foreach my $diff (@possible_diffs) {
514
        my $net_amount = $form->round_amount(($transaction->{amount} + $diff) / $transaction->{tax_rate}, 2);
515
        next if ($net_amount != $transaction->{net_amount});
516

  
517
        $transaction->{diff}    = $diff;
518
        $transaction->{amount} += $diff;
519
        $transaction->{umsatz} += $diff;
520
        $absumsatz             -= $diff;
521
        $correction             = 1;
522

  
523
        last;
524
      }
525

  
526
      $idx++;
439 527
    }
440 528

  
441
    if (abs($absumsatz) > 0.01) {
442
      push @errors, "Datev-Export fehlgeschlagen! Bei Transaktion $trans->[0]->{trans_id} ($absumsatz)\n";
529
    if (abs($absumsatz) >= 0.01) {
530
      push @errors, "Datev-Export fehlgeschlagen! Bei Transaktion $trans->[0]->{trans_id} ($absumsatz, Rundungsfehler $rounding_error)\n";
443 531
    }
444 532
  }
445 533

  
......
463 551

  
464 552
  #Header
465 553
  my $header  = "\x1D\x181";
466
  $header    .= _fill($form->{datentraegernr}, 3, '0');
554
  $header    .= _fill($form->{datentraegernr}, 3, ' ', 'left');
467 555
  $header    .= ($fromto) ? "11" : "13"; # Anwendungsnummer
468 556
  $header    .= _fill($form->{dfvkz}, 2, '0');
469 557
  $header    .= _fill($form->{beraternr}, 7, '0');
......
669 757
      my $datevautomatik = 0;
670 758
      my $taxkey         = 0;
671 759
      my $charttax       = 0;
672
      my %umlaute = ('?' => 'ae',
673
                     '?' => 'oe',
674
                     '?' => 'ue',
675
                     '?' => 'Ae',
676
                     '?' => 'Oe',
677
                     '?' => 'Ue',
678
                     '?' => 'sz');
760
      my $iconv          = $main::locale->{iconv_iso8859};
761
      my %umlaute = ($iconv->convert('?') => 'ae',
762
                     $iconv->convert('?') => 'oe',
763
                     $iconv->convert('?') => 'ue',
764
                     $iconv->convert('?') => 'Ae',
765
                     $iconv->convert('?') => 'Oe',
766
                     $iconv->convert('?') => 'Ue',
767
                     $iconv->convert('?') => 'sz');
679 768
      for (my $i = 0; $i < $trans_lines; $i++) {
680 769
        if ($trans_lines == 2) {
681 770
          if (abs($transaction->[$i]->{'amount'}) > abs($umsatz)) {
......
695 784
        if ($transaction->[$i]->{'charttax'}) {
696 785
          $charttax = $transaction->[$i]->{'charttax'};
697 786
        }
698
        if (   ($transaction->[$i]->{'id'} eq $transaction->[$i]->{'chart_id'})
699
            && ($trans_lines > 2)) {
700
          undef($transaction->[$i]);
701
        } elsif ($transaction->[$i]->{'amount'} > 0) {
787
        if ($transaction->[$i]->{'amount'} > 0) {
702 788
          $haben = $i;
703 789
        } else {
704 790
          $soll = $i;
......
743 829

  
744 830
      if (   ( $datevautomatik || $taxkey)
745 831
          && (!$datevautomatik || ($datevautomatik && ($charttax ne $taxkey)))) {
746
        $kne_file->add_block("\x6C" . (!$datevautomatik ? $taxkey : "4"));
832
#         $kne_file->add_block("\x6C" . (!$datevautomatik ? $taxkey : "4"));
833
        $kne_file->add_block("\x6C${taxkey}");
747 834
      }
748 835

  
749 836
      $kne_file->add_block($gegenkonto);

Auch abrufbar als: Unified diff