Typos in PriceSource Doku
[kivitendo-erp.git] / SL / PriceSource.pm
1 package SL::PriceSource;
2
3 use strict;
4 use parent 'SL::DB::Object';
5 use Rose::Object::MakeMethods::Generic (
6   scalar => [ qw(record_item record) ],
7 );
8
9 use List::UtilsBy qw(min_by max_by);
10 use SL::PriceSource::ALL;
11 use SL::PriceSource::Price;
12 use SL::Locale::String;
13
14 sub all_price_sources {
15   my ($self) = @_;
16
17   map {
18     $_->new(record_item => $self->record_item, record => $self->record)
19   } SL::PriceSource::ALL->all_enabled_price_sources
20 }
21
22 sub price_from_source {
23   my ($self, $source) = @_;
24   my ($source_name, $spec) = split m{/}, $source, 2;
25
26   my $class = SL::PriceSource::ALL->price_source_class_by_name($source_name);
27
28   return $class
29     ? $class->new(record_item => $self->record_item, record => $self->record)->price_from_source($source, $spec)
30     : empty_price();
31 }
32
33 sub available_prices {
34   map { $_->available_prices } $_[0]->all_price_sources;
35 }
36
37 sub available_discounts {
38   return if $_[0]->record_item->part->not_discountable;
39   map { $_->available_discounts } $_[0]->all_price_sources;
40 }
41
42 sub best_price {
43   min_by { $_->price } max_by { $_->priority } grep { $_->price > 0 } grep { $_ } map { $_->best_price } $_[0]->all_price_sources;
44 }
45
46 sub best_discount {
47   max_by { $_->discount } max_by { $_->priority } grep { $_->discount } grep { $_ } map { $_->best_discount } $_[0]->all_price_sources;
48 }
49
50 sub empty_price {
51   SL::PriceSource::Price->new(
52     description => t8('None (PriceSource)'),
53   );
54 }
55
56 1;
57
58 __END__
59
60 =encoding utf-8
61
62 =head1 NAME
63
64 SL::PriceSource - mixin for price_sources in record items
65
66 =head1 DESCRIPTION
67
68 PriceSource is an interface that allows generic algorithms to be plugged
69 together to calculate available prices for a position in a record.
70
71 Each algorithm can access details of the record to realize dependencies on
72 part, customer, vendor, date, quantity etc, which was previously not possible.
73
74 =head1 BACKGROUND AND PHILOSOPHY
75
76 sql ledger and subsequently Lx-Office had three prices per part: sellprice,
77 listprice and lastcost. At the moment a part is loaded into a record, the
78 applicable price is copied and after that it is free to be changed.
79
80 Later on additional things were added. Various types of discount, vendor pricelists
81 and the infamous price groups. The problem is not that those didn't work, the
82 problem is, that they had to guess too much when to change a price with the
83 available price from the database, and when to leave the user entered price.
84
85 Unrelated to that, users asked for more ways to store special prices, based on
86 qty (block pricing, bulk discount), based on date (special offers), based on
87 customers (special terms), up to full blown calculation modules.
88
89 On a third front sales personnel asked for ways to see what price options a
90 position in a quotation has, and wanted information available when a price
91 offer changed.
92
93 Price sources put that together by making some compromises:
94
95 =over 4
96
97 =item 1.
98
99 Only change the price on creation of a position or when asked to.
100
101 =item 2.
102
103 Either set the price from a price source and let it be read only, or use a free
104 price.
105
106 =item 3.
107
108 Save the origin of each price with the record so that the calculation can be
109 reproduced.
110
111 =item 4.
112
113 Make price calculation flexible and pluggable.
114
115 =back
116
117 The first point creates user security by never changing a price for them
118 without their explicit consent, eliminating all problems originating from
119 trying to be smart. The second and third one ensure that later on the
120 calculation can be repeated so that invalid prices can be caught (because for
121 example the special offer is no longer valid), and so that sales personnel have
122 information about rising or falling prices. The fourth point ensures that
123 insular calculation processes can be developed independent of the core code.
124
125 =head1 INTERFACE METHODS
126
127 =over 4
128
129 =item C<new PARAMS>
130
131 C<PARAMS> must contain both C<record> and C<record_item>. C<record_item> does
132 not have to be registered in C<record>.
133
134 =item C<price_from_source>
135
136 Attempts to retrieve a formerly calculated price with the same conditions
137
138 =item C<available_prices>
139
140 Returns all available prices.
141
142 =item C<best_price>
143
144 Attempts to get the best available price. returns L<empty_price> if no price is found.
145
146 =item C<empty_price>
147
148 A special empty price, that does not change the previously entered price, and
149 opens the price field to manual changes.
150
151 =back
152
153 =head1 SEE ALSO
154
155 L<SL::PriceSource::Base>,
156 L<SL::PriceSource::Price>,
157 L<SL::PriceSource::ALL>
158
159 =head1 BUGS
160
161 None yet. :)
162
163 =head1 AUTHOR
164
165 Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>
166
167 =cut