CreatePeriodicInvoices-Job: for anstelle von map für reine Nebeneffekt-Schleifen
[kivitendo-erp.git] / SL / PriceSource / PriceRules.pm
1 package SL::PriceSource::PriceRules;
2
3 use strict;
4 use parent qw(SL::PriceSource::Base);
5
6 use SL::PriceSource::Price;
7 use SL::PriceSource::Discount;
8 use SL::Locale::String;
9 use SL::DB::PriceRule;
10 use List::UtilsBy qw(min_by max_by);
11
12 sub name { 'price_rules' }
13
14 sub description { t8('Price Rule') }
15
16 sub available_rules {
17   my ($self, %params) = @_;
18
19   $self->{available} ||= SL::DB::Manager::PriceRule->get_all_matching(record => $self->record, record_item => $self->record_item);
20 }
21
22 sub available_price_rules {
23   my $rules = $_[0]->available_rules;
24   grep { $_->price_type != SL::DB::Manager::PriceRule::PRICE_DISCOUNT() } @$rules
25 }
26
27 sub available_discount_rules {
28   my $rules = $_[0]->available_rules;
29   grep { $_->price_type == SL::DB::Manager::PriceRule::PRICE_DISCOUNT() } @$rules
30 }
31
32 sub available_prices {
33   my ($self, %params) = @_;
34
35   map { $self->make_price_from_rule($_) } $self->available_price_rules;
36 }
37
38 sub available_discounts {
39   my ($self, %params) = @_;
40
41   map { $self->make_discount_from_rule($_) } $self->available_discount_rules;
42 }
43
44 sub price_from_source {
45   my ($self, $source, $spec) = @_;
46
47   my $rule = SL::DB::Manager::PriceRule->find_by(id => $spec);
48   if ($rule->price_type == SL::DB::Manager::PriceRule::PRICE_DISCOUNT()) {
49     return $self->make_discount_from_rule($rule);
50   } else {
51     return $self->make_price_from_rule($rule);
52   }
53 }
54
55 sub best_price {
56   my ($self) = @_;
57
58   my @rules     = $self->available_price_rules;
59
60   return unless @rules;
61
62   my @max_prio  = max_by { $_->priority } @rules;
63   my $min_price = min_by { $self->price_for_rule($_) } @max_prio;
64
65   $self->make_price_from_rule($min_price);
66 }
67
68 sub best_discount {
69   my ($self) = @_;
70
71   my @rules     = $self->available_discount_rules;
72
73   return unless @rules;
74
75   my @max_prio     = max_by { $_->priority } @rules;
76   my $max_discount = max_by { $_->discount } @max_prio;
77
78   $self->make_discount_from_rule($max_discount);
79 }
80
81 sub price_for_rule {
82   my ($self, $rule) = @_;
83   $rule->price_type != SL::DB::Manager::PriceRule::PRICE_NEW()
84     ? (1 - $rule->reduction / 100) * ($rule->is_sales ? $self->part->sellprice : $self->part->lastcost)
85     : $rule->price;
86 }
87
88 sub make_price_from_rule {
89   my ($self, $rule) = @_;
90
91   SL::PriceSource::Price->new(
92     price        => $self->price_for_rule($rule),
93     spec         => $rule->id,
94     description  => $rule->name,
95     price_source => $self,
96     (invalid      => t8('This Price Rule is no longer valid'))x!!$rule->obsolete,
97   )
98 }
99
100 sub make_discount_from_rule {
101   my ($self, $rule) = @_;
102
103   SL::PriceSource::Discount->new(
104     discount     => $rule->discount / 100,
105     spec         => $rule->id,
106     description  => $rule->name,
107     price_source => $self,
108     (invalid      => t8('This Price Rule is no longer valid'))x!!$rule->obsolete,
109   )
110 }
111
112 1;