8233c58bd945d6a938167dfce2322a6d96a37412
[kivitendo-erp.git] / SL / PriceSource / Base.pm
1 package SL::PriceSource::Base;
2
3 use strict;
4
5 use parent qw(SL::DB::Object);
6 use Rose::Object::MakeMethods::Generic (
7   scalar => [ qw(record_item record) ],
8 );
9
10 sub name { die 'name needs to be implemented' }
11
12 sub description { die 'description needs to be implemented' }
13
14 sub available_prices { die 'available_prices needs to be implemented' }
15
16 sub available_discounts { die 'available_discounts needs to be implemented' }
17
18 sub best_price { die 'best_price needs to be implemented' }
19
20 sub best_discounts { die 'best_discounts needs to be implemented' }
21
22 sub price_from_source { die 'price_from_source needs to be implemented:' . "@_" }
23
24 sub part {
25   $_[0]->record_item->part;
26 }
27
28 sub customer_vendor {
29   $_[0]->record->is_sales ? $_[0]->record->customer : $_[0]->record->vendor;
30 }
31
32 1;
33
34 __END__
35
36 =encoding utf-8
37
38 =head1 NAME
39
40 SL::PriceSource::Base - this is the base class for price source adapters
41
42 =head1 SYNOPSIS
43
44   # working example adapter:
45   package SL::PriceSource::FiveOnEverything;
46
47   use parent qw(SL::PriceSource::Base);
48
49   # used as internal identifier
50   sub name { 'simple' }
51
52   # used in frontend to signal where this comes from
53   sub description { t8('Simple') }
54
55   my $price = SL::PriceSource::Price->new(
56     price        => 5,
57     description  => t8('Only today 5$ on everything!'),
58     price_source => $self,
59   );
60
61   # give list of prices that this
62   sub available_prices {
63     return ($price);
64   }
65
66   sub best_price {
67     return $price;
68   }
69
70   sub price_from_source {
71     return $price;
72   }
73
74 =head1 DESCRIPTION
75
76 See L<SL::PriceSource> for information about the mechanism.
77
78 This is the base class for a price source algorithm. To play well, you'll have
79 to implement a number of interface methods and be aware of a number of corner
80 conditions.
81
82 =head1 AVAILABLE METHODS
83
84 =over 4
85
86 =item C<record_item>
87
88 =item C<record>
89
90 C<record> can be any one of L<SL::DB::Order>, L<SL::DB::DeliveryOrder>,
91 L<SL::DB::Invoice>, L<SL::DB::PurchaseInvoice>. C<record_item> is of the
92 corresponding position type.
93
94 You can assume that both are filled with all information available at the time.
95 C<part> and C<customer>/C<vendor> as well as C<is_sales> can be relied upon. You must NOT
96 rely on both being linked together, in particular
97
98   $self->record_item->record   # don't do that
99
100 is not guaranteed to work.
101
102 Also these are copies and not the original documents. Do not try to change
103 anything and do not save those.
104
105 =item C<part>
106
107 Shortcut to C<< record_item->part >>
108
109 =item C<customer_vendor>
110
111 Shortcut to C<< record->is_sales ? record->customer : record->vendor >>
112
113 =back
114
115 =head1 INTERFACE METHODS
116
117 =over 4
118
119 =item C<name>
120
121 Must return a unique internal name. Must be entered in
122 L<SL::PriceSource::ALL>.
123
124 =item C<description>
125
126 Must return a translated name to be used in the frontend. Will be used to
127 distinguish the origin of different prices.
128
129 =item C<available_prices>
130
131 Must return a list of all prices that your algorithm can recommend to the user
132 for the current situation. Each price must have a unique spec that can be used
133 to recreate it later. Try to be brief, no one needs 20 different price
134 suggestions.
135
136 =item C<available_discounts>
137
138 Must return a list of all prices that your algorithm can recommend to the user
139 for the current situation. Each discount must have a unique spec that can be
140 used to recreate it later. Try to be brief, no one needs 20 different discount
141 suggestions.
142
143 =item C<best_price>
144
145 Must return what you think of as the best matching price in your
146 C<available_prices>. This does not have to be the lowest price, but it will be
147 compared later to other price sources, and the lowest will be set.
148
149 =item C<best_discount>
150
151 Must return what you think of as the best matching discount in your
152 C<available_discounts>. This does not have to be the highest discount, but it
153 will be compared later to other price sources, and the highest will be set.
154
155 =item C<price_from_source SOURCE, SPEC>
156
157 Must recreate the price or discount from C<SPEC> and return. For reference, the
158 complete C<SOURCE> entry from C<record_item.active_price_source> or
159 C<record_item.active_discount_source> is included.
160
161 Note that constraints from the rest of the C<record> do not apply anymore. If
162 information needed for the retrieval can be deleted elsewhere, then you must
163 guard against that.
164
165 If the price for the same coditions changed, return the new price. It will be
166 presented as an option to the user if the record is still editable.
167
168 If the price is not valid anymore or not reconstructable, return a price with
169 C<price_source> and C<spec> set to the same values as before but with
170 C<invalid> or C<missing> set.
171
172 =back
173
174 =head1 TRAPS AND CORNER CASES
175
176 =over 4
177
178 =item *
179
180 Be aware that all 8 types of record will be passed to your algorithm. If you
181 don't serve some of them, just return empty lists on C<available_prices> and
182 C<best_price>
183
184 =item *
185
186 Information in C<record> might be missing. Especially on newly or automatically
187 created records there might be fields not set at all.
188
189 =item *
190
191 Records will not be calculated. If you need tax data or position totals, you
192 need to invoke that yourself.
193
194 =item *
195
196 Accessor methods might not be present in some of the record types.
197
198 =item *
199
200 You do not need to do price factor and row discount calculation. These will be
201 done automatically afterwards. You do have to include customer/vendor discounts
202 if your price interacts with those.
203
204 =item *
205
206 The price field in purchase records is still C<sellprice>.
207
208 =item *
209
210 C<source> and C<spec> are tainted. If you store data directly in C<spec>, sanitize.
211
212 =back
213
214 =head1 SEE ALSO
215
216 L<SL::PriceSource>,
217 L<SL::PriceSource::Price>,
218 L<SL::PriceSource::ALL>
219
220 =head1 BUGS
221
222 None yet. :)
223
224 =head1 AUTHOR
225
226 Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>
227
228 =cut