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
sub empty_price {
SL::PriceSource::Price->new(
source => '',
description => t8('None (PriceSource)'),
);
}
......
SL::PriceSource - mixin for price_sources in record items
=head1 SYNOPSIS
=head1 DESCRIPTION
# in record item class
PriceSource is an interface that allows generic algorithms to be plugged
together to calculate available prices for a position in a record.
use SL::PriceSource;
Each algorithm can access details of the record to realize dependancies on
part, customer, vendor, date, quantity etc, which was previously not possible.
# later on:
=head1 BACKGROUND AND PHILOSOPY
$record_item->all_price_sources
$record_item->price_source # get
$record_item->price_source($c) # set
sql ledger and subsequently Lx-Office had three prices per part: sellprice,
listprice and lastcost. At the moment a part is loaded into a record, the
applicable price is copied and after that free to be changed.
$record_item->update_price_source # set price to calculated
Later on additional things joined. Various types of discount, vendor pricelists
and the infamous price groups. The problem is not that those didn't work, the
problem is, that they had to guess to much when to change a price with the
available price from database, and when to leave the user entered price.
=head1 DESCRIPTION
Unrelated to that, users asked for more ways to store special prices, based on
qty (block pricing, bulk discount), based on date (special offers), based on
customers (special terms), up to full blown calculation modules.
On a third front sales personnel asked for ways to see what price options a
position in a quotation has, and wanted information available when a price
offer changed.
Price sources put that together by making some compromises:
=over 4
=item 1.
Only change the price on creation of a position or when asked to.
=item 2.
Either set the price from a price source and let it be read only, or use a free
price.
=item 3.
Save the origin of each price with the record so that the calculation can be
reproduced.
=item 4.
Make price calculation flexible and pluggable.
=back
The first point creates user security by never changing a price for them
without their explicit consent, eliminating all problems originating from
trying to be smart. The second and third one ensure that later on the
calculation can be repeated so that invalid prices can be caught (because for
example the special offer is no longer valid), and so that sales personnel have
information about rising or falling prices. The fourth point ensures that
insular calculation processes can be developed independant of the core code.
=head1 INTERFACE METHODS
=over 4
=item C<new PARAMS>
C<PARAMS> must contain both C<record> and C<record_item>. C<record_item> does
not have to be registered in C<record>.
=item C<price_from_source>
Attempts to retrieve a formerly calculated price with the same conditions
=item C<available_prices>
Returns all available prices.
=item C<best_price>
This mixin provides a way to use price_source objects from within a record item.
Record items in this contest mean OrderItems, InvoiceItems and
DeliveryOrderItems.
Attempts to get the best available price. returns L<empty_price> if no price is found.
=head1 FUNCTIONS
=item C<empty_price>
price_sources
A special empty price, that does not change the previously entered price, and
opens the price field to manual changes.
returns a list of price_source objects which are created with the current record
item.
=back
active_price_source
=head1 SEE ALSO
returns the object representing the currently chosen price_source method or
undef if custom price is chosen. Note that this must not necessarily be the
active price, if something affecting the price_source has changed, the price
calculated can differ from the price in the record. It is the responsibility of
the implementing code to decide what to do in this case.
L<SL::PriceSource::Base>,
L<SL::PriceSource::Price>,
L<SL::PriceSource::ALL>
=head1 BUGS
SL/PriceSource/Base.pm
=head1 NAME
SL::PriceSource::Base - <oneliner description>
SL::PriceSource::Base - this is the base class for price source adapters
=head1 SYNOPSIS
# in consuming module
# TODO: thats bullshit, theres no need to have this pollute the namespace
# make a manager that handles this
# working example adapter:
package SL::PriceSource::FiveOnEverything;
my @list_of_price_sources = $record_item->price_sources;
for (@list_of_price_sources) {
my $internal_name = $_->name;
my $translated_name = $_->description;
my $price = $_->price;
use parent qw(SL::PriceSource::Base);
# used as internal identifier
sub name { 'simple' }
# used in frontend to signal where this comes from
sub description { t8('Simple') }
my $price = SL::PriceSource::Price->new(
price => 5,
description => t8('Only today 5$ on everything!'),
price_source => $self,
);
# give list of prices that this
sub available_prices {
return ($price);
}
$record_item->set_active_price_source($price_source) # equivalent to:
$record_item->active_price_source($price_source->name);
$record_item->sellprice($price_source->price);
sub best_price {
return $price;
}
# for finer control
$price_source->needed_params
$price_source->supported_params
sub price_from_source {
return $price;
}
=head1 DESCRIPTION
PriceSource is an interface that allows generic algorithms to be used, to
calculate a price for a position in a record.
See L<SL::PriceSource> for information about the mechanism.
This is the base class for a price source algorithm. To play well, you'll have
to implement a number of interface methods and be aware of a number of corner
conditions.
=head1 AVAILABLE METHODS
=over 4
=item C<record_item>
=item C<record>
C<record> can be any one of L<SL::DB::Order>, L<SL::DB::DeliveryOrder>,
L<SL::DB::Invoice>, L<SL::DB::PurchaseInvoice>. C<record_item> is of the
corresponding position type.
You can assume that both are filled with all information available at the time.
C<part> and C<customer>/C<vendor> as well as C<is_sales> can be relied upon. You must NOT
rely on both being linked together, in particular
$self->record_item->record # don't do that
If any such price_source algorithm is known to the system, a user can chose
which of them should be used to claculate the price displayed in the record.
is not guaranteed to work.
The algorithm is saved togetherwith the target price, so that changes in the
record can recalculate the price accordingly, and otherwise manual changes to
the price can reset the price_source used to custom (aka no price_source).
Also these are copies and not the original documents. Do not try to change
anything and do not save those.
=item C<part>
Shortcut to C<< record_item->part >>
=back
=head1 INTERFACE METHODS
......
=item C<name>
Should return a unique internal name. Should be entered in
L<SL::PriceSource::ALL> so that a name_to_class lookup works.
Must return a unique internal name. Must be entered in
L<SL::PriceSource::ALL>.
=item C<description>
Should return a translated name.
Must return a translated name to be used in frontend. Will be used, to
distinguish the origin of different prices.
=item C<available_prices>
=item C<needed_params>
Must return a list of all prices that you algorithm can recommend the user
for the current situation. Each price must have a unique spec that can be used
to recreate it later. Try to be brief, no one needs 20 different price
suggestions.
Should return a list of elements that a record_item NEEDS to be used with this calulation.
=item C<best_price>
Both C<needed_params> nad C<supported_params> are purely informational at this point.
Must return what you think of as the best matching price in your
C<available_prices>. This does not have to be the lowest price, but it will be
compared later to other price sources, and the lowest will be set.
=item C<supported_params>
=item C<price_from_source SOURCE, SPEC>
Should return a list of elements that a record_item MAY HAVE to be used with this calulation.
Must recreate the price from C<SPEC> and return. For reference, the complete
C<SOURCE> entry from C<record_item.active_price_source> is included.
Both C<needed_params> nad C<supported_params> are purely informational at this point.
Note that constraints from the rest of the C<record> do not apply anymore. If
information needed for the retrieval can be deleted elsewhere, then you must
guard against that.
=item C<price>
If the price for the same coditions changed, return the new price. It will be
presented as an option to the user if the record is still editable.
Calculate a price and return. Do not mutate the record_item. Should will return
undef if price is not applicable to the current record_item.
If the price is not valid anymore or not reconstructable, return a price with
C<price_source> and C<spec> set to the same values as before but with
C<invalid> or C<missing> set.
=back
=head1 TRAPS AND CORNER CASES
=over 4
=item *
Be aware that all 8 types of record will be passed to your algorithm. If you
don't serve some of them, just return emptry lists on C<available_prices> and
C<best_price>
=item *
Information in C<record> might be missing. Especially on newly or automatically
created records there might be fields not set at all.
=item *
Records will not be calculated. If you need tax data or position totals, you
need to invoke that for yourself.
=item *
Accessor methods might not be present in some of the record types.
=item *
You do not need to do price factor and row discount calculation. These will be
done automatically afterwards. You do have to include customer/vendor discount
if your price interacts with those.
=item *
The price field in purchase records is still C<sellprice>.
=item *
C<source> and C<spec> are tainted. If you store data directly in C<spec>, sanitize.
=head1 SEE ALSO
L<SL::PriceSource>,
L<SL::PriceSource::Price>,
L<SL::PriceSource::ALL>
=head1 BUGS
None yet. :)
SL/PriceSource/Price.pm
use parent 'SL::DB::Object';
use Rose::Object::MakeMethods::Generic (
scalar => [ qw(price description spec price_source) ],
scalar => [ qw(price description spec price_source invalid missing) ],
array => [ qw(depends_on) ]
);
......
}
1;
__END__
=encoding utf-8
=head1 NAME
SL::PriceSource::Price - contrainer to pass calculated prices around
=head1 SYNOPSIS
# in PriceSource::Base implementation
$price = SL::PriceSource::Price->new(
price => 10.3,
spec => '10.3', # something you can easily parse later
description => t8('Fix price 10.3'),
price_source => $self,
)
# special empty price in SL::PriceSource
SL::PriceSource::Price->new(
description => t8('None (PriceSource)'),
);
# invalid price
SL::PriceSource::Price->new(
price => $original_price,
spec => $original_spec,
description => $original_description,
invalid => t8('Offer expired #1 weeks ago', $dt->delta_weeks),
price_source => $self,
);
# missing price
SL::PriceSource::Price->new(
price => $original_price, # will keep last entered price
spec => $original_spec,
description => '',
missing => t8('Um, sorry, cannot find that one'),
price_source => $self,
);
=head1 DESCRIPTION
See L<SL::PriceSource> for information about the mechanism.
This is a container for prices that are generated by L<SL::PriceSource::Base>
implementations.
=head1 CONSTRUCTOR FIELDS
=over 4
=item C<price>
The price. A price of 0 is special and is considered undesirable. If passed as
part of C<available_prices> it will be filtered out. If returned as
C<best_price> or C<price_from_source> it will be warned about.
=item C<spec>
A unique string that can later be understood by the creating implementation.
Can be empty if the implementation only supports one price for a given
record_item.
=item C<description>
A localized short description of the origins of this price.
=item C<price_source>
A ref to the creating algorithm.
=item C<missing>
OPTIONAL. Both indicator and localized message that the price with this spec
could not be reproduced and should be changed.
=item C<invalid>
OPTIONAL. Both indicator and localized message that the conditions for this
price are no longer valid, and that the price should be changed.
=back
=head1 SEE ALSO
L<SL::PriceSource>,
L<SL::PriceSource::Base>,
L<SL::PriceSource::ALL>
=head1 BUGS
None yet. :)
=head1 AUTHOR
Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>
=cut

Auch abrufbar als: Unified diff