Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 418f0e70

Von Sven Schöling vor etwa 10 Jahren hinzugefügt

  • ID 418f0e7084b02c1c057e4f10b858b6bffc25e354
  • Vorgänger 2bdd0bc5
  • Nachfolger 0a3f51b8

PriceSource: Dokumentation

Unterschiede anzeigen:

SL/PriceSource.pm
40 40

  
41 41
sub empty_price {
42 42
  SL::PriceSource::Price->new(
43
    source      => '',
44 43
    description => t8('None (PriceSource)'),
45 44
  );
46 45
}
......
55 54

  
56 55
SL::PriceSource - mixin for price_sources in record items
57 56

  
58
=head1 SYNOPSIS
57
=head1 DESCRIPTION
59 58

  
60
  # in record item class
59
PriceSource is an interface that allows generic algorithms to be plugged
60
together to calculate available prices for a position in a record.
61 61

  
62
  use SL::PriceSource;
62
Each algorithm can access details of the record to realize dependancies on
63
part, customer, vendor, date, quantity etc, which was previously not possible.
63 64

  
64
  # later on:
65
=head1 BACKGROUND AND PHILOSOPY
65 66

  
66
  $record_item->all_price_sources
67
  $record_item->price_source      # get
68
  $record_item->price_source($c)  # set
67
sql ledger and subsequently Lx-Office had three prices per part: sellprice,
68
listprice and lastcost. At the moment a part is loaded into a record, the
69
applicable price is copied and after that free to be changed.
69 70

  
70
  $record_item->update_price_source # set price to calculated
71
Later on additional things joined. Various types of discount, vendor pricelists
72
and the infamous price groups. The problem is not that those didn't work, the
73
problem is, that they had to guess to much when to change a price with the
74
available price from database, and when to leave the user entered price.
71 75

  
72
=head1 DESCRIPTION
76
Unrelated to that, users asked for more ways to store special prices, based on
77
qty (block pricing, bulk discount), based on date (special offers), based on
78
customers (special terms), up to full blown calculation modules.
79

  
80
On a third front sales personnel asked for ways to see what price options a
81
position in a quotation has, and wanted information available when a price
82
offer changed.
83

  
84
Price sources put that together by making some compromises:
85

  
86
=over 4
87

  
88
=item 1.
89

  
90
Only change the price on creation of a position or when asked to.
91

  
92
=item 2.
93

  
94
Either set the price from a price source and let it be read only, or use a free
95
price.
96

  
97
=item 3.
98

  
99
Save the origin of each price with the record so that the calculation can be
100
reproduced.
101

  
102
=item 4.
103

  
104
Make price calculation flexible and pluggable.
105

  
106
=back
107

  
108
The first point creates user security by never changing a price for them
109
without their explicit consent, eliminating all problems originating from
110
trying to be smart. The second and third one ensure that later on the
111
calculation can be repeated so that invalid prices can be caught (because for
112
example the special offer is no longer valid), and so that sales personnel have
113
information about rising or falling prices. The fourth point ensures that
114
insular calculation processes can be developed independant of the core code.
115

  
116
=head1 INTERFACE METHODS
117

  
118
=over 4
119

  
120
=item C<new PARAMS>
121

  
122
C<PARAMS> must contain both C<record> and C<record_item>. C<record_item> does
123
not have to be registered in C<record>.
124

  
125
=item C<price_from_source>
126

  
127
Attempts to retrieve a formerly calculated price with the same conditions
128

  
129
=item C<available_prices>
130

  
131
Returns all available prices.
132

  
133
=item C<best_price>
73 134

  
74
This mixin provides a way to use price_source objects from within a record item.
75
Record items in this contest mean OrderItems, InvoiceItems and
76
DeliveryOrderItems.
135
Attempts to get the best available price. returns L<empty_price> if no price is found.
77 136

  
78
=head1 FUNCTIONS
137
=item C<empty_price>
79 138

  
80
price_sources
139
A special empty price, that does not change the previously entered price, and
140
opens the price field to manual changes.
81 141

  
82
returns a list of price_source objects which are created with the current record
83
item.
142
=back
84 143

  
85
active_price_source
144
=head1 SEE ALSO
86 145

  
87
returns the object representing the currently chosen price_source method or
88
undef if custom price is chosen. Note that this must not necessarily be the
89
active price, if something affecting the price_source has changed, the price
90
calculated can differ from the price in the record. It is the responsibility of
91
the implementing code to decide what to do in this case.
146
L<SL::PriceSource::Base>,
147
L<SL::PriceSource::Price>,
148
L<SL::PriceSource::ALL>
92 149

  
93 150
=head1 BUGS
94 151

  
SL/PriceSource/Base.pm
29 29

  
30 30
=head1 NAME
31 31

  
32
SL::PriceSource::Base - <oneliner description>
32
SL::PriceSource::Base - this is the base class for price source adapters
33 33

  
34 34
=head1 SYNOPSIS
35 35

  
36
  # in consuming module
37
# TODO: thats bullshit, theres no need to have this pollute the namespace
38
# make a manager that handles this
36
  # working example adapter:
37
  package SL::PriceSource::FiveOnEverything;
39 38

  
40
  my @list_of_price_sources = $record_item->price_sources;
41
  for (@list_of_price_sources) {
42
    my $internal_name   = $_->name;
43
    my $translated_name = $_->description;
44
    my $price           = $_->price;
39
  use parent qw(SL::PriceSource::Base);
40

  
41
  # used as internal identifier
42
  sub name { 'simple' }
43

  
44
  # used in frontend to signal where this comes from
45
  sub description { t8('Simple') }
46

  
47
  my $price = SL::PriceSource::Price->new(
48
    price        => 5,
49
    description  => t8('Only today 5$ on everything!'),
50
    price_source => $self,
51
  );
52

  
53
  # give list of prices that this
54
  sub available_prices {
55
    return ($price);
45 56
  }
46 57

  
47
  $record_item->set_active_price_source($price_source)  # equivalent to:
48
  $record_item->active_price_source($price_source->name);
49
  $record_item->sellprice($price_source->price);
58
  sub best_price {
59
    return $price;
60
  }
50 61

  
51
  # for finer control
52
  $price_source->needed_params
53
  $price_source->supported_params
62
  sub price_from_source {
63
    return $price;
64
  }
54 65

  
55 66
=head1 DESCRIPTION
56 67

  
57
PriceSource is an interface that allows generic algorithms to be used, to
58
calculate a price for a position in a record.
68
See L<SL::PriceSource> for information about the mechanism.
69

  
70
This is the base class for a price source algorithm. To play well, you'll have
71
to implement a number of interface methods and be aware of a number of corner
72
conditions.
73

  
74
=head1 AVAILABLE METHODS
75

  
76
=over 4
77

  
78
=item C<record_item>
79

  
80
=item C<record>
81

  
82
C<record> can be any one of L<SL::DB::Order>, L<SL::DB::DeliveryOrder>,
83
L<SL::DB::Invoice>, L<SL::DB::PurchaseInvoice>. C<record_item> is of the
84
corresponding position type.
85

  
86
You can assume that both are filled with all information available at the time.
87
C<part> and C<customer>/C<vendor> as well as C<is_sales> can be relied upon. You must NOT
88
rely on both being linked together, in particular
89

  
90
  $self->record_item->record   # don't do that
59 91

  
60
If any such price_source algorithm is known to the system, a user can chose
61
which of them should be used to claculate the price displayed in the record.
92
is not guaranteed to work.
62 93

  
63
The algorithm is saved togetherwith the target price, so that changes in the
64
record can recalculate the price accordingly, and otherwise manual changes to
65
the price can reset the price_source used to custom (aka no price_source).
94
Also these are copies and not the original documents. Do not try to change
95
anything and do not save those.
96

  
97
=item C<part>
98

  
99
Shortcut to C<< record_item->part >>
100

  
101
=back
66 102

  
67 103
=head1 INTERFACE METHODS
68 104

  
......
70 106

  
71 107
=item C<name>
72 108

  
73
Should return a unique internal name. Should be entered in
74
L<SL::PriceSource::ALL> so that a name_to_class lookup works.
109
Must return a unique internal name. Must be entered in
110
L<SL::PriceSource::ALL>.
75 111

  
76 112
=item C<description>
77 113

  
78
Should return a translated name.
114
Must return a translated name to be used in frontend. Will be used, to
115
distinguish the origin of different prices.
116

  
117
=item C<available_prices>
79 118

  
80
=item C<needed_params>
119
Must return a list of all prices that you algorithm can recommend the user
120
for the current situation. Each price must have a unique spec that can be used
121
to recreate it later. Try to be brief, no one needs 20 different price
122
suggestions.
81 123

  
82
Should return a list of elements that a record_item NEEDS to be used with this calulation.
124
=item C<best_price>
83 125

  
84
Both C<needed_params> nad C<supported_params> are purely informational at this point.
126
Must return what you think of as the best matching price in your
127
C<available_prices>. This does not have to be the lowest price, but it will be
128
compared later to other price sources, and the lowest will be set.
85 129

  
86
=item C<supported_params>
130
=item C<price_from_source SOURCE, SPEC>
87 131

  
88
Should return a list of elements that a record_item MAY HAVE to be used with this calulation.
132
Must recreate the price from C<SPEC> and return. For reference, the complete
133
C<SOURCE> entry from C<record_item.active_price_source> is included.
89 134

  
90
Both C<needed_params> nad C<supported_params> are purely informational at this point.
135
Note that constraints from the rest of the C<record> do not apply anymore. If
136
information needed for the retrieval can be deleted elsewhere, then you must
137
guard against that.
91 138

  
92
=item C<price>
139
If the price for the same coditions changed, return the new price. It will be
140
presented as an option to the user if the record is still editable.
93 141

  
94
Calculate a price and return. Do not mutate the record_item. Should will return
95
undef if price is not applicable to the current record_item.
142
If the price is not valid anymore or not reconstructable, return a price with
143
C<price_source> and C<spec> set to the same values as before but with
144
C<invalid> or C<missing> set.
96 145

  
97 146
=back
98 147

  
148
=head1 TRAPS AND CORNER CASES
149

  
150
=over 4
151

  
152
=item *
153

  
154
Be aware that all 8 types of record will be passed to your algorithm. If you
155
don't serve some of them, just return emptry lists on C<available_prices> and
156
C<best_price>
157

  
158
=item *
159

  
160
Information in C<record> might be missing. Especially on newly or automatically
161
created records there might be fields not set at all.
162

  
163
=item *
164

  
165
Records will not be calculated. If you need tax data or position totals, you
166
need to invoke that for yourself.
167

  
168
=item *
169

  
170
Accessor methods might not be present in some of the record types.
171

  
172
=item *
173

  
174
You do not need to do price factor and row discount calculation. These will be
175
done automatically afterwards. You do have to include customer/vendor discount
176
if your price interacts with those.
177

  
178
=item *
179

  
180
The price field in purchase records is still C<sellprice>.
181

  
182
=item *
183

  
184
C<source> and C<spec> are tainted. If you store data directly in C<spec>, sanitize.
185

  
186
=head1 SEE ALSO
187

  
188
L<SL::PriceSource>,
189
L<SL::PriceSource::Price>,
190
L<SL::PriceSource::ALL>
191

  
99 192
=head1 BUGS
100 193

  
101 194
None yet. :)
SL/PriceSource/Price.pm
4 4

  
5 5
use parent 'SL::DB::Object';
6 6
use Rose::Object::MakeMethods::Generic (
7
  scalar => [ qw(price description spec price_source) ],
7
  scalar => [ qw(price description spec price_source invalid missing) ],
8 8
  array => [ qw(depends_on) ]
9 9
);
10 10

  
......
32 32
}
33 33

  
34 34
1;
35

  
36
__END__
37

  
38
=encoding utf-8
39

  
40
=head1 NAME
41

  
42
SL::PriceSource::Price - contrainer to pass calculated prices around
43

  
44
=head1 SYNOPSIS
45

  
46
  # in PriceSource::Base implementation
47
  $price = SL::PriceSource::Price->new(
48
    price        => 10.3,
49
    spec         => '10.3', # something you can easily parse later
50
    description  => t8('Fix price 10.3'),
51
    price_source => $self,
52
  )
53

  
54
  # special empty price in SL::PriceSource
55
  SL::PriceSource::Price->new(
56
    description => t8('None (PriceSource)'),
57
  );
58

  
59
  # invalid price
60
  SL::PriceSource::Price->new(
61
    price        => $original_price,
62
    spec         => $original_spec,
63
    description  => $original_description,
64
    invalid      => t8('Offer expired #1 weeks ago', $dt->delta_weeks),
65
    price_source => $self,
66
  );
67

  
68
  # missing price
69
  SL::PriceSource::Price->new(
70
    price        => $original_price,              # will keep last entered price
71
    spec         => $original_spec,
72
    description  => '',
73
    missing      => t8('Um, sorry, cannot find that one'),
74
    price_source => $self,
75
  );
76

  
77

  
78
=head1 DESCRIPTION
79

  
80
See L<SL::PriceSource> for information about the mechanism.
81

  
82
This is a container for prices that are generated by L<SL::PriceSource::Base>
83
implementations.
84

  
85
=head1 CONSTRUCTOR FIELDS
86

  
87
=over 4
88

  
89
=item C<price>
90

  
91
The price. A price of 0 is special and is considered undesirable. If passed as
92
part of C<available_prices> it will be filtered out. If returned as
93
C<best_price> or C<price_from_source> it will be warned about.
94

  
95
=item C<spec>
96

  
97
A unique string that can later be understood by the creating implementation.
98
Can be empty if the implementation only supports one price for a given
99
record_item.
100

  
101
=item C<description>
102

  
103
A localized short description of the origins of this price.
104

  
105
=item C<price_source>
106

  
107
A ref to the creating algorithm.
108

  
109
=item C<missing>
110

  
111
OPTIONAL. Both indicator and localized message that the price with this spec
112
could not be reproduced and should be changed.
113

  
114
=item C<invalid>
115

  
116
OPTIONAL. Both indicator and localized message that the conditions for this
117
price are no longer valid, and that the price should be changed.
118

  
119
=back
120

  
121
=head1 SEE ALSO
122

  
123
L<SL::PriceSource>,
124
L<SL::PriceSource::Base>,
125
L<SL::PriceSource::ALL>
126

  
127
=head1 BUGS
128

  
129
None yet. :)
130

  
131
=head1 AUTHOR
132

  
133
Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>
134

  
135
=cut

Auch abrufbar als: Unified diff