Revision e20f3f0d
Von Moritz Bunkus vor mehr als 15 Jahren hinzugefügt
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
Diverse Bugfixes im DATEV-Export