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