kivitendo/SL/PriceSource.pm @ 79b7fc43
eebe8e90 | Sven Schöling | package SL::PriceSource;
|
||
use strict;
|
||||
use parent 'SL::DB::Object';
|
||||
use Rose::Object::MakeMethods::Generic (
|
||||
0409db7c | Sven Schöling | scalar => [ qw(record_item record) ],
|
||
1c311d64 | Sven Schöling | 'array --get_set_init' => [ qw(all_price_sources) ],
|
||
eebe8e90 | Sven Schöling | );
|
||
89b26688 | Sven Schöling | use List::UtilsBy qw(min_by max_by);
|
||
eebe8e90 | Sven Schöling | use SL::PriceSource::ALL;
|
||
use SL::PriceSource::Price;
|
||||
use SL::Locale::String;
|
||||
1c311d64 | Sven Schöling | sub init_all_price_sources {
|
||
eebe8e90 | Sven Schöling | my ($self) = @_;
|
||
1c311d64 | Sven Schöling | [ map {
|
||
0409db7c | Sven Schöling | $_->new(record_item => $self->record_item, record => $self->record)
|
||
1c311d64 | Sven Schöling | } SL::PriceSource::ALL->all_enabled_price_sources ]
|
||
eebe8e90 | Sven Schöling | }
|
||
sub price_from_source {
|
||||
my ($self, $source) = @_;
|
||||
my ($source_name, $spec) = split m{/}, $source, 2;
|
||||
my $class = SL::PriceSource::ALL->price_source_class_by_name($source_name);
|
||||
return $class
|
||||
89b26688 | Sven Schöling | ? $class->new(record_item => $self->record_item, record => $self->record)->price_from_source($source, $spec)
|
||
eebe8e90 | Sven Schöling | : empty_price();
|
||
}
|
||||
sub available_prices {
|
||||
map { $_->available_prices } $_[0]->all_price_sources;
|
||||
}
|
||||
89b26688 | Sven Schöling | sub available_discounts {
|
||
67f06c1e | Sven Schöling | return if $_[0]->record_item->part->not_discountable;
|
||
89b26688 | Sven Schöling | map { $_->available_discounts } $_[0]->all_price_sources;
|
||
}
|
||||
eebe8e90 | Sven Schöling | sub best_price {
|
||
9f666261 | Sven Schöling | min_by { $_->price } max_by { $_->priority } grep { $_->price > 0 } grep { $_ } map { $_->best_price } $_[0]->all_price_sources;
|
||
89b26688 | Sven Schöling | }
|
||
sub best_discount {
|
||||
9f666261 | Sven Schöling | max_by { $_->discount } max_by { $_->priority } grep { $_->discount } grep { $_ } map { $_->best_discount } $_[0]->all_price_sources;
|
||
eebe8e90 | Sven Schöling | }
|
||
sub empty_price {
|
||||
SL::PriceSource::Price->new(
|
||||
description => t8('None (PriceSource)'),
|
||||
);
|
||||
}
|
||||
1;
|
||||
__END__
|
||||
=encoding utf-8
|
||||
=head1 NAME
|
||||
SL::PriceSource - mixin for price_sources in record items
|
||||
418f0e70 | Sven Schöling | =head1 DESCRIPTION
|
||
eebe8e90 | Sven Schöling | |||
418f0e70 | Sven Schöling | PriceSource is an interface that allows generic algorithms to be plugged
|
||
together to calculate available prices for a position in a record.
|
||||
eebe8e90 | Sven Schöling | |||
049e49fe | Sven Schöling | Each algorithm can access details of the record to realize dependencies on
|
||
418f0e70 | Sven Schöling | part, customer, vendor, date, quantity etc, which was previously not possible.
|
||
eebe8e90 | Sven Schöling | |||
fd6f0f82 | Geoffrey Richardson | =head1 BACKGROUND AND PHILOSOPHY
|
||
eebe8e90 | Sven Schöling | |||
418f0e70 | Sven Schöling | 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
|
||||
fd6f0f82 | Geoffrey Richardson | applicable price is copied and after that it is free to be changed.
|
||
eebe8e90 | Sven Schöling | |||
fd6f0f82 | Geoffrey Richardson | Later on additional things were added. Various types of discount, vendor pricelists
|
||
418f0e70 | Sven Schöling | and the infamous price groups. The problem is not that those didn't work, the
|
||
fd6f0f82 | Geoffrey Richardson | 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.
|
||||
eebe8e90 | Sven Schöling | |||
418f0e70 | Sven Schöling | 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
|
||||
049e49fe | Sven Schöling | insular calculation processes can be developed independent of the core code.
|
||
418f0e70 | Sven Schöling | |||
=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>
|
||||
eebe8e90 | Sven Schöling | |||
418f0e70 | Sven Schöling | Attempts to get the best available price. returns L<empty_price> if no price is found.
|
||
eebe8e90 | Sven Schöling | |||
418f0e70 | Sven Schöling | =item C<empty_price>
|
||
eebe8e90 | Sven Schöling | |||
418f0e70 | Sven Schöling | A special empty price, that does not change the previously entered price, and
|
||
opens the price field to manual changes.
|
||||
eebe8e90 | Sven Schöling | |||
418f0e70 | Sven Schöling | =back
|
||
eebe8e90 | Sven Schöling | |||
418f0e70 | Sven Schöling | =head1 SEE ALSO
|
||
eebe8e90 | Sven Schöling | |||
418f0e70 | Sven Schöling | L<SL::PriceSource::Base>,
|
||
L<SL::PriceSource::Price>,
|
||||
L<SL::PriceSource::ALL>
|
||||
eebe8e90 | Sven Schöling | |||
=head1 BUGS
|
||||
None yet. :)
|
||||
=head1 AUTHOR
|
||||
Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>
|
||||
=cut
|