1 package SL::PriceSource;
 
   4 use parent 'SL::DB::Object';
 
   5 use Rose::Object::MakeMethods::Generic (
 
   6   scalar => [ qw(record_item record) ],
 
   9 use List::UtilsBy qw(min_by max_by);
 
  10 use SL::PriceSource::ALL;
 
  11 use SL::PriceSource::Price;
 
  12 use SL::Locale::String;
 
  14 sub all_price_sources {
 
  18     $_->new(record_item => $self->record_item, record => $self->record)
 
  19   } SL::PriceSource::ALL->all_enabled_price_sources
 
  22 sub price_from_source {
 
  23   my ($self, $source) = @_;
 
  24   my ($source_name, $spec) = split m{/}, $source, 2;
 
  26   my $class = SL::PriceSource::ALL->price_source_class_by_name($source_name);
 
  29     ? $class->new(record_item => $self->record_item, record => $self->record)->price_from_source($source, $spec)
 
  33 sub available_prices {
 
  34   map { $_->available_prices } $_[0]->all_price_sources;
 
  37 sub available_discounts {
 
  38   return if $_[0]->record_item->part->not_discountable;
 
  39   map { $_->available_discounts } $_[0]->all_price_sources;
 
  43   min_by { $_->price } max_by { $_->priority } grep { $_->price > 0 } grep { $_ } map { $_->best_price } $_[0]->all_price_sources;
 
  47   max_by { $_->discount } max_by { $_->priority } grep { $_->discount } grep { $_ } map { $_->best_discount } $_[0]->all_price_sources;
 
  51   SL::PriceSource::Price->new(
 
  52     description => t8('None (PriceSource)'),
 
  64 SL::PriceSource - mixin for price_sources in record items
 
  68 PriceSource is an interface that allows generic algorithms to be plugged
 
  69 together to calculate available prices for a position in a record.
 
  71 Each algorithm can access details of the record to realize dependencies on
 
  72 part, customer, vendor, date, quantity etc, which was previously not possible.
 
  74 =head1 BACKGROUND AND PHILOSOPY
 
  76 sql ledger and subsequently Lx-Office had three prices per part: sellprice,
 
  77 listprice and lastcost. At the moment a part is loaded into a record, the
 
  78 applicable price is copied and after that free to be changed.
 
  80 Later on additional things joined. Various types of discount, vendor pricelists
 
  81 and the infamous price groups. The problem is not that those didn't work, the
 
  82 problem is, that they had to guess to much when to change a price with the
 
  83 available price from database, and when to leave the user entered price.
 
  85 Unrelated to that, users asked for more ways to store special prices, based on
 
  86 qty (block pricing, bulk discount), based on date (special offers), based on
 
  87 customers (special terms), up to full blown calculation modules.
 
  89 On a third front sales personnel asked for ways to see what price options a
 
  90 position in a quotation has, and wanted information available when a price
 
  93 Price sources put that together by making some compromises:
 
  99 Only change the price on creation of a position or when asked to.
 
 103 Either set the price from a price source and let it be read only, or use a free
 
 108 Save the origin of each price with the record so that the calculation can be
 
 113 Make price calculation flexible and pluggable.
 
 117 The first point creates user security by never changing a price for them
 
 118 without their explicit consent, eliminating all problems originating from
 
 119 trying to be smart. The second and third one ensure that later on the
 
 120 calculation can be repeated so that invalid prices can be caught (because for
 
 121 example the special offer is no longer valid), and so that sales personnel have
 
 122 information about rising or falling prices. The fourth point ensures that
 
 123 insular calculation processes can be developed independent of the core code.
 
 125 =head1 INTERFACE METHODS
 
 131 C<PARAMS> must contain both C<record> and C<record_item>. C<record_item> does
 
 132 not have to be registered in C<record>.
 
 134 =item C<price_from_source>
 
 136 Attempts to retrieve a formerly calculated price with the same conditions
 
 138 =item C<available_prices>
 
 140 Returns all available prices.
 
 144 Attempts to get the best available price. returns L<empty_price> if no price is found.
 
 148 A special empty price, that does not change the previously entered price, and
 
 149 opens the price field to manual changes.
 
 155 L<SL::PriceSource::Base>,
 
 156 L<SL::PriceSource::Price>,
 
 157 L<SL::PriceSource::ALL>
 
 165 Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>