Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 0e68056c

Von Kivitendo Admin vor etwa 10 Jahren hinzugefügt

  • ID 0e68056cbc17b531266c16454f3a74ae1e57dea6
  • Vorgänger 71233175
  • Nachfolger 3270e883

Rundung bei Debitorenbuchung, Kreditorenbuchung und Dialogbuchung

Zwei neue Hilfsfunktionen für Form eingeführt die von ap/ar/gl genutzt
werden:

  • calculate_tax wird zur Berechnung der Steuer bei
    • update in ar, ap und gl
    • post_transaction in AR.pm und AP.pm innerhalb von calculate_arap
  • calculate_arap berechnet netamount, amount und totaltax anhand einer
    vorhandenen $form in ar/ap, und formatiert und rundet die amount_$i and
    tax_$i in der $form.

Das Ziel war es, daß diese drei Belege mit den gleichen Formeln zur
Steuerberechnung bei Steuer inkl./exkl. arbeiten. Ein Vorteil im
Vergleich zur Einkaufs- und Verkaufsrechnung ist, daß es hier keine
Positionen/Mengen/Rabatte/etc gibt, sondern direkt Werte auf Konten
bebucht werden, man muß nur Währungskurse berücksichtigen und beim
Schreiben in die Datenbank auf die Vorzeichen achten. Einkaufs- und
Verkaufsrechnungen werden hiervon nicht beeinflußt.

Vor allem bei der Einstellung "MwSt. inkl" wird die Steuer nun immer auf
zwei Stellen gerundet in die Datenbank geschrieben, und nicht (wie
bisher in manchen Fällen) mit 5 Nachkommastellen, was zu mehreren Bugs
geführt hat.

Es gibt einen neuen Test t/form/arap.t der die Berechnungen dieser
beiden Funktionen testet, nicht aber das eigentliche Buchen der Belege.

Mit diesem Commit sollten für zukünftige Buchungen folgende Bugs behoben
sein:

1691 - Rundung bei Berichten bei Buchungen mit MwSt inkl.
2029 - Rundungsfehler bei Dialogbuchung
2033 - Unterschiede in Rundungen durch taxincluded
2094 - Rundungsprobleme in Kreditorenbuchungen: Cent "kippt" bei Zahlungseinbuchung
2435 - Rundungsfehler in Kreditorenbuchungen (Netto vs. Brutto)

Dieser Commit greift in die Eingeweide von Uraltcode ein, wo die
diversen Stellen alle mal mit copy&paste entstanden und dann langsam
divergiert sind, hat also hohes Fehlerpotential. Es lohnt sich, den
DATEV-Check anzuschalten.

Unterschiede anzeigen:

SL/Form.pm
79 79
use URI;
80 80
use List::Util qw(first max min sum);
81 81
use List::MoreUtils qw(all any apply);
82
use SL::DB::Tax;
82 83

  
83 84
use strict;
84 85

  
......
3446 3447
  return $self;
3447 3448
}
3448 3449

  
3450
sub calculate_arap {
3451
  my ($self,$buysell,$taxincluded,$exchangerate,$roundplaces) = @_;
3452

  
3453
  # this function is used to calculate netamount, total_tax and amount for AP and
3454
  # AR transactions (Kreditoren-/Debitorenbuchungen) by going over all lines
3455
  # (1..$rowcount)
3456
  # Thus it needs a fully prepared $form to work on.
3457
  # calculate_arap assumes $form->{amount_$i} entries still need to be parsed
3458

  
3459
  # The calculated total values are all rounded (default is to 2 places) and
3460
  # returned as parameters rather than directly modifying form.  The aim is to
3461
  # make the calculation of AP and AR behave identically.  There is a test-case
3462
  # for this function in t/form/arap.t
3463

  
3464
  # While calculating the totals $form->{amount_$i} and $form->{tax_$i} are
3465
  # modified and formatted and receive the correct sign for writing straight to
3466
  # acc_trans, depending on whether they are ar or ap.
3467

  
3468
  # check parameters
3469
  die "taxincluded needed in Form->calculate_arap" unless defined $taxincluded;
3470
  die "exchangerate needed in Form->calculate_arap" unless defined $exchangerate;
3471
  die 'illegal buysell parameter, has to be \"buy\" or \"sell\" in Form->calculate_arap\n' unless $buysell =~ /^(buy|sell)$/;
3472
  $roundplaces = 2 unless $roundplaces;
3473

  
3474
  my $sign = 1;  # adjust final results for writing amount to acc_trans
3475
  $sign = -1 if $buysell eq 'buy';
3476

  
3477
  my ($netamount,$total_tax,$amount);
3478

  
3479
  my $tax;
3480

  
3481
  # parse and round amounts, setting correct sign for writing to acc_trans
3482
  for my $i (1 .. $self->{rowcount}) {
3483
    $self->{"amount_$i"} = $self->round_amount($self->parse_amount(\%::myconfig, $self->{"amount_$i"}) * $exchangerate * $sign, $roundplaces);
3484

  
3485
    $amount += $self->{"amount_$i"} * $sign;
3486
  }
3487

  
3488
  for my $i (1 .. $self->{rowcount}) {
3489
    next unless $self->{"amount_$i"};
3490
    ($self->{"tax_id_$i"}) = split /--/, $self->{"taxchart_$i"};
3491
    my $tax_id = $self->{"tax_id_$i"};
3492

  
3493
    my $selected_tax = SL::DB::Manager::Tax->find_by(id => "$tax_id");
3494

  
3495
    if ( $selected_tax ) {
3496

  
3497
      if ( $buysell eq 'sell' ) {
3498
        $self->{AR_amounts}{"tax_$i"} = $selected_tax->chart->accno unless $selected_tax->taxkey == 0;
3499
      } else {
3500
        $self->{AP_amounts}{"tax_$i"} = $selected_tax->chart->accno unless $selected_tax->taxkey == 0;
3501
      };
3502

  
3503
      $self->{"taxkey_$i"} = $selected_tax->taxkey;
3504
      $self->{"taxrate_$i"} = $selected_tax->rate;
3505
    };
3506

  
3507
    ($self->{"amount_$i"}, $self->{"tax_$i"}) = $self->calculate_tax($self->{"amount_$i"},$self->{"taxrate_$i"},$taxincluded,$roundplaces);
3508

  
3509
    $netamount  += $self->{"amount_$i"};
3510
    $total_tax  += $self->{"tax_$i"};
3511

  
3512
  }
3513
  $amount = $netamount + $total_tax;
3514

  
3515
  # due to $sign amount_$i und tax_$i already have the right sign for acc_trans
3516
  # but reverse sign of totals for writing amounts to ar
3517
  if ( $buysell eq 'buy' ) {
3518
    $netamount *= -1;
3519
    $amount    *= -1;
3520
    $total_tax *= -1;
3521
  };
3522

  
3523
  return($netamount,$total_tax,$amount);
3524
}
3525

  
3449 3526
sub format_dates {
3450 3527
  my ($self, $dateformat, $longformat, @indices) = @_;
3451 3528

  
......
3558 3635
  return $layout;
3559 3636
}
3560 3637

  
3638
sub calculate_tax {
3639
  # this function calculates the net amount and tax for the lines in ar, ap and
3640
  # gl and is used for update as well as post. When used with update the return
3641
  # value of amount isn't needed
3642

  
3643
  # calculate_tax should always work with positive values, or rather as the user inputs them
3644
  # calculate_tax uses db/perl numberformat, i.e. parsed numbers
3645
  # convert to negative numbers (when necessary) only when writing to acc_trans
3646
  # the amount from $form for ap/ar/gl is currently always rounded to 2 decimals before it reaches here
3647
  # for post_transaction amount already contains exchangerate and correct sign and is rounded
3648
  # calculate_tax doesn't (need to) know anything about exchangerate
3649

  
3650
  my ($self,$amount,$taxrate,$taxincluded,$roundplaces) = @_;
3651

  
3652
  $roundplaces = 2 unless defined $roundplaces;
3653

  
3654
  my $tax;
3655

  
3656
  if ($taxincluded *= 1) {
3657
    # calculate tax (unrounded), subtract from amount, round amount and round tax
3658
    $tax       = $amount - ($amount / ($taxrate + 1)); # equivalent to: taxrate * amount / (taxrate + 1)
3659
    $amount    = $self->round_amount($amount - $tax, $roundplaces);
3660
    $tax       = $self->round_amount($tax, $roundplaces);
3661
  } else {
3662
    $tax       = $amount * $taxrate;
3663
    $tax       = $self->round_amount($tax, $roundplaces);
3664
  }
3665

  
3666
  $tax = 0 unless $tax;
3667

  
3668
  return ($amount,$tax);
3669
};
3670

  
3561 3671
1;
3562 3672

  
3563 3673
__END__

Auch abrufbar als: Unified diff