Revision f16aaa2a
Von Moritz Bunkus vor fast 14 Jahren hinzugefügt
SL/DB/Helper/PriceTaxCalculator.pm | ||
---|---|---|
19 | 19 |
|
20 | 20 |
my %data = ( lastcost_total => 0, |
21 | 21 |
invoicediff => 0, |
22 |
last_incex_chart_id => undef, |
|
22 | 23 |
units_by_name => \%units_by_name, |
23 | 24 |
price_factors_by_id => \%price_factors_by_id, |
24 | 25 |
taxes => { }, |
... | ... | |
42 | 43 |
_calculate_item($self, $item, $idx, \%data, %params); |
43 | 44 |
} |
44 | 45 |
|
45 |
my $tax_sum = sum map { _round($_, 2) } values %{ $data{taxes} }; |
|
46 |
|
|
47 |
$self->amount( _round($self->netamount + $tax_sum, 2)); |
|
48 |
$self->netamount( _round($self->netamount, 2)); |
|
49 |
$self->marge_percent($self->netamount ? ($self->netamount - $data{lastcost_total}) * 100 / $self->netamount : 0); |
|
50 |
|
|
51 |
_calculate_invoice($self, \%data, %params) if $data{is_invoice}; |
|
46 |
_calculate_amounts($self, \%data, %params); |
|
52 | 47 |
|
53 | 48 |
return $self unless wantarray; |
54 | 49 |
|
... | ... | |
85 | 80 |
my $linetotal = _round($sellprice * $item->qty / $item->price_factor, 2) * $data->{exchangerate}; |
86 | 81 |
$linetotal = _round($linetotal, 2); |
87 | 82 |
|
88 |
$data->{invoicediff} += $sellprice * $item->qty * $data->{exchangerate} / $item->price_factor - $linetotal; |
|
83 |
$data->{invoicediff} += $sellprice * $item->qty * $data->{exchangerate} / $item->price_factor - $linetotal if $self->taxincluded;
|
|
89 | 84 |
|
90 | 85 |
if (!$linetotal) { |
91 | 86 |
$item->marge_total( 0); |
... | ... | |
119 | 114 |
$self->netamount($self->netamount + $sellprice * $item->qty / $item->price_factor); |
120 | 115 |
|
121 | 116 |
my $chart = $item->part->get_chart(type => $data->{is_sales} ? 'income' : 'expense', taxzone => $self->taxzone_id); |
122 |
$data->{amounts}->{$chart->id} += $linetotal; |
|
117 |
$data->{amounts}->{ $chart->id } ||= { taxkey => $taxkey->id, amount => 0 }; |
|
118 |
$data->{amounts}->{ $chart->id }->{amount} += $linetotal; |
|
123 | 119 |
|
124 | 120 |
push @{ $data->{assembly_items} }, []; |
125 |
$item->allocated(_calculate_assembly_item($self, $data, $item->part, $item->base_qty, $item->unit_obj->convert_to(1, $item->part->unit_obj))); |
|
121 |
if ($item->part->is_assembly) { |
|
122 |
_calculate_assembly_item($self, $data, $item->part, $item->base_qty, $item->unit_obj->convert_to(1, $item->part->unit_obj)); |
|
123 |
} elsif ($item->part->is_part) { |
|
124 |
$item->allocated(_calculate_part_item($self, $data, $item->part, $item->base_qty, $item->unit_obj->convert_to(1, $item->part->unit_obj))); |
|
125 |
} |
|
126 |
|
|
127 |
$data->{last_incex_chart_id} = $chart->id if $data->{is_sales}; |
|
126 | 128 |
|
127 |
$::lxdebug->message(0, "CALCULATE! ${idx} i.qty " . $item->qty . " i.sellprice " . $item->sellprice . " sellprice $sellprice taxamount $tax_amount " .
|
|
128 |
"i.linetotal $linetotal netamount " . $self->netamount . " marge_total " . $item->marge_total . " marge_percent " . $item->marge_percent);
|
|
129 |
_dbg("CALCULATE! ${idx} i.qty " . $item->qty . " i.sellprice " . $item->sellprice . " sellprice $sellprice num_dec $num_dec taxamount $tax_amount " .
|
|
130 |
"i.linetotal $linetotal netamount " . $self->netamount . " marge_total " . $item->marge_total . " marge_percent " . $item->marge_percent); |
|
129 | 131 |
} |
130 | 132 |
|
131 |
sub _calculate_invoice {
|
|
133 |
sub _calculate_amounts {
|
|
132 | 134 |
my ($self, $data, %params) = @_; |
133 | 135 |
|
134 |
return unless $data->{invoice}; |
|
136 |
my $tax_diff = 0; |
|
137 |
foreach my $chart_id (keys %{ $data->{taxes} }) { |
|
138 |
my $rounded = _round($data->{taxes}->{$chart_id} * $data->{exchangerate}, 2); |
|
139 |
$tax_diff += $data->{taxes}->{$chart_id} * $data->{exchangerate} - $rounded if $self->taxincluded; |
|
140 |
$data->{taxes}->{$chart_id} = $rounded; |
|
141 |
} |
|
142 |
|
|
143 |
my $amount = _round(($self->netamount + $tax_diff) * $data->{exchangerate}, 2); |
|
144 |
my $diff = $amount - ($self->netamount + $tax_diff) * $data->{exchangerate}; |
|
145 |
my $netamount = $amount; |
|
146 |
|
|
147 |
if ($self->taxincluded) { |
|
148 |
$data->{invoicediff} += $diff; |
|
149 |
$data->{amounts}->{ $data->{last_incex_chart_id} }->{amount} += $data->{invoicediff} if $data->{last_incex_chart_id}; |
|
150 |
} |
|
151 |
|
|
152 |
_dbg("Sna " . $self->netamount . " idiff " . $data->{invoicediff} . " tdiff ${tax_diff}"); |
|
153 |
|
|
154 |
my $tax = sum values %{ $data->{taxes} }; |
|
155 |
$data->{arap_amount} = $netamount + $tax; |
|
156 |
|
|
157 |
$self->netamount( $netamount); |
|
158 |
$self->amount( $netamount + $tax); |
|
159 |
$self->marge_percent($self->netamount ? ($self->netamount - $data->{lastcost_total}) * 100 / $self->netamount : 0); |
|
135 | 160 |
} |
136 | 161 |
|
137 | 162 |
sub _calculate_assembly_item { |
138 | 163 |
my ($self, $data, $part, $total_qty, $base_factor) = @_; |
139 | 164 |
|
140 | 165 |
return 0 unless $::eur && $data->{is_invoice}; |
141 |
return _calculate_part_service_item($self, $data, $part, $total_qty) unless $part->is_assembly; |
|
142 | 166 |
|
143 |
my $allocated = 0; |
|
144 | 167 |
foreach my $assembly_entry (@{ $part->assemblies }) { |
145 |
push @{ $data->{assembly_items}->[-1] }, { part => $assembly_entry->part, qty => $total_qty * $assembly_entry->qty }; |
|
146 |
$allocated += _calculate_assembly_item($self, $data, $assembly_entry->part, $total_qty * $assembly_entry->qty); |
|
168 |
push @{ $data->{assembly_items}->[-1] }, { part => $assembly_entry->part, |
|
169 |
qty => $total_qty * $assembly_entry->qty, |
|
170 |
allocated => 0 }; |
|
171 |
|
|
172 |
if ($assembly_entry->part->is_assembly) { |
|
173 |
_calculate_assembly_item($self, $data, $assembly_entry->part, $total_qty * $assembly_entry->qty); |
|
174 |
} elsif ($assembly_entry->part->is_part) { |
|
175 |
my $allocated = _calculate_part_item($self, $data, $assembly_entry->part, $total_qty * $assembly_entry->qty); |
|
176 |
$data->{assembly_items}->[-1]->[-1]->{allocated} = $allocated; |
|
177 |
} |
|
147 | 178 |
} |
148 |
|
|
149 |
return $allocated; |
|
150 | 179 |
} |
151 | 180 |
|
152 |
sub _calculate_part_service_item {
|
|
181 |
sub _calculate_part_item { |
|
153 | 182 |
my ($self, $data, $part, $total_qty, $base_factor) = @_; |
154 | 183 |
|
155 |
$::lxdebug->message(0, "cpsi tq " . $total_qty);
|
|
184 |
_dbg("cpsi tq " . $total_qty);
|
|
156 | 185 |
|
157 | 186 |
return 0 unless $::eur && $data->{is_invoice} && $total_qty; |
158 | 187 |
|
... | ... | |
166 | 195 |
\'(base_qty + allocated) < 0' ] ]); |
167 | 196 |
|
168 | 197 |
while (($remaining_qty > 0) && ($entry = $iterator->next)) { |
169 |
my $qty = min($remaining_qty, $entry->base_qty * -1 - $entry->allocated - $data->{allocated}->{$part->id});
|
|
170 |
$::lxdebug->message(0, "qty $qty");
|
|
198 |
my $qty = min($remaining_qty, $entry->base_qty * -1 - $entry->allocated - $data->{allocated}->{ $entry->id });
|
|
199 |
_dbg("qty $qty");
|
|
171 | 200 |
|
172 | 201 |
next unless $qty; |
173 | 202 |
|
... | ... | |
176 | 205 |
$data->{amounts_cogs}->{ $expense_income_chart->id } -= $linetotal; |
177 | 206 |
$data->{amounts_cogs}->{ $inventory_chart->id } += $linetotal; |
178 | 207 |
|
179 |
$data->{allocated}->{ $part->id } ||= 0;
|
|
180 |
$data->{allocated}->{ $part->id } += $qty;
|
|
181 |
$remaining_qty -= $qty; |
|
208 |
$data->{allocated}->{ $entry->id } ||= 0;
|
|
209 |
$data->{allocated}->{ $entry->id } += $qty;
|
|
210 |
$remaining_qty -= $qty;
|
|
182 | 211 |
} |
183 | 212 |
|
184 | 213 |
$iterator->finish; |
... | ... | |
191 | 220 |
} |
192 | 221 |
|
193 | 222 |
sub _num_decimal_places { |
194 |
return length( (split(/\./, '' . shift, 2))[1] || '' ); |
|
223 |
return length( (split(/\./, '' . ($_[0] * 1), 2))[1] || '' ); |
|
224 |
} |
|
225 |
|
|
226 |
sub _dbg { |
|
227 |
$::lxdebug->message(0, join(' ', @_)); |
|
195 | 228 |
} |
196 | 229 |
|
197 | 230 |
1; |
... | ... | |
213 | 246 |
=item C<calculate_prices_and_taxes %params> |
214 | 247 |
|
215 | 248 |
Calculates the prices, amounts and taxes for an order, a quotation or |
216 |
an invoice. The function assumes that the mixing package has a certain |
|
217 |
layout and provides certain functions: |
|
249 |
an invoice. |
|
250 |
|
|
251 |
The function assumes that the mixing package has a certain layout and |
|
252 |
provides certain functions: |
|
218 | 253 |
|
219 | 254 |
=over 2 |
220 | 255 |
|
... | ... | |
249 | 284 |
|
250 | 285 |
=over 2 |
251 | 286 |
|
252 |
=item C<self> |
|
253 |
|
|
254 |
The object itself. |
|
255 |
|
|
256 | 287 |
=item C<taxes> |
257 | 288 |
|
258 | 289 |
A hash reference with the calculated taxes. The keys are chart IDs, |
... | ... | |
261 | 292 |
=item C<amounts> |
262 | 293 |
|
263 | 294 |
A hash reference with the calculated amounts. The keys are chart IDs, |
264 |
the values the calculated amounts. |
|
295 |
the values are hash references containing the two keys C<amount> and |
|
296 |
C<taxkey>. |
|
297 |
|
|
298 |
=item C<amounts_cogs> |
|
299 |
|
|
300 |
A hash reference with the calculated amounts for costs of goods |
|
301 |
sold. The keys are chart IDs, the values the calculated amounts. |
|
302 |
|
|
303 |
=item C<assembly_items> |
|
304 |
|
|
305 |
An array reference with as many entries as there are items in the |
|
306 |
record. Each entry is again an array reference of hash references with |
|
307 |
the keys C<part> (an instance of L<SL::DB::Part>), C<qty> and |
|
308 |
C<allocated>. Is only valid for invoices and can be used to populate |
|
309 |
the C<invoice> table with entries for assemblies. |
|
310 |
|
|
311 |
=item C<allocated> |
|
312 |
|
|
313 |
A hash reference. The keys are IDs of entries in the C<invoice> |
|
314 |
table. The values are the new values for the entry's C<allocated> |
|
315 |
column. Only valid for invoices. |
|
316 |
|
|
317 |
=item C<exchangerate> |
|
318 |
|
|
319 |
The exchangerate used for the calculation. |
|
265 | 320 |
|
266 | 321 |
=back |
267 | 322 |
|
Auch abrufbar als: Unified diff
Stark erweiterte Berechnung für Rechnungen