6685578670ea9b8170608de236ba2f33b9ad572b
[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_price_from_rule($rule);
50   }
51
52   return;
53 }
54
55 sub discount_from_source {
56   my ($self, $source, $spec) = @_;
57
58   my $rule = SL::DB::Manager::PriceRule->find_by(id => $spec);
59   if ($rule->price_type == SL::DB::Manager::PriceRule::PRICE_DISCOUNT()) {
60     return $self->make_discount_from_rule($rule);
61   }
62
63   return;
64 }
65
66 sub best_price {
67   my ($self) = @_;
68
69   my @rules     = $self->available_price_rules;
70
71   return unless @rules;
72
73   my @max_prio  = max_by { $_->priority } @rules;
74   my $min_price = min_by { $self->price_for_rule($_) } @max_prio;
75
76   $self->make_price_from_rule($min_price);
77 }
78
79 sub best_discount {
80   my ($self) = @_;
81
82   my @rules     = $self->available_discount_rules;
83
84   return unless @rules;
85
86   my @max_prio     = max_by { $_->priority } @rules;
87   my $max_discount = max_by { $_->discount } @max_prio;
88
89   $self->make_discount_from_rule($max_discount);
90 }
91
92 sub price_for_rule {
93   my ($self, $rule) = @_;
94   $rule->price_type != SL::DB::Manager::PriceRule::PRICE_NEW()
95     ? (1 - $rule->reduction / 100) * ($rule->is_sales ? $self->part->sellprice : $self->part->lastcost)
96     : $rule->price;
97 }
98
99 sub make_price_from_rule {
100   my ($self, $rule) = @_;
101
102   SL::PriceSource::Price->new(
103     price        => $self->price_for_rule($rule),
104     spec         => $rule->id,
105     description  => $rule->name,
106     priority     => $rule->priority,
107     price_source => $self,
108     (invalid      => t8('This Price Rule is no longer valid'))x!!$rule->obsolete,
109   )
110 }
111
112 sub make_discount_from_rule {
113   my ($self, $rule) = @_;
114
115   SL::PriceSource::Discount->new(
116     discount     => $rule->discount / 100,
117     spec         => $rule->id,
118     description  => $rule->name,
119     priority     => $rule->priority,
120     price_source => $self,
121     (invalid      => t8('This Price Rule is no longer valid'))x!!$rule->obsolete,
122   )
123 }
124
125 1;