Revision 9229c547
Von Moritz Bunkus vor fast 14 Jahren hinzugefügt
SL/DB/Helper/PriceTaxCalculator.pm | ||
---|---|---|
6 | 6 |
our @EXPORT = qw(calculate_prices_and_taxes); |
7 | 7 |
|
8 | 8 |
use Carp; |
9 |
use List::Util qw(sum); |
|
9 |
use List::Util qw(sum min);
|
|
10 | 10 |
use SL::DB::Default; |
11 | 11 |
use SL::DB::PriceFactor; |
12 | 12 |
use SL::DB::Unit; |
... | ... | |
14 | 14 |
sub calculate_prices_and_taxes { |
15 | 15 |
my ($self, %params) = @_; |
16 | 16 |
|
17 |
my $is_sales = $self->can('customer') && $self->customer; |
|
18 |
my $is_invoice = (ref($self) =~ /Invoice/) || $params{invoice}; |
|
19 |
|
|
20 | 17 |
my %units_by_name = map { ( $_->name => $_ ) } @{ SL::DB::Manager::Unit->get_all }; |
21 | 18 |
my %price_factors_by_id = map { ( $_->id => $_ ) } @{ SL::DB::Manager::PriceFactor->get_all }; |
22 |
my %taxes_by_chart_id = (); |
|
23 |
my %amounts_by_chart_id = (); |
|
24 | 19 |
|
25 | 20 |
my %data = ( lastcost_total => 0, |
26 | 21 |
invoicediff => 0, |
27 | 22 |
units_by_name => \%units_by_name, |
28 | 23 |
price_factors_by_id => \%price_factors_by_id, |
29 |
taxes_by_chart_id => \%taxes_by_chart_id, |
|
30 |
amounts_by_chart_id => \%amounts_by_chart_id, |
|
24 |
taxes => { }, |
|
25 |
amounts => { }, |
|
26 |
amounts_cogs => { }, |
|
27 |
allocated => { }, |
|
28 |
assembly_items => [ ], |
|
31 | 29 |
exchangerate => undef, |
30 |
is_sales => $self->can('customer') && $self->customer, |
|
31 |
is_invoice => (ref($self) =~ /Invoice/) || $params{invoice}, |
|
32 | 32 |
); |
33 | 33 |
|
34 |
if (($self->curr || '') ne SL::DB::Default->get_default_currency) { |
|
35 |
$data{exchangerate} = $::form->check_exchangerate(\%::myconfig, $self->curr, $self->transdate, $is_sales ? 'buy' : 'sell'); |
|
36 |
$data{exchangerate} ||= $params{exchangerate}; |
|
37 |
} |
|
38 |
$data{exchangerate} ||= 1; |
|
34 |
_get_exchangerate($self, \%data, %params); |
|
39 | 35 |
|
40 | 36 |
$self->netamount( 0); |
41 | 37 |
$self->marge_total(0); |
... | ... | |
43 | 39 |
my $idx = 0; |
44 | 40 |
foreach my $item ($self->items) { |
45 | 41 |
$idx++; |
46 |
_calculate_item($self, $item, $idx, \%data); |
|
42 |
_calculate_item($self, $item, $idx, \%data, %params);
|
|
47 | 43 |
} |
48 | 44 |
|
49 |
my $tax_sum = sum map { _round($_, 2) } values %taxes_by_chart_id;
|
|
45 |
my $tax_sum = sum map { _round($_, 2) } values %{ $data{taxes} };
|
|
50 | 46 |
|
51 | 47 |
$self->amount( _round($self->netamount + $tax_sum, 2)); |
52 | 48 |
$self->netamount( _round($self->netamount, 2)); |
53 | 49 |
$self->marge_percent($self->netamount ? ($self->netamount - $data{lastcost_total}) * 100 / $self->netamount : 0); |
54 | 50 |
|
51 |
_calculate_invoice($self, \%data, %params) if $data{is_invoice}; |
|
52 |
|
|
55 | 53 |
return $self unless wantarray; |
56 |
return ( self => $self, |
|
57 |
taxes => \%taxes_by_chart_id, |
|
58 |
amounts => \%amounts_by_chart_id, |
|
59 |
); |
|
54 |
|
|
55 |
return map { ($_ => $data{$_}) } qw(taxes amounts amounts_cogs allocated exchangerate assembly_items); |
|
56 |
} |
|
57 |
|
|
58 |
sub _get_exchangerate { |
|
59 |
my ($self, $data, %params) = @_; |
|
60 |
|
|
61 |
if (($self->curr || '') ne SL::DB::Default->get_default_currency) { |
|
62 |
$data->{exchangerate} = $::form->check_exchangerate(\%::myconfig, $self->curr, $self->transdate, $data->{is_sales} ? 'buy' : 'sell'); |
|
63 |
$data->{exchangerate} ||= $params{exchangerate}; |
|
64 |
} |
|
65 |
$data->{exchangerate} ||= 1; |
|
60 | 66 |
} |
61 | 67 |
|
62 | 68 |
sub _calculate_item { |
63 |
my ($self, $item, $idx, $data) = @_; |
|
69 |
my ($self, $item, $idx, $data, %params) = @_;
|
|
64 | 70 |
|
65 | 71 |
my $part_unit = $data->{units_by_name}->{ $item->part->unit }; |
66 | 72 |
my $item_unit = $data->{units_by_name}->{ $item->unit }; |
... | ... | |
107 | 113 |
$tax_amount = $linetotal * $tax_rate; |
108 | 114 |
} |
109 | 115 |
|
110 |
$data->{taxes_by_chart_id}->{ $taxkey->chart_id } ||= 0;
|
|
111 |
$data->{taxes_by_chart_id}->{ $taxkey->chart_id } += $tax_amount;
|
|
116 |
$data->{taxes}->{ $taxkey->chart_id } ||= 0; |
|
117 |
$data->{taxes}->{ $taxkey->chart_id } += $tax_amount; |
|
112 | 118 |
|
113 | 119 |
$self->netamount($self->netamount + $sellprice * $item->qty / $item->price_factor); |
114 | 120 |
|
115 | 121 |
my $chart = $item->part->get_chart(type => $data->{is_sales} ? 'income' : 'expense', taxzone => $self->taxzone_id); |
116 |
$data->{amounts_by_chart_id}->{$chart->id} += $linetotal; |
|
117 |
|
|
118 |
if ($data->{is_invoice}) { |
|
119 |
if ($item->part->is_assembly) { |
|
120 |
# process_assembly()... |
|
121 |
} else { |
|
122 |
# cogs... |
|
123 |
} |
|
124 |
} |
|
122 |
$data->{amounts}->{$chart->id} += $linetotal; |
|
123 |
|
|
124 |
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))); |
|
125 | 126 |
|
126 | 127 |
$::lxdebug->message(0, "CALCULATE! ${idx} i.qty " . $item->qty . " i.sellprice " . $item->sellprice . " sellprice $sellprice taxamount $tax_amount " . |
127 | 128 |
"i.linetotal $linetotal netamount " . $self->netamount . " marge_total " . $item->marge_total . " marge_percent " . $item->marge_percent); |
128 | 129 |
} |
129 | 130 |
|
131 |
sub _calculate_invoice { |
|
132 |
my ($self, $data, %params) = @_; |
|
133 |
|
|
134 |
return unless $data->{invoice}; |
|
135 |
} |
|
136 |
|
|
137 |
sub _calculate_assembly_item { |
|
138 |
my ($self, $data, $part, $total_qty, $base_factor) = @_; |
|
139 |
|
|
140 |
return 0 unless $::eur && $data->{is_invoice}; |
|
141 |
return _calculate_part_service_item($self, $data, $part, $total_qty) unless $part->is_assembly; |
|
142 |
|
|
143 |
my $allocated = 0; |
|
144 |
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); |
|
147 |
} |
|
148 |
|
|
149 |
return $allocated; |
|
150 |
} |
|
151 |
|
|
152 |
sub _calculate_part_service_item { |
|
153 |
my ($self, $data, $part, $total_qty, $base_factor) = @_; |
|
154 |
|
|
155 |
$::lxdebug->message(0, "cpsi tq " . $total_qty); |
|
156 |
|
|
157 |
return 0 unless $::eur && $data->{is_invoice} && $total_qty; |
|
158 |
|
|
159 |
my ($entry); |
|
160 |
$base_factor ||= 1; |
|
161 |
my $remaining_qty = $total_qty; |
|
162 |
my $expense_income_chart = $part->get_chart(type => $data->{is_sales} ? 'expense' : 'income', taxzone => $self->taxzone_id); |
|
163 |
my $inventory_chart = $part->get_chart(type => 'inventory', taxzone => $self->taxzone_id); |
|
164 |
|
|
165 |
my $iterator = SL::DB::Manager::InvoiceItem->get_all_iterator(query => [ and => [ parts_id => $part->id, |
|
166 |
\'(base_qty + allocated) < 0' ] ]); |
|
167 |
|
|
168 |
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"); |
|
171 |
|
|
172 |
next unless $qty; |
|
173 |
|
|
174 |
my $linetotal = _round(($entry->sellprice * $qty) / $base_factor, 2); |
|
175 |
|
|
176 |
$data->{amounts_cogs}->{ $expense_income_chart->id } -= $linetotal; |
|
177 |
$data->{amounts_cogs}->{ $inventory_chart->id } += $linetotal; |
|
178 |
|
|
179 |
$data->{allocated}->{ $part->id } ||= 0; |
|
180 |
$data->{allocated}->{ $part->id } += $qty; |
|
181 |
$remaining_qty -= $qty; |
|
182 |
} |
|
183 |
|
|
184 |
$iterator->finish; |
|
185 |
|
|
186 |
return $remaining_qty - $total_qty; |
|
187 |
} |
|
188 |
|
|
130 | 189 |
sub _round { |
131 | 190 |
return $::form->round_amount(@_); |
132 | 191 |
} |
Auch abrufbar als: Unified diff
Weitere Berechnungen für Rechnungen