1 package SL::DB::Helper::PriceTaxCalculator;
5 use parent qw(Exporter);
6 our @EXPORT = qw(calculate_prices_and_taxes);
9 use List::Util qw(sum);
10 use SL::DB::PriceFactor;
13 sub calculate_prices_and_taxes {
16 my $is_sales = $self->can('customer') && $self->customer;
18 my %units_by_name = map { ( $_->name => $_ ) } @{ SL::DB::Manager::Unit->get_all };
19 my %price_factors_by_id = map { ( $_->id => $_ ) } @{ SL::DB::Manager::PriceFactor->get_all };
20 my %taxes_by_chart_id = ();
23 $self->marge_total(0);
24 my $lastcost_total = 0;
28 foreach my $item ($self->items) {
31 my $part_unit = $units_by_name{ $item->part->unit };
32 my $item_unit = $units_by_name{ $item->unit };
34 croak("Undefined unit " . $item->part->unit) if !$part_unit;
35 croak("Undefined unit " . $item->unit) if !$item_unit;
37 $item->base_qty($item_unit->convert_to($item->qty, $part_unit));
39 my $num_dec = num_decimal_places($item->sellprice);
40 my $discount = round($item->sellprice * ($item->discount || 0), $num_dec);
41 my $sellprice = round($item->sellprice - $discount, $num_dec);
43 $item->price_factor( ! $item->price_factor_obj ? 1 : ($item->price_factor_obj->factor || 1));
44 $item->marge_price_factor(! $item->part->price_factor ? 1 : ($item->part->price_factor->factor || 1));
45 my $linetotal = round($sellprice * $item->qty / $item->price_factor, 2);
48 $item->marge_total( 0);
49 $item->marge_percent(0);
52 my $lastcost = ! ($item->lastcost * 1) ? ($item->part->lastcost || 0) : $item->lastcost;
54 $item->marge_total( $linetotal - $lastcost / $item->marge_price_factor);
55 $item->marge_percent($item->marge_total * 100 / $linetotal);
57 $self->marge_total( $self->marge_total + $item->marge_total);
58 $lastcost_total += $lastcost;
61 my $taxkey = $item->part->get_taxkey(date => $self->transdate, is_sales => $is_sales, taxzone => $self->taxzone_id);
62 my $tax_rate = $taxkey->tax->rate;
63 my $tax_amount = undef;
65 if ($self->taxincluded) {
66 $tax_amount = $linetotal * $tax_rate / ($tax_rate + 1);
67 $sellprice = $sellprice / ($tax_rate + 1);
70 $tax_amount = $linetotal * $tax_rate;
73 $taxes_by_chart_id{ $taxkey->chart_id } ||= 0;
74 $taxes_by_chart_id{ $taxkey->chart_id } += $tax_amount;
76 $self->netamount($self->netamount + $sellprice * $item->qty / $item->price_factor);
78 $::lxdebug->message(0, "CALCULATE! ${idx} i.qty " . $item->qty . " i.sellprice " . $item->sellprice . " sellprice $sellprice taxamount $tax_amount " .
79 "i.linetotal $linetotal netamount " . $self->netamount . " marge_total " . $item->marge_total . " marge_percent " . $item->marge_percent);
82 my $tax_sum = sum map { round($_, 2) } values %taxes_by_chart_id;
84 $self->amount( round($self->netamount + $tax_sum, 2));
85 $self->netamount( round($self->netamount, 2));
86 $self->marge_percent($self->netamount ? ($self->netamount - $lastcost_total) * 100 / $self->netamount : 0);
88 return $self unless wantarray;
89 return ( self => $self,
90 taxes => \%taxes_by_chart_id,
95 return $::form->round_amount(@_);
98 sub num_decimal_places {
99 return length( (split(/\./, '' . shift, 2))[1] || '' );
111 SL::DB::Helper::PriceTaxCalculator - Mixin for calculating the prices,
112 amounts and taxes of orders, quotations, invoices
118 =item C<calculate_prices_and_taxes %params>
120 Calculates the prices, amounts and taxes for an order, a quotation or
121 an invoice. The function assumes that the mixing package has a certain
122 layout and provides certain functions:
130 =item C<customer> or C<vendor>
132 Determines if the record is a sales or purchase record.
136 Accessor returning all line items for this record. The line items
137 themselves must again have a certain layout. Instances of
138 L<SL::DB::OrderItem> and L<SL::DB::InvoiceItem> are supported.
142 The following values are calculated and set for C<$self>: C<amount>,
143 C<netamount>, C<marge_percent>, C<marge_total>.
145 The following values are calculated and set for each line item:
146 C<base_qty>, C<price_factor>, C<marge_price_factor>, C<marge_total>,
149 The objects are not saved.
151 Returns C<$self> in scalar context.
153 In array context a hash with the following keys is returned:
163 A hash reference with the calculated taxes. The keys are chart IDs,
164 the values the calculated taxes.
176 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>