Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 4ff296dd

Von Moritz Bunkus vor fast 14 Jahren hinzugefügt

  • ID 4ff296dd32073025266104b2bb35644d9ec553d7
  • Vorgänger dbdaaafb
  • Nachfolger a6bf2bd6

Basisversion des Kalkulators für Beträge, Preise und Steuern

Unterschiede anzeigen:

SL/DB/Helper/PriceTaxCalculator.pm
1
package SL::DB::Helper::PriceTaxCalculator;
2

  
3
use strict;
4

  
5
use parent qw(Exporter);
6
our @EXPORT = qw(calculate_prices_and_taxes);
7

  
8
use Carp;
9
use List::Util qw(sum);
10
use SL::DB::PriceFactor;
11
use SL::DB::Unit;
12

  
13
sub calculate_prices_and_taxes {
14
  my ($self) = @_;
15

  
16
  my $is_sales            = $self->can('customer') && $self->customer;
17

  
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   = ();
21

  
22
  $self->netamount(  0);
23
  $self->marge_total(0);
24
  my $lastcost_total = 0;
25

  
26
  my $idx = 0;
27

  
28
  foreach my $item ($self->items) {
29
    $idx++;
30

  
31
    my $part_unit  = $units_by_name{ $item->part->unit };
32
    my $item_unit  = $units_by_name{ $item->unit       };
33

  
34
    croak("Undefined unit " . $item->part->unit) if !$part_unit;
35
    croak("Undefined unit " . $item->unit)       if !$item_unit;
36

  
37
    $item->base_qty($item_unit->convert_to($item->qty, $part_unit));
38

  
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);
42

  
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);
46

  
47
    if (!$linetotal) {
48
      $item->marge_total(  0);
49
      $item->marge_percent(0);
50

  
51
    } else {
52
      my $lastcost = ! ($item->lastcost * 1) ? ($item->part->lastcost || 0) : $item->lastcost;
53

  
54
      $item->marge_total(  $linetotal - $lastcost / $item->marge_price_factor);
55
      $item->marge_percent($item->marge_total * 100 / $linetotal);
56

  
57
      $self->marge_total(  $self->marge_total + $item->marge_total);
58
      $lastcost_total += $lastcost;
59
    }
60

  
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;
64

  
65
    if ($self->taxincluded) {
66
      $tax_amount = $linetotal * $tax_rate / ($tax_rate + 1);
67
      $sellprice  = $sellprice             / ($tax_rate + 1);
68

  
69
    } else {
70
      $tax_amount = $linetotal * $tax_rate;
71
    }
72

  
73
    $taxes_by_chart_id{ $taxkey->chart_id } ||= 0;
74
    $taxes_by_chart_id{ $taxkey->chart_id }  += $tax_amount;
75

  
76
    $self->netamount($self->netamount + $sellprice * $item->qty / $item->price_factor);
77

  
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);
80
  }
81

  
82
  my $tax_sum = sum map { round($_, 2) } values %taxes_by_chart_id;
83

  
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);
87

  
88
  return $self unless wantarray;
89
  return ( self  => $self,
90
           taxes => \%taxes_by_chart_id,
91
         );
92
}
93

  
94
sub round {
95
  return $::form->round_amount(@_);
96
}
97

  
98
sub num_decimal_places {
99
  return length( (split(/\./, '' . shift, 2))[1] || '' );
100
}
101

  
102
1;
103
__END__
104

  
105
=pod
106

  
107
=encoding utf8
108

  
109
=head1 NAME
110

  
111
SL::DB::Helper::PriceTaxCalculator - Mixin for calculating the prices,
112
amounts and taxes of orders, quotations, invoices
113

  
114
=head1 FUNCTIONS
115

  
116
=over 4
117

  
118
=item C<calculate_prices_and_taxes %params>
119

  
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:
123

  
124
=over 2
125

  
126
=item C<transdate>
127

  
128
The record's date.
129

  
130
=item C<customer> or C<vendor>
131

  
132
Determines if the record is a sales or purchase record.
133

  
134
=item C<items>
135

  
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.
139

  
140
=back
141

  
142
The following values are calculated and set for C<$self>: C<amount>,
143
C<netamount>, C<marge_percent>, C<marge_total>.
144

  
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>,
147
C<marge_percent>.
148

  
149
The objects are not saved.
150

  
151
Returns C<$self> in scalar context.
152

  
153
In array context a hash with the following keys is returned:
154

  
155
=over 2
156

  
157
=item C<self>
158

  
159
The object itself.
160

  
161
=item C<taxes>
162

  
163
A hash reference with the calculated taxes. The keys are chart IDs,
164
the values the calculated taxes.
165

  
166
=back
167

  
168
=back
169

  
170
=head1 BUGS
171

  
172
Nothing here yet.
173

  
174
=head1 AUTHOR
175

  
176
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
177

  
178
=cut

Auch abrufbar als: Unified diff