X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=SL%2FPriceSource.pm;h=c8cdc6cd197a2201da5ee9ac6b56535aae91a272;hb=590708a7a8d02ecd60267271f276e975c08d56c6;hp=9a3a978c5ce1debd1292318bdf71feefe9d751e8;hpb=eebe8e90991eacadb6fbd20a648c152017a620c7;p=kivitendo-erp.git diff --git a/SL/PriceSource.pm b/SL/PriceSource.pm index 9a3a978c5..c8cdc6cd1 100644 --- a/SL/PriceSource.pm +++ b/SL/PriceSource.pm @@ -3,20 +3,21 @@ package SL::PriceSource; use strict; use parent 'SL::DB::Object'; use Rose::Object::MakeMethods::Generic ( - scalar => [ qw(record_item) ], + scalar => [ qw(record_item record) ], + 'array --get_set_init' => [ qw(all_price_sources) ], ); -use List::UtilsBy qw(min_by); +use List::UtilsBy qw(min_by max_by); use SL::PriceSource::ALL; use SL::PriceSource::Price; use SL::Locale::String; -sub all_price_sources { +sub init_all_price_sources { my ($self) = @_; - return map { - $_->new(record_item => $self->record_item) - } SL::PriceSource::ALL->all_price_sources + [ map { + $_->new(record_item => $self->record_item, record => $self->record) + } SL::PriceSource::ALL->all_enabled_price_sources ] } sub price_from_source { @@ -26,7 +27,7 @@ sub price_from_source { my $class = SL::PriceSource::ALL->price_source_class_by_name($source_name); return $class - ? $class->new(record_item => $self->record_item)->price_from_source($source, $spec) + ? $class->new(record_item => $self->record_item, record => $self->record)->price_from_source($source, $spec) : empty_price(); } @@ -34,13 +35,21 @@ sub available_prices { map { $_->available_prices } $_[0]->all_price_sources; } +sub available_discounts { + return if $_[0]->record_item->part->not_discountable; + map { $_->available_discounts } $_[0]->all_price_sources; +} + sub best_price { - min_by { $_->price } map { $_->best_price } $_[0]->all_price_sources; + min_by { $_->price } max_by { $_->priority } grep { $_->price > 0 } grep { $_ } map { $_->best_price } $_[0]->all_price_sources; +} + +sub best_discount { + max_by { $_->discount } max_by { $_->priority } grep { $_->discount } grep { $_ } map { $_->best_discount } $_[0]->all_price_sources; } sub empty_price { SL::PriceSource::Price->new( - source => '', description => t8('None (PriceSource)'), ); } @@ -55,40 +64,98 @@ __END__ 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 dependencies on +part, customer, vendor, date, quantity etc, which was previously not possible. - # later on: +=head1 BACKGROUND AND PHILOSOPHY - $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 it is free to be changed. - $record_item->update_price_source # set price to calculated +Later on additional things were added. 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 too much when to change a price with the +available price from the 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 independent of the core code. + +=head1 INTERFACE METHODS + +=over 4 + +=item C + +C must contain both C and C. C does +not have to be registered in C. + +=item C + +Attempts to retrieve a formerly calculated price with the same conditions + +=item C + +Returns all available prices. + +=item C -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 if no price is found. -=head1 FUNCTIONS +=item C -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, +L, +L =head1 BUGS