From: Moritz Bunkus Date: Tue, 9 Nov 2010 08:30:58 +0000 (+0100) Subject: Basisversion des Kalkulators für Beträge, Preise und Steuern X-Git-Tag: release-2.6.3~61^2~7^2~1^2~2^2~78 X-Git-Url: http://wagnertech.de/git?a=commitdiff_plain;h=4ff296dd32073025266104b2bb35644d9ec553d7;p=kivitendo-erp.git Basisversion des Kalkulators für Beträge, Preise und Steuern --- diff --git a/SL/DB/Helper/PriceTaxCalculator.pm b/SL/DB/Helper/PriceTaxCalculator.pm new file mode 100644 index 000000000..ac262f187 --- /dev/null +++ b/SL/DB/Helper/PriceTaxCalculator.pm @@ -0,0 +1,178 @@ +package SL::DB::Helper::PriceTaxCalculator; + +use strict; + +use parent qw(Exporter); +our @EXPORT = qw(calculate_prices_and_taxes); + +use Carp; +use List::Util qw(sum); +use SL::DB::PriceFactor; +use SL::DB::Unit; + +sub calculate_prices_and_taxes { + my ($self) = @_; + + my $is_sales = $self->can('customer') && $self->customer; + + my %units_by_name = map { ( $_->name => $_ ) } @{ SL::DB::Manager::Unit->get_all }; + my %price_factors_by_id = map { ( $_->id => $_ ) } @{ SL::DB::Manager::PriceFactor->get_all }; + my %taxes_by_chart_id = (); + + $self->netamount( 0); + $self->marge_total(0); + my $lastcost_total = 0; + + my $idx = 0; + + foreach my $item ($self->items) { + $idx++; + + my $part_unit = $units_by_name{ $item->part->unit }; + my $item_unit = $units_by_name{ $item->unit }; + + croak("Undefined unit " . $item->part->unit) if !$part_unit; + croak("Undefined unit " . $item->unit) if !$item_unit; + + $item->base_qty($item_unit->convert_to($item->qty, $part_unit)); + + my $num_dec = num_decimal_places($item->sellprice); + my $discount = round($item->sellprice * ($item->discount || 0), $num_dec); + my $sellprice = round($item->sellprice - $discount, $num_dec); + + $item->price_factor( ! $item->price_factor_obj ? 1 : ($item->price_factor_obj->factor || 1)); + $item->marge_price_factor(! $item->part->price_factor ? 1 : ($item->part->price_factor->factor || 1)); + my $linetotal = round($sellprice * $item->qty / $item->price_factor, 2); + + if (!$linetotal) { + $item->marge_total( 0); + $item->marge_percent(0); + + } else { + my $lastcost = ! ($item->lastcost * 1) ? ($item->part->lastcost || 0) : $item->lastcost; + + $item->marge_total( $linetotal - $lastcost / $item->marge_price_factor); + $item->marge_percent($item->marge_total * 100 / $linetotal); + + $self->marge_total( $self->marge_total + $item->marge_total); + $lastcost_total += $lastcost; + } + + my $taxkey = $item->part->get_taxkey(date => $self->transdate, is_sales => $is_sales, taxzone => $self->taxzone_id); + my $tax_rate = $taxkey->tax->rate; + my $tax_amount = undef; + + if ($self->taxincluded) { + $tax_amount = $linetotal * $tax_rate / ($tax_rate + 1); + $sellprice = $sellprice / ($tax_rate + 1); + + } else { + $tax_amount = $linetotal * $tax_rate; + } + + $taxes_by_chart_id{ $taxkey->chart_id } ||= 0; + $taxes_by_chart_id{ $taxkey->chart_id } += $tax_amount; + + $self->netamount($self->netamount + $sellprice * $item->qty / $item->price_factor); + + $::lxdebug->message(0, "CALCULATE! ${idx} i.qty " . $item->qty . " i.sellprice " . $item->sellprice . " sellprice $sellprice taxamount $tax_amount " . + "i.linetotal $linetotal netamount " . $self->netamount . " marge_total " . $item->marge_total . " marge_percent " . $item->marge_percent); + } + + my $tax_sum = sum map { round($_, 2) } values %taxes_by_chart_id; + + $self->amount( round($self->netamount + $tax_sum, 2)); + $self->netamount( round($self->netamount, 2)); + $self->marge_percent($self->netamount ? ($self->netamount - $lastcost_total) * 100 / $self->netamount : 0); + + return $self unless wantarray; + return ( self => $self, + taxes => \%taxes_by_chart_id, + ); +} + +sub round { + return $::form->round_amount(@_); +} + +sub num_decimal_places { + return length( (split(/\./, '' . shift, 2))[1] || '' ); +} + +1; +__END__ + +=pod + +=encoding utf8 + +=head1 NAME + +SL::DB::Helper::PriceTaxCalculator - Mixin for calculating the prices, +amounts and taxes of orders, quotations, invoices + +=head1 FUNCTIONS + +=over 4 + +=item C + +Calculates the prices, amounts and taxes for an order, a quotation or +an invoice. The function assumes that the mixing package has a certain +layout and provides certain functions: + +=over 2 + +=item C + +The record's date. + +=item C or C + +Determines if the record is a sales or purchase record. + +=item C + +Accessor returning all line items for this record. The line items +themselves must again have a certain layout. Instances of +L and L are supported. + +=back + +The following values are calculated and set for C<$self>: C, +C, C, C. + +The following values are calculated and set for each line item: +C, C, C, C, +C. + +The objects are not saved. + +Returns C<$self> in scalar context. + +In array context a hash with the following keys is returned: + +=over 2 + +=item C + +The object itself. + +=item C + +A hash reference with the calculated taxes. The keys are chart IDs, +the values the calculated taxes. + +=back + +=back + +=head1 BUGS + +Nothing here yet. + +=head1 AUTHOR + +Moritz Bunkus Em.bunkus@linet-services.deE + +=cut