From: Sven Schöling Date: Tue, 14 Oct 2014 13:45:01 +0000 (+0200) Subject: PriceSource: Rabattbehandlung X-Git-Tag: release-3.2.0beta~155 X-Git-Url: http://wagnertech.de/git?a=commitdiff_plain;h=89b2668811eac6023ad58322e2f9970ddb6a27c9;p=kivitendo-erp.git PriceSource: Rabattbehandlung --- diff --git a/SL/DB/MetaSetup/DeliveryOrderItem.pm b/SL/DB/MetaSetup/DeliveryOrderItem.pm index d4789d640..c9b90e399 100644 --- a/SL/DB/MetaSetup/DeliveryOrderItem.pm +++ b/SL/DB/MetaSetup/DeliveryOrderItem.pm @@ -9,30 +9,31 @@ use base qw(SL::DB::Object); __PACKAGE__->meta->table('delivery_order_items'); __PACKAGE__->meta->columns( - base_qty => { type => 'float', scale => 4 }, - cusordnumber => { type => 'text' }, - delivery_order_id => { type => 'integer', not_null => 1 }, - description => { type => 'text' }, - discount => { type => 'float', scale => 4 }, - id => { type => 'integer', not_null => 1, sequence => 'delivery_order_items_id' }, - itime => { type => 'timestamp', default => 'now()' }, - lastcost => { type => 'numeric', precision => 15, scale => 5 }, - longdescription => { type => 'text' }, - marge_price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 }, - mtime => { type => 'timestamp' }, - ordnumber => { type => 'text' }, - parts_id => { type => 'integer', not_null => 1 }, - price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 }, - price_factor_id => { type => 'integer' }, - pricegroup_id => { type => 'integer' }, - project_id => { type => 'integer' }, - qty => { type => 'numeric', precision => 25, scale => 5 }, - reqdate => { type => 'date' }, - sellprice => { type => 'numeric', precision => 15, scale => 5 }, - serialnumber => { type => 'text' }, - transdate => { type => 'text' }, - unit => { type => 'varchar', length => 20 }, - active_price_source => { type => 'text', default => '', not_null => 1 }, + active_discount_source => { type => 'text', default => '', not_null => 1 }, + active_price_source => { type => 'text', default => '', not_null => 1 }, + base_qty => { type => 'float', scale => 4 }, + cusordnumber => { type => 'text' }, + delivery_order_id => { type => 'integer', not_null => 1 }, + description => { type => 'text' }, + discount => { type => 'float', scale => 4 }, + id => { type => 'integer', not_null => 1, sequence => 'delivery_order_items_id' }, + itime => { type => 'timestamp', default => 'now()' }, + lastcost => { type => 'numeric', precision => 15, scale => 5 }, + longdescription => { type => 'text' }, + marge_price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 }, + mtime => { type => 'timestamp' }, + ordnumber => { type => 'text' }, + parts_id => { type => 'integer', not_null => 1 }, + price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 }, + price_factor_id => { type => 'integer' }, + pricegroup_id => { type => 'integer' }, + project_id => { type => 'integer' }, + qty => { type => 'numeric', precision => 25, scale => 5 }, + reqdate => { type => 'date' }, + sellprice => { type => 'numeric', precision => 15, scale => 5 }, + serialnumber => { type => 'text' }, + transdate => { type => 'text' }, + unit => { type => 'varchar', length => 20 }, ); __PACKAGE__->meta->primary_key_columns([ 'id' ]); diff --git a/SL/DB/MetaSetup/InvoiceItem.pm b/SL/DB/MetaSetup/InvoiceItem.pm index 26ce472f9..68ed569e4 100644 --- a/SL/DB/MetaSetup/InvoiceItem.pm +++ b/SL/DB/MetaSetup/InvoiceItem.pm @@ -9,37 +9,38 @@ use base qw(SL::DB::Object); __PACKAGE__->meta->table('invoice'); __PACKAGE__->meta->columns( - allocated => { type => 'float', scale => 4 }, - assemblyitem => { type => 'boolean', default => 'false' }, - base_qty => { type => 'float', scale => 4 }, - cusordnumber => { type => 'text' }, - deliverydate => { type => 'date' }, - description => { type => 'text' }, - discount => { type => 'float', scale => 4 }, - donumber => { type => 'text' }, - fxsellprice => { type => 'numeric', precision => 15, scale => 5 }, - id => { type => 'integer', not_null => 1, sequence => 'invoiceid' }, - itime => { type => 'timestamp', default => 'now()' }, - lastcost => { type => 'numeric', precision => 15, scale => 5 }, - longdescription => { type => 'text' }, - marge_percent => { type => 'numeric', precision => 15, scale => 5 }, - marge_price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 }, - marge_total => { type => 'numeric', precision => 15, scale => 5 }, - mtime => { type => 'timestamp' }, - ordnumber => { type => 'text' }, - parts_id => { type => 'integer' }, - price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 }, - price_factor_id => { type => 'integer' }, - pricegroup_id => { type => 'integer' }, - project_id => { type => 'integer' }, - qty => { type => 'float', scale => 4 }, - sellprice => { type => 'numeric', precision => 15, scale => 5 }, - serialnumber => { type => 'text' }, - subtotal => { type => 'boolean', default => 'false' }, - trans_id => { type => 'integer' }, - transdate => { type => 'text' }, - unit => { type => 'varchar', length => 20 }, - active_price_source => { type => 'text', default => '', not_null => 1 }, + active_discount_source => { type => 'text', default => '', not_null => 1 }, + active_price_source => { type => 'text', default => '', not_null => 1 }, + allocated => { type => 'float', scale => 4 }, + assemblyitem => { type => 'boolean', default => 'false' }, + base_qty => { type => 'float', scale => 4 }, + cusordnumber => { type => 'text' }, + deliverydate => { type => 'date' }, + description => { type => 'text' }, + discount => { type => 'float', scale => 4 }, + donumber => { type => 'text' }, + fxsellprice => { type => 'numeric', precision => 15, scale => 5 }, + id => { type => 'integer', not_null => 1, sequence => 'invoiceid' }, + itime => { type => 'timestamp', default => 'now()' }, + lastcost => { type => 'numeric', precision => 15, scale => 5 }, + longdescription => { type => 'text' }, + marge_percent => { type => 'numeric', precision => 15, scale => 5 }, + marge_price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 }, + marge_total => { type => 'numeric', precision => 15, scale => 5 }, + mtime => { type => 'timestamp' }, + ordnumber => { type => 'text' }, + parts_id => { type => 'integer' }, + price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 }, + price_factor_id => { type => 'integer' }, + pricegroup_id => { type => 'integer' }, + project_id => { type => 'integer' }, + qty => { type => 'float', scale => 4 }, + sellprice => { type => 'numeric', precision => 15, scale => 5 }, + serialnumber => { type => 'text' }, + subtotal => { type => 'boolean', default => 'false' }, + trans_id => { type => 'integer' }, + transdate => { type => 'text' }, + unit => { type => 'varchar', length => 20 }, ); __PACKAGE__->meta->primary_key_columns([ 'id' ]); diff --git a/SL/DB/MetaSetup/OrderItem.pm b/SL/DB/MetaSetup/OrderItem.pm index 2bd6bf542..7e4b2c916 100644 --- a/SL/DB/MetaSetup/OrderItem.pm +++ b/SL/DB/MetaSetup/OrderItem.pm @@ -9,34 +9,35 @@ use base qw(SL::DB::Object); __PACKAGE__->meta->table('orderitems'); __PACKAGE__->meta->columns( - base_qty => { type => 'float', scale => 4 }, - cusordnumber => { type => 'text' }, - description => { type => 'text' }, - discount => { type => 'float', scale => 4 }, - id => { type => 'integer', not_null => 1, sequence => 'orderitemsid' }, - itime => { type => 'timestamp', default => 'now()' }, - lastcost => { type => 'numeric', precision => 15, scale => 5 }, - longdescription => { type => 'text' }, - marge_percent => { type => 'numeric', precision => 15, scale => 5 }, - marge_price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 }, - marge_total => { type => 'numeric', precision => 15, scale => 5 }, - mtime => { type => 'timestamp' }, - ordnumber => { type => 'text' }, - parts_id => { type => 'integer' }, - price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 }, - price_factor_id => { type => 'integer' }, - pricegroup_id => { type => 'integer' }, - project_id => { type => 'integer' }, - qty => { type => 'float', scale => 4 }, - reqdate => { type => 'date' }, - sellprice => { type => 'numeric', precision => 15, scale => 5 }, - serialnumber => { type => 'text' }, - ship => { type => 'float', scale => 4 }, - subtotal => { type => 'boolean', default => 'false' }, - trans_id => { type => 'integer' }, - transdate => { type => 'text' }, - unit => { type => 'varchar', length => 20 }, - active_price_source => { type => 'text', default => '', not_null => 1 }, + active_discount_source => { type => 'text', default => '', not_null => 1 }, + active_price_source => { type => 'text', default => '', not_null => 1 }, + base_qty => { type => 'float', scale => 4 }, + cusordnumber => { type => 'text' }, + description => { type => 'text' }, + discount => { type => 'float', scale => 4 }, + id => { type => 'integer', not_null => 1, sequence => 'orderitemsid' }, + itime => { type => 'timestamp', default => 'now()' }, + lastcost => { type => 'numeric', precision => 15, scale => 5 }, + longdescription => { type => 'text' }, + marge_percent => { type => 'numeric', precision => 15, scale => 5 }, + marge_price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 }, + marge_total => { type => 'numeric', precision => 15, scale => 5 }, + mtime => { type => 'timestamp' }, + ordnumber => { type => 'text' }, + parts_id => { type => 'integer' }, + price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 }, + price_factor_id => { type => 'integer' }, + pricegroup_id => { type => 'integer' }, + project_id => { type => 'integer' }, + qty => { type => 'float', scale => 4 }, + reqdate => { type => 'date' }, + sellprice => { type => 'numeric', precision => 15, scale => 5 }, + serialnumber => { type => 'text' }, + ship => { type => 'float', scale => 4 }, + subtotal => { type => 'boolean', default => 'false' }, + trans_id => { type => 'integer' }, + transdate => { type => 'text' }, + unit => { type => 'varchar', length => 20 }, ); __PACKAGE__->meta->primary_key_columns([ 'id' ]); diff --git a/SL/DO.pm b/SL/DO.pm index a37e516f1..478a12512 100644 --- a/SL/DO.pm +++ b/SL/DO.pm @@ -285,9 +285,9 @@ sub save { sellprice, discount, unit, reqdate, project_id, serialnumber, ordnumber, transdate, cusordnumber, lastcost, price_factor_id, price_factor, marge_price_factor, pricegroup_id, - active_price_source) + active_price_source, active_discount_source) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, - (SELECT factor FROM price_factors WHERE id = ?), ?, ?, ?)|; + (SELECT factor FROM price_factors WHERE id = ?), ?, ?, ?, ?)|; my $h_item = prepare_query($form, $dbh, $q_item); my $q_item_stock = @@ -343,7 +343,7 @@ sub save { conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}), $pricegroup_id, - $form->{"active_price_source_$i"}); + $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"}); do_statement($form, $h_item, $q_item, @values); my $stock_info = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_$i"}); @@ -690,7 +690,7 @@ sub retrieve { doi.reqdate, doi.project_id, doi.serialnumber, doi.lastcost, doi.ordnumber, doi.transdate, doi.cusordnumber, doi.longdescription, doi.price_factor_id, doi.price_factor, doi.marge_price_factor, doi.pricegroup_id, - doi.active_price_source, + doi.active_price_source, doi.active_discount_source, pr.projectnumber, dord.transdate AS dord_transdate, dord.donumber, pg.partsgroup FROM delivery_order_items doi diff --git a/SL/IR.pm b/SL/IR.pm index 6c5c5019b..ee676cb69 100644 --- a/SL/IR.pm +++ b/SL/IR.pm @@ -381,15 +381,16 @@ sub post_invoice { $query = qq|INSERT INTO invoice (id, trans_id, parts_id, description, longdescription, qty, base_qty, sellprice, fxsellprice, discount, allocated, unit, deliverydate, - project_id, serialnumber, price_factor_id, price_factor, marge_price_factor) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT factor FROM price_factors WHERE id = ?), ?, ?)|; + project_id, serialnumber, price_factor_id, price_factor, marge_price_factor, + active_price_source, active_discount_source) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT factor FROM price_factors WHERE id = ?), ?, ?, ?)|; @values = ($invoice_id, conv_i($form->{id}), conv_i($form->{"id_$i"}), $form->{"description_$i"}, $restricter->process($form->{"longdescription_$i"}), $form->{"qty_$i"} * -1, $baseqty * -1, $form->{"sellprice_$i"}, $fxsellprice, $form->{"discount_$i"}, $allocated, $form->{"unit_$i"}, conv_date($form->{deliverydate}), conv_i($form->{"project_id_$i"}), $form->{"serialnumber_$i"}, conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}), - conv_i($form->{"active_price_source_$i"}), + conv_i($form->{"active_price_source_$i"}), conv_i($form->{"active_discount_source_$i"}), ); do_query($form, $dbh, $query, @values); @@ -978,7 +979,7 @@ sub retrieve_invoice { i.id AS invoice_id, i.description, i.longdescription, i.qty, i.fxsellprice AS sellprice, i.parts_id AS id, i.unit, i.deliverydate, i.project_id, i.serialnumber, - i.price_factor_id, i.price_factor, i.marge_price_factor, i.discount, i.active_price_source, + i.price_factor_id, i.price_factor, i.marge_price_factor, i.discount, i.active_price_source, i.active_discount_source, p.partnumber, p.inventory_accno_id AS part_inventory_accno_id, pr.projectnumber, pg.partsgroup FROM invoice i diff --git a/SL/IS.pm b/SL/IS.pm index e7a437e41..227b4428b 100644 --- a/SL/IS.pm +++ b/SL/IS.pm @@ -761,9 +761,10 @@ sub post_invoice { sellprice, fxsellprice, discount, allocated, assemblyitem, unit, deliverydate, project_id, serialnumber, pricegroup_id, ordnumber, donumber, transdate, cusordnumber, base_qty, subtotal, - marge_percent, marge_total, lastcost, active_price_source, + marge_percent, marge_total, lastcost, active_price_source, active_discount_source, + price_factor_id, price_factor, marge_price_factor) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT factor FROM price_factors WHERE id = ?), ?)|; @values = ($invoice_id, conv_i($form->{id}), conv_i($form->{"id_$i"}), @@ -776,7 +777,7 @@ sub post_invoice { $form->{"cusordnumber_$i"}, $baseqty, $form->{"subtotal_$i"} ? 't' : 'f', $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"}, $form->{"lastcost_$i"}, - $form->{"active_price_source_$i"}, + $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"}, conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"})); do_query($form, $dbh, $query, @values); @@ -1678,7 +1679,7 @@ sub retrieve_invoice { i.id AS invoice_id, i.description, i.longdescription, i.qty, i.fxsellprice AS sellprice, i.discount, i.parts_id AS id, i.unit, i.deliverydate AS reqdate, i.project_id, i.serialnumber, i.id AS invoice_pos, i.pricegroup_id, i.ordnumber, i.donumber, i.transdate, i.cusordnumber, i.subtotal, i.lastcost, - i.price_factor_id, i.price_factor, i.marge_price_factor, i.active_price_source, + i.price_factor_id, i.price_factor, i.marge_price_factor, i.active_price_source, i.active_discount_source, p.partnumber, p.assembly, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id, p.formel, p.listprice, pr.projectnumber, pg.partsgroup, prg.pricegroup diff --git a/SL/OE.pm b/SL/OE.pm index 506db6bdc..b45449863 100644 --- a/SL/OE.pm +++ b/SL/OE.pm @@ -532,7 +532,7 @@ sub save { sellprice = ?, discount = ?, unit = ?, reqdate = ?, project_id = ?, serialnumber = ?, ship = ?, pricegroup_id = ?, ordnumber = ?, transdate = ?, cusordnumber = ?, subtotal = ?, marge_percent = ?, marge_total = ?, lastcost = ?, price_factor_id = ?, - active_price_source = ?, + active_price_source = ?, active_discount_source = ?, price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ? WHERE id = ? SQL @@ -547,7 +547,7 @@ SQL $form->{"cusordnumber_$i"}, $form->{"subtotal_$i"} ? 't' : 'f', $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"}, $form->{"lastcost_$i"}, - $form->{"active_price_source_$i"}, + $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"}, conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}), conv_i($orderitems_id), @@ -947,7 +947,7 @@ sub retrieve { o.sellprice, o.parts_id AS id, o.unit, o.discount, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id, o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost, o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription, - o.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_source, + o.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_source, o.active_discount_source, pr.projectnumber, p.formel, pg.partsgroup, o.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=o.pricegroup_id) as pricegroup FROM orderitems o diff --git a/SL/PriceSource.pm b/SL/PriceSource.pm index 241ae045e..e7a5906dd 100644 --- a/SL/PriceSource.pm +++ b/SL/PriceSource.pm @@ -6,7 +6,7 @@ use Rose::Object::MakeMethods::Generic ( scalar => [ qw(record_item record) ], ); -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; @@ -14,7 +14,7 @@ use SL::Locale::String; sub all_price_sources { my ($self) = @_; - return map { + map { $_->new(record_item => $self->record_item, record => $self->record) } SL::PriceSource::ALL->all_enabled_price_sources } @@ -26,7 +26,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,8 +34,16 @@ sub available_prices { map { $_->available_prices } $_[0]->all_price_sources; } +sub available_discounts { + map { $_->available_discounts } $_[0]->all_price_sources; +} + sub best_price { - min_by { $_->price } grep { $_->price > 0 } map { $_->best_price } $_[0]->all_price_sources; + min_by { $_->price } grep { $_->price > 0 } grep { $_ } map { $_->best_price } $_[0]->all_price_sources; +} + +sub best_discount { + max_by { $_->discount } grep { $_->discount } grep { $_ } map { $_->best_discount } $_[0]->all_price_sources; } sub empty_price { diff --git a/SL/PriceSource/ALL.pm b/SL/PriceSource/ALL.pm index a15d6b690..f9d86001c 100644 --- a/SL/PriceSource/ALL.pm +++ b/SL/PriceSource/ALL.pm @@ -4,17 +4,26 @@ use strict; use SL::PriceSource::Pricegroup; use SL::PriceSource::MasterData; use SL::PriceSource::Makemodel; +use SL::PriceSource::Customer; +use SL::PriceSource::Vendor; +use SL::PriceSource::Business; my %price_sources_by_name = ( master_data => 'SL::PriceSource::MasterData', + customer => 'SL::PriceSource::Customer', + vendor => 'SL::PriceSource::Vendor', pricegroup => 'SL::PriceSource::Pricegroup', makemodel => 'SL::PriceSource::Makemodel', + business => 'SL::PriceSource::Business', ); my @price_sources_order = qw( master_data + customer + vendor pricegroup makemodel + business ); sub all_enabled_price_sources { diff --git a/SL/PriceSource/Base.pm b/SL/PriceSource/Base.pm index 261897d25..513f3d44e 100644 --- a/SL/PriceSource/Base.pm +++ b/SL/PriceSource/Base.pm @@ -13,14 +13,22 @@ sub description { die 'description needs to be implemented' } sub available_prices { die 'available_prices needs to be implemented' } +sub available_discounts { die 'available_discounts needs to be implemented' } + sub best_price { die 'best_price needs to be implemented' } +sub best_discounts { die 'best_discounts needs to be implemented' } + sub price_from_source { die 'price_from_source needs to be implemented:' . "@_" } sub part { $_[0]->record_item->part; } +sub customer_vendor { + $_[0]->record->is_sales ? $_[0]->record->customer : $_[0]->record->vendor; +} + 1; __END__ @@ -98,6 +106,10 @@ anything and do not save those. Shortcut to C<< record_item->part >> +=item C + +Shortcut to C<< record->is_sales ? record->customer : record->vendor >> + =back =head1 INTERFACE METHODS @@ -121,16 +133,30 @@ 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. +=item C + +Must return a list of all prices that you algorithm can recommend the user +for the current situation. Each discount must have a unique spec that can be +used to recreate it later. Try to be brief, no one needs 20 different discount +suggestions. + =item C Must return what you think of as the best matching price in your C. 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 + +Must return what you think of as the best matching discount in your +C. This does not have to be the highest discount, but it +will be compared later to other price sources, and the highest will be set. + =item C -Must recreate the price from C and return. For reference, the complete -C entry from C is included. +Must recreate the price or discount from C and return. For reference, the +complete C entry from C or +C is included. Note that constraints from the rest of the C do not apply anymore. If information needed for the retrieval can be deleted elsewhere, then you must diff --git a/SL/PriceSource/Business.pm b/SL/PriceSource/Business.pm new file mode 100644 index 000000000..ca66c5dba --- /dev/null +++ b/SL/PriceSource/Business.pm @@ -0,0 +1,78 @@ +package SL::PriceSource::Business; + +use strict; +use parent qw(SL::PriceSource::Base); + +use SL::DB::Business; +use SL::PriceSource::Discount; +use SL::Locale::String; + +sub name { 'business' } + +sub description { t8('Business') } + +sub available_prices { } + +sub available_discounts { + my ($self, %params) = @_; + + return unless $self->customer_vendor; + return unless $self->customer_vendor->business; + return unless $self->customer_vendor->business->discount != 0; + + SL::PriceSource::Discount->new( + discount => $self->customer_vendor->business->discount, + spec => $self->customer_vendor->business->id, + description => t8('Business Discount'), + price_source => $self, + ); +} + +sub price_from_source { + my ($self, $source, $spec) = @_; + + my $business = SL::DB::Business->load_cached($spec); + + if (!$business) { + return SL::PriceSource::Discount->new( + missing => t8('Could not load this business'), + price_source => $self, + ) + } + + if (!$self->customer_vendor) { + return SL::PriceSource::Discount->new( + discount => $business->discount, + spec => $business->id, + description => t8('Business Discount'), + price_source => $self, + invalid => t8('This discount is only valid in records with customer or vendor'), + ) + } + + if ($business->id != $self->customer_vendor->business->id) { + return SL::PriceSource::Discount->new( + discount => $business->discount, + spec => $business->id, + description => t8('Business Discount'), + price_source => $self, + invalid => t8('This discount is only valid for business #1', $business->full_description), + ) + } + + return SL::PriceSource::Discount->new( + discount => $business->discount, + spec => $business->id, + description => t8('Business Discount'), + price_source => $self, + ); +} + +sub best_price { } + +sub best_discount { + &available_discounts; +} + +1; + diff --git a/SL/PriceSource/Customer.pm b/SL/PriceSource/Customer.pm new file mode 100644 index 000000000..6bf06bc30 --- /dev/null +++ b/SL/PriceSource/Customer.pm @@ -0,0 +1,78 @@ +package SL::PriceSource::Customer; + +use strict; +use parent qw(SL::PriceSource::Base); + +use SL::DB::Customer; +use SL::PriceSource::Discount; +use SL::Locale::String; + +sub name { 'customer_discount' } + +sub description { t8('Customer Discount') } + +sub available_prices { } + +sub available_discounts { + my ($self, %params) = @_; + + return unless $self->record->is_sales; + return unless $self->record->customer; + return unless $self->record->customer->discount != 0; + + SL::PriceSource::Discount->new( + discount => $self->record->customer->discount, + spec => $self->record->customer->id, + description => t8('Customer Discount'), + price_source => $self, + ); +} + +sub price_from_source { + my ($self, $source, $spec) = @_; + + my $customer = SL::DB::Customer->load_cached($spec); + + if (!$customer) { + return SL::PriceSource::Discount->new( + missing => t8('Could not load this customer'), + price_source => $self, + ) + } + + if (!$self->record->customer) { + return SL::PriceSource::Discount->new( + discount => $customer->discount, + spec => $customer->id, + description => t8('Customer Discount'), + price_source => $self, + invalid => t8('This discount is only valid in sales documents'), + ) + } + + if ($customer->id != $self->record->customer->id) { + return SL::PriceSource::Discount->new( + discount => $customer->discount, + spec => $customer->id, + description => t8('Customer Discount'), + price_source => $self, + invalid => t8('This discount is only valid for customer #1', $customer->full_description), + ) + } + + return SL::PriceSource::Discount->new( + discount => $customer->discount, + spec => $customer->id, + description => t8('Customer Discount'), + price_source => $self, + ); +} + +sub best_price { } + +sub best_discount { + &available_discounts; +} + +1; + diff --git a/SL/PriceSource/Discount.pm b/SL/PriceSource/Discount.pm new file mode 100644 index 000000000..6c95f9059 --- /dev/null +++ b/SL/PriceSource/Discount.pm @@ -0,0 +1,142 @@ +package SL::PriceSource::Discount; + +use strict; + +use parent 'SL::DB::Object'; +use Rose::Object::MakeMethods::Generic ( + scalar => [ qw(discount description spec price_source invalid missing) ], +); + +require SL::DB::Helper::Attr; +SL::DB::Helper::Attr::make(__PACKAGE__, + discount => 'numeric(15,5)', +); + +sub source { + $_[0]->price_source + ? $_[0]->price_source->name . '/' . $_[0]->spec + : ''; +} + +sub full_description { + my ($self) = @_; + + $self->price_source + ? $self->price_source->description . ': ' . $self->description + : $self->description +} + +sub source_description { + my ($self) = @_; + + $self->price_source + ? $self->price_source->description + : $self->description +} + +sub to_str { + "source: @{[ $_[0]->source ]}, discount: @{[ $_[0]->discount ]}, description: @{[ $_[0]->description ]}" +} + +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( + discount => 10, + spec => 'summersale2014', # something you can easily parse later + description => t8('10% discount during summer sale 2014'), + price_source => $self, + ) + + # special empty price in SL::PriceSource, for internal use. + SL::PriceSource::Price->new( + description => t8('None (PriceSource)'), + ); + + # price can't be restored + SL::PriceSource::Price->new( + missing => t8('Um, sorry, cannot find that one'), + price_source => $self, + ); + + # 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, + ); + +=head1 DESCRIPTION + +See L for information about the mechanism. + +This is a container for prices that are generated by L +implementations. + +=head1 CONSTRUCTOR FIELDS + +=over 4 + +=item C + +The discount in percent. A discount of 0 will be ignored. If passed as +part of C it will be filtered out. If returned as +C or C it will trigger a warning. + +=item C + +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 + +A localized short description of the origins of this price. + +=item C + +A ref to the creating algorithm. + +=item C + +OPTIONAL. Both indicator and localized message that the price with this spec +could not be reproduced and should be changed. + +If price is missing, you do not need to supply anything except C. + +=item C + +OPTIONAL. Both indicator and localized message that the conditions for this +price are no longer valid, and that the price should be changed. + +If price is missing, you do not need to supply anything except C. + +=back + +=head1 SEE ALSO + +L, +L, +L + +=head1 BUGS + +None yet. :) + +=head1 AUTHOR + +Sven Schoeling Es.schoeling@linet-services.deE + +=cut diff --git a/SL/PriceSource/Makemodel.pm b/SL/PriceSource/Makemodel.pm index 2a4f5b043..e1146a394 100644 --- a/SL/PriceSource/Makemodel.pm +++ b/SL/PriceSource/Makemodel.pm @@ -23,6 +23,8 @@ sub available_prices { $self->part->makemodels; } +sub available_discounts { } + sub price_from_source { my ($self, $source, $spec) = @_; @@ -42,6 +44,8 @@ sub best_price { } +sub best_discount { } + sub make_price_from_makemodel { my ($self, $makemodel) = @_; diff --git a/SL/PriceSource/MasterData.pm b/SL/PriceSource/MasterData.pm index 3f7c11d5d..e7ca2a3cc 100644 --- a/SL/PriceSource/MasterData.pm +++ b/SL/PriceSource/MasterData.pm @@ -20,6 +20,8 @@ sub available_prices { : ($self->make_lastcost, $self->make_listprice); } +sub available_discounts { } + sub price_from_source { my ($self, $source, $spec) = @_; @@ -35,6 +37,8 @@ sub best_price { : $_[0]->make_lastcost } +sub best_discount { } + sub make_sellprice { my ($self) = @_; diff --git a/SL/PriceSource/Price.pm b/SL/PriceSource/Price.pm index 1e8f0a299..71bd1ff4f 100644 --- a/SL/PriceSource/Price.pm +++ b/SL/PriceSource/Price.pm @@ -7,14 +7,14 @@ use Rose::Object::MakeMethods::Generic ( scalar => [ qw(price description spec price_source invalid missing) ], ); -use SL::DB::Helper::Attr; +require SL::DB::Helper::Attr; SL::DB::Helper::Attr::make(__PACKAGE__, price => 'numeric(15,5)', ); sub source { $_[0]->price_source - ? $_[0]->price_source->name . '/' . $_[0]->spec + ? $_[0]->price_source->name . '/' . $_[0]->spec : ''; } @@ -31,11 +31,11 @@ sub source_description { $self->price_source ? $self->price_source->description - : $self->description + : $self->description } sub to_str { - "source: @{[ $_[0]->source ]}, price: @{[ $_[0]->price]}, description: @{[ $_[0]->description ]}" + "source: @{[ $_[0]->source ]}, price: @{[ $_[0]->price ]}, description: @{[ $_[0]->description ]}" } 1; @@ -53,35 +53,31 @@ SL::PriceSource::Price - contrainer to pass calculated prices around # 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'), + spec => '3', # something you can easily parse later + description => t8('Fix price 10.3 for customer 3'), price_source => $self, ) - # special empty price in SL::PriceSource + # special empty price in SL::PriceSource, for internal use. SL::PriceSource::Price->new( description => t8('None (PriceSource)'), ); - # invalid price + # price can't be restored SL::PriceSource::Price->new( - price => $original_price, - spec => $original_spec, - description => $original_description, - invalid => t8('Offer expired #1 weeks ago', $dt->delta_weeks), + missing => t8('Um, sorry, cannot find that one'), price_source => $self, ); - # missing price + # invalid price SL::PriceSource::Price->new( - price => $original_price, # will keep last entered price + price => $original_price, spec => $original_spec, - description => '', - missing => t8('Um, sorry, cannot find that one'), + description => $original_description, + invalid => t8('Offer expired #1 weeks ago', $dt->delta_weeks), price_source => $self, ); - =head1 DESCRIPTION See L for information about the mechanism. @@ -97,7 +93,7 @@ implementations. The price. A price of 0 is special and is considered undesirable. If passed as part of C it will be filtered out. If returned as -C or C it will be warned about. +C or C it will trigger a warning. =item C @@ -118,11 +114,15 @@ A ref to the creating algorithm. OPTIONAL. Both indicator and localized message that the price with this spec could not be reproduced and should be changed. +If price is missing, you do not need to supply anything except C. + =item C OPTIONAL. Both indicator and localized message that the conditions for this price are no longer valid, and that the price should be changed. +If price is missing, you do not need to supply anything except C. + =back =head1 SEE ALSO diff --git a/SL/PriceSource/Pricegroup.pm b/SL/PriceSource/Pricegroup.pm index 127ec660e..8a322c5ac 100644 --- a/SL/PriceSource/Pricegroup.pm +++ b/SL/PriceSource/Pricegroup.pm @@ -33,6 +33,8 @@ sub available_prices { } @$prices; } +sub available_discounts { } + sub price_from_source { my ($self, $source, $spec) = @_; @@ -57,6 +59,8 @@ sub best_price { return $best_price || (); } +sub best_discount { } + sub make_price { my ($self, $price_obj) = @_; diff --git a/SL/PriceSource/Vendor.pm b/SL/PriceSource/Vendor.pm new file mode 100644 index 000000000..94d9bd01f --- /dev/null +++ b/SL/PriceSource/Vendor.pm @@ -0,0 +1,79 @@ +package SL::PriceSource::Vendor; + +use strict; +use parent qw(SL::PriceSource::Base); + +use SL::DB::Vendor; +use SL::PriceSource::Price; +use SL::Locale::String; + +sub name { 'vendor_discount' } + +sub description { t8('Vendor Discount') } + +sub available_prices { } + +sub available_discounts { + my ($self, %params) = @_; + + return if $self->record->is_sales; + return unless $self->record->vendor; + return unless $self->record->vendor->discount != 0; + + SL::PriceSource::Vendor->new( + discount => $self->record->vendor->discount, + spec => $self->record->vendor->id, + description => t8('Vendor Discount'), + price_source => $self, + ); +} + +sub price_from_source { + my ($self, $source, $spec) = @_; + + my $vendor = SL::DB::Vendor->load_cached($spec); + + if (!$vendor) { + return SL::PriceSource::Discount->new( + missing => t8('Could not load this vendor'), + price_source => $self, + ) + } + + if (!$self->record->vendor) { + return SL::PriceSource::Discount->new( + discount => $vendor->discount, + spec => $vendor->id, + description => t8('Vendor Discount'), + price_source => $self, + invalid => t8('This discount is only valid in purchase documents'), + ) + } + + if ($vendor->id != $self->record->vendor->id) { + return SL::PriceSource::Discount->new( + discount => $vendor->discount, + spec => $vendor->id, + description => t8('Vendor Discount'), + price_source => $self, + invalid => t8('This discount is only valid for vendor #1', $vendor->full_description), + ) + } + + return SL::PriceSource::Discount->new( + discount => $vendor->discount, + spec => $vendor->id, + description => t8('Vendor Discount'), + price_source => $self, + ); +} + + +sub best_price { } + +sub best_discount { + &available_discounts; +} + +1; + diff --git a/bin/mozilla/do.pl b/bin/mozilla/do.pl index b242acb67..362447a7c 100644 --- a/bin/mozilla/do.pl +++ b/bin/mozilla/do.pl @@ -427,7 +427,7 @@ sub update_delivery_order { map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} } keys %{ $form->{item_list}[0] }; $form->{"marge_price_factor_$i"} = $form->{item_list}->[0]->{price_factor}; - $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"} * (1 - $form->{tradediscount})); + $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}); $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}); $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"}); } diff --git a/bin/mozilla/io.pl b/bin/mozilla/io.pl index 565b3c5fd..57c2b8c54 100644 --- a/bin/mozilla/io.pl +++ b/bin/mozilla/io.pl @@ -226,6 +226,8 @@ sub display_row { for my $i (1 .. $numrows) { my %column_data = (); + my $record_item = $record->id && $record->items ? $record->items->[$i-1] : _make_record_item($i); + # undo formatting map { $form->{"${_}_$i"} = $form->parse_amount(\%myconfig, $form->{"${_}_$i"}) } qw(qty discount sellprice lastcost price_new price_old) @@ -235,8 +237,6 @@ sub display_row { $form->{"sellprice_$i"} = $form->{"price_new_$i"}; } - my $record_item = $record->id && $record->items ? $record->items->[$i-1] : _make_record_item($i); - # unit begin $form->{"unit_old_$i"} ||= $form->{"unit_$i"}; $form->{"selected_unit_$i"} ||= $form->{"unit_$i"}; @@ -310,14 +310,15 @@ sub display_row { } my $sellprice_value = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces); + my $discount_value = $form->format_amount(\%myconfig, $form->{"discount_$i"}); my $edit_prices = $main::auth->assert('edit_prices', 1) && !$::form->{"active_price_source_$i"}; + my $edit_discounts = $main::auth->assert('edit_prices', 1) && !$::form->{"active_discount_source_$i"}; $column_data{sellprice} = (!$edit_prices) ? $cgi->hidden( -name => "sellprice_$i", -id => "sellprice_$i", -value => $sellprice_value) . $sellprice_value : $cgi->textfield(-name => "sellprice_$i", -id => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value => $sellprice_value); - $column_data{discount} = (!$edit_prices) - ? $cgi->textfield(-readonly => "readonly", - -name => "discount_$i", -size => 3, -value => $form->format_amount(\%myconfig, $form->{"discount_$i"})) - : $cgi->textfield(-name => "discount_$i", -size => 3, -value => $form->format_amount(\%myconfig, $form->{"discount_$i"})); + $column_data{discount} = (!$edit_discounts) + ? $cgi->hidden( -name => "discount_$i", -id => "discount_$i", -value => $discount_value) . $discount_value . ' %' + : $cgi->textfield(-name => "discount_$i", -id => "discount_$i", -size => 3, -value => $discount_value); $column_data{linetotal} = $form->format_amount(\%myconfig, $linetotal, 2); $column_data{bin} = $form->{"bin_$i"}; @@ -325,14 +326,26 @@ sub display_row { if ($form->{"id_${i}"} && !$is_delivery_order) { my $price_source = SL::PriceSource->new(record_item => $record_item, record => $record); - my $price = $price_source->price_from_source($::form->{"active_price_source_$i"}); + my $price = $price_source->price_from_source($::form->{"active_price_source_$i"}); + my $discount = $price_source->price_from_source($::form->{"active_discount_source_$i"}); $column_data{price_source} .= $cgi->button(-value => $price->source_description, -onClick => "kivi.io.price_chooser($i)"); if ($price->source) { $column_data{price_source} .= ' ' . $cgi->img({src => 'image/flag-red.png', alt => $price->invalid, title => $price->invalid }) if $price->invalid; $column_data{price_source} .= ' ' . $cgi->img({src => 'image/flag-red.png', alt => $price->missing, title => $price->missing }) if $price->missing; - $column_data{price_source} .= ' ' . $cgi->img({src => 'image/up.png', alt => t8('This price has since gone up'), title => t8('This price has since gone up' ) }) if $price->price > $record_item->sellprice; - $column_data{price_source} .= ' ' . $cgi->img({src => 'image/down.png', alt => t8('This price has since gone down'), title => t8('This price has since gone down') }) if $price->price < $record_item->sellprice; - $column_data{price_source} .= ' ' . $cgi->img({src => 'image/ok.png', alt => t8('There is a better price available'), title => t8('There is a better price available') }) if $price->source ne $price_source->best_price->source; + if (!$price->missing && !$price->invalid) { + $column_data{price_source} .= ' ' . $cgi->img({src => 'image/up.png', alt => t8('This price has since gone up'), title => t8('This price has since gone up' ) }) if $price->price > $record_item->sellprice; + $column_data{price_source} .= ' ' . $cgi->img({src => 'image/down.png', alt => t8('This price has since gone down'), title => t8('This price has since gone down') }) if $price->price < $record_item->sellprice; + $column_data{price_source} .= ' ' . $cgi->img({src => 'image/ok.png', alt => t8('There is a better price available'), title => t8('There is a better price available') }) if $price->source ne $price_source->best_price->source; + } + } + if ($discount->source) { + $column_data{discount_source} .= ' ' . $cgi->img({src => 'image/flag-red.png', alt => $discount->invalid, title => $discount->invalid }) if $discount->invalid; + $column_data{discount_source} .= ' ' . $cgi->img({src => 'image/flag-red.png', alt => $discount->missing, title => $discount->missing }) if $discount->missing; + if (!$discount->missing && !$discount->invalid) { + $column_data{price_source} .= ' ' . $cgi->img({src => 'image/up.png', alt => t8('This discount has since gone up'), title => t8('This discount has since gone up') }) if $discount->discount * 100 > $record_item->discount; + $column_data{price_source} .= ' ' . $cgi->img({src => 'image/down.png', alt => t8('This discount has since gone down'), title => t8('This discount has since gone down') }) if $discount->discount * 100 < $record_item->discount; + $column_data{price_source} .= ' ' . $cgi->img({src => 'image/ok.png', alt => t8('There is a better discount available'), title => t8('There is a better discount available') }) if $discount->source ne $price_source->best_discount->source; + } } } @@ -428,7 +441,7 @@ sub display_row { $cgi->hidden("-name" => "unit_old_$i", "-value" => $form->{"selected_unit_$i"}), $cgi->hidden("-name" => "price_new_$i", "-value" => $form->format_amount(\%myconfig, $form->{"price_new_$i"})), map { ($cgi->hidden("-name" => $_, "-id" => $_, "-value" => $form->{$_})); } map { $_."_$i" } - (qw(orderitems_id bo price_old id inventory_accno bin partsgroup partnotes active_price_source + (qw(orderitems_id bo price_old id inventory_accno bin partsgroup partnotes active_price_source active_discount_source income_accno expense_accno listprice assembly taxaccounts ordnumber donumber transdate cusordnumber longdescription basefactor marge_absolut marge_percent marge_price_factor weight), @hidden_vars) ); @@ -469,7 +482,6 @@ sub select_item { $::form->header; my @item_list = map { - $_->{display_sellprice} = $_->{sellprice} * (1 - $::form->{tradediscount}); $_->{display_sellprice} /= $_->{price_factor} if ($_->{price_factor}); $_; } @{ $::form->{item_list} }; @@ -536,6 +548,13 @@ sub item_selected { $::form->{"active_price_source_$i"} = $best_price->source; } + my $best_discount = $price_source->best_discount; + + if ($best_discount) { + $::form->{"discount_$i"} = $best_discount->discount; + $::form->{"active_discount_source_$i"} = $best_discount->source; + } + $form->{"marge_price_factor_$i"} = $new_item->{price_factor}; @@ -557,11 +576,6 @@ sub item_selected { $form->{"sellprice_$i"} = $form->round_amount($form->{"sellprice_$i"}, $decimalplaces); } - - # tradediscount - if ($::form->{tradediscount}) { - $::form->{"sellprice_$i"} *= 1 - $::form->{tradediscount}; - } } map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) } @@ -710,7 +724,7 @@ sub remove_emptied_rows { transdate longdescription basefactor marge_total marge_percent marge_price_factor lastcost price_factor_id partnotes stock_out stock_in has_sernumber reqdate orderitems_id - active_price_source); + active_price_source active_discount_source); my $ic_cvar_configs = CVar->get_configs(module => 'IC'); push @flds, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs }; diff --git a/bin/mozilla/ir.pl b/bin/mozilla/ir.pl index d982e7c2b..303eae069 100644 --- a/bin/mozilla/ir.pl +++ b/bin/mozilla/ir.pl @@ -511,6 +511,20 @@ sub update { if ($sellprice) { $form->{"sellprice_$i"} = $sellprice; } else { + my $record = _make_record(); + my $price_source = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record); + my $best_price = $price_source->best_price; + my $best_discount = $price_source->best_discount; + + if ($best_price) { + $::form->{"sellprice_$i"} = $best_price->price; + $::form->{"active_price_source_$i"} = $best_price->source; + } + if ($best_discount) { + $::form->{"discount_$i"} = $best_discount->discount; + $::form->{"active_discount_source_$i"} = $best_discount->source; + } + # if there is an exchange rate adjust sellprice $form->{"sellprice_$i"} /= $exchangerate; } diff --git a/bin/mozilla/is.pl b/bin/mozilla/is.pl index 66f6b44c5..f7ef78d88 100644 --- a/bin/mozilla/is.pl +++ b/bin/mozilla/is.pl @@ -587,8 +587,21 @@ sub update { if ($sellprice) { $form->{"sellprice_$i"} = $sellprice; } else { + my $record = _make_record(); + my $price_source = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record); + my $best_price = $price_source->best_price; + my $best_discount = $price_source->best_discount; + + if ($best_price) { + $::form->{"sellprice_$i"} = $best_price->price; + $::form->{"active_price_source_$i"} = $best_price->source; + } + if ($best_discount) { + $::form->{"discount_$i"} = $best_discount->discount; + $::form->{"active_discount_source_$i"} = $best_discount->source; + } + # if there is an exchange rate adjust sellprice - $form->{"sellprice_$i"} *= (1 - $form->{tradediscount}); $form->{"sellprice_$i"} /= $exchangerate; } diff --git a/bin/mozilla/oe.pl b/bin/mozilla/oe.pl index cc67e746f..860d722d4 100644 --- a/bin/mozilla/oe.pl +++ b/bin/mozilla/oe.pl @@ -678,16 +678,20 @@ sub update { if ($sellprice) { $form->{"sellprice_$i"} = $sellprice; } else { - my $record = _make_record(); - my $price_source = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record); - my $best_price = $price_source->best_price; + my $record = _make_record(); + my $price_source = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record); + my $best_price = $price_source->best_price; + my $best_discount = $price_source->best_discount; if ($best_price) { $::form->{"sellprice_$i"} = $best_price->price; $::form->{"active_price_source_$i"} = $best_price->source; } + if ($best_discount) { + $::form->{"discount_$i"} = $best_discount->discount; + $::form->{"active_discount_source_$i"} = $best_discount->source; + } - $form->{"sellprice_$i"} *= (1 - $form->{tradediscount}); $form->{"sellprice_$i"} /= $exchangerate; # if there is an exchange rate adjust sellprice } diff --git a/js/kivi.io.js b/js/kivi.io.js index 109b20010..4758583e2 100644 --- a/js/kivi.io.js +++ b/js/kivi.io.js @@ -43,4 +43,10 @@ namespace('kivi.io', function(ns) { if (price_str) $('#sellprice_' + row).val(price_str); $('#update_button').click(); } + + ns.update_discount_source = function(row, source, discount_str) { + $('#active_discount_source_' + row).val(source); + if (discount_str) $('#discount_' + row).val(discount_str); + $('#update_button').click(); + } }); diff --git a/locale/de/all b/locale/de/all index c45b0ede4..67093eb8a 100755 --- a/locale/de/all +++ b/locale/de/all @@ -342,6 +342,7 @@ $self->{texts} = { 'Beratername' => 'Beratername', 'Beraternummer' => 'Beraternummer', 'Best Before' => 'Mindesthaltbarkeit', + 'Best Discount' => 'Bester Rabatt', 'Best Price' => 'Bester Preis', 'Bilanz' => 'Bilanz', 'Billable amount' => 'Abrechenbarer Betrag', @@ -383,6 +384,8 @@ $self->{texts} = { 'Buchungsgruppen' => 'Buchungsgruppen', 'Buchungskonto' => 'Buchungskonto', 'Buchungsnummer' => 'Buchungsnummer', + 'Business' => 'Kunden-/Lieferantentyp', + 'Business Discount' => 'Kunden-/Lieferantentyp-Rabatt', 'Business Number' => 'Firmennummer', 'Business Volume' => 'Geschäftsvolumen', 'Business evaluation' => 'Betriebswirtschaftliche Auswertung', @@ -561,6 +564,9 @@ $self->{texts} = { 'Could not load class #1 (#2): "#3"' => 'Konnte Klasse #1 (#2) nicht laden: "#3"', 'Could not load class #1, #2' => 'Konnte Klasse #1 nicht laden: "#2"', 'Could not load employee' => 'Konnte Benutzer nicht laden', + 'Could not load this business' => 'Konnte diesen Kunden-/Lieferantentyp nicht laden', + 'Could not load this customer' => 'Konnte diesen Kunden nicht laden', + 'Could not load this vendor' => 'Konnte diesen Lieferanten nicht laden', 'Could not print dunning.' => 'Die Mahnungen konnten nicht gedruckt werden.', 'Could not spawn ghostscript.' => 'Die Anwendung "ghostscript" konnte nicht gestartet werden.', 'Could not spawn the printer command.' => 'Die Druckanwendung konnte nicht gestartet werden.', @@ -669,6 +675,7 @@ $self->{texts} = { 'Customer' => 'Kunde', 'Customer (database ID)' => 'Kunde (Datenbank-ID)', 'Customer (name)' => 'Kunde (Name)', + 'Customer Discount' => 'Kundenrabatt', 'Customer Master Data' => 'Kundenstammdaten', 'Customer Name' => 'Kundenname', 'Customer Number' => 'Kundennummer', @@ -825,6 +832,7 @@ $self->{texts} = { 'Destination warehouse' => 'Ziellager', 'Destination warehouse and bin' => 'Ziellager und -lagerplatz', 'Detail view' => 'Detailanzeige', + 'Details' => 'Details', 'Details (one letter abbreviation)' => 'D', 'Dial command missing in kivitendo configuration\'s [cti] section' => 'Wählbefehl fehlt im Abschnitt [cti] der kivitendo-Konfiguration', 'Difference' => 'Differenz', @@ -834,6 +842,7 @@ $self->{texts} = { 'Discard duplicate entries in CSV file' => 'Doppelte Einträge in CSV-Datei verwerfen', 'Discard entries with duplicates in database or CSV file' => 'Einträge aus CSV-Datei verwerfen, die es bereits in der Datenbank oder der CSV-Datei gibt', 'Discount' => 'Rabatt', + 'Discounts' => 'Rabatte', 'Display' => 'Anzeigen', 'Display file' => 'Datei anzeigen', 'Display options' => 'Anzeigeoptionen', @@ -1601,6 +1610,7 @@ $self->{texts} = { 'No.' => 'Position', 'No/individual shipping address' => 'Keine/individuelle Lieferadresse', 'None' => 'Kein', + 'None (PriceSource Discount)' => 'Freier Rabatt', 'None (PriceSource)' => 'Freier Preis', 'Normal users cannot log in.' => 'Normale Benutzer können sich nicht anmelden.', 'Normalize Customer / Vendor names' => 'Normalisierung Kunden- / Lieferantennamen', @@ -1853,6 +1863,7 @@ $self->{texts} = { 'Pricegroup missing!' => 'Preisgruppe fehlt!', 'Pricegroup saved!' => 'Preisgruppe gespeichert!', 'Pricegroups' => 'Preisgruppen', + 'Prices' => 'Preise', 'Print' => 'Drucken', 'Print and Post' => 'Drucken und Buchen', 'Print automatically' => 'Automatisch ausdrucken', @@ -2634,6 +2645,7 @@ $self->{texts} = { 'There are still transfers not matching the qty of the delivery order. Stock operations can not be changed later. Do you really want to proceed?' => 'Einige der Lagerbewegungen sind nicht vollständig und Lagerbewegungen können nachträglich nicht mehr verändert werden. Wollen Sie wirklich fortfahren?', 'There are undefined currencies in your system.' => 'In Ihrer Datenbank wurden Währungen benutzt, die nicht ordnungsgemäß in den Währungen eingetragen wurden.', 'There are usually three ways to install Perl modules.' => 'Es gibt normalerweise drei Arten, ein Perlmodul zu installieren.', + 'There is a better discount available' => 'Es is ein besserer Rabatt verfügbar', 'There is a better price available' => 'Es ist ein besserer Preis verfügbar', 'There is already a taxkey 0 with tax rate not 0.' => 'Es existiert bereits ein Steuerschlüssel mit Steuersatz ungleich 0%.', 'There is an inconsistancy in your database.' => 'In Ihrer Datenbank sind Unstimmigkeiten vorhanden.', @@ -2653,6 +2665,14 @@ $self->{texts} = { 'This can be done with the following query:' => 'Dies kann mit der folgenden Datenbankabfrage erreicht werden:', 'This could have happened for two reasons:' => 'Dies kann aus zwei Gründen geschehen sein:', 'This customer number is already in use.' => 'Diese Kundennummer wird bereits verwendet.', + 'This discount has since gone down' => 'Dieser Rabatt ist mittlerweile niedriger', + 'This discount has since gone up' => 'Dieser Rabatt ist mittlerweile höher', + 'This discount is only valid for business #1' => 'Dieser Rabatt ist nur für Kunden-/Lieferantentyp #1 gültig', + 'This discount is only valid for customer #1' => 'Dieser Rabatt ist nur für Kunde #1 gültig', + 'This discount is only valid for vendor #1' => 'Dieser Rabatt ist nur für Lieferant #1 gültig', + 'This discount is only valid in purchase documents' => 'Dieser Rabatt ist nur in Einkaufsdokumenten gültig', + 'This discount is only valid in records with customer or vendor' => 'Dieser Rabatt ist nur in Dokumenten mit Kunde oder Lieferant gültig', + 'This discount is only valid in sales documents' => 'Dieser Rabatt ist nur in Verkaufsdokumenten gültig', 'This feature especially prevents mistakes by mixing up prior tax and sales tax.' => 'Dieses Feature vermeidet insbesondere Verwechslungen von Umsatz- und Vorsteuer.', 'This function requires the presence of articles with a time-based unit such as "h" or "min".' => 'Für diese Funktion mussen Artikel mit einer Zeit-basierten Einheit wie "Std" oder "min" existieren.', 'This group is valid for the following clients' => 'Diese Gruppe ist für die folgenden Mandanten gültig', @@ -2779,6 +2799,7 @@ $self->{texts} = { 'Unsupported image type (supported types: #1)' => 'Nicht unterstützter Bildtyp (unterstützte Typen: #1)', 'Until' => 'Bis', 'Update' => 'Erneuern', + 'Update Discount' => 'Rabatt übernehmen', 'Update Price' => 'Preis übernehmen', 'Update Prices' => 'Preise aktualisieren', 'Update SKR04: new tax account 3804 (19%)' => 'Update SKR04: neues Steuerkonto 3804 (19%) für innergemeinschaftlichen Erwerb', @@ -2830,6 +2851,7 @@ $self->{texts} = { 'Vendor' => 'Lieferant', 'Vendor (database ID)' => '(Datenbank-ID)', 'Vendor (name)' => 'Lieferant (Name)', + 'Vendor Discount' => 'Lieferantenrabatt', 'Vendor Invoice' => 'Einkaufsrechnung', 'Vendor Invoices & AP Transactions' => 'Einkaufsrechnungen & Kreditorenbuchungen', 'Vendor Name' => 'Lieferantenname', diff --git a/sql/Pg-upgrade2/recorditem_active_dicount_source.sql b/sql/Pg-upgrade2/recorditem_active_dicount_source.sql new file mode 100644 index 000000000..aea966a12 --- /dev/null +++ b/sql/Pg-upgrade2/recorditem_active_dicount_source.sql @@ -0,0 +1,8 @@ +-- @tag: recorditem_active_record_source +-- @description: Preisquellen: Rabatte +-- @depends: release_3_1_0 recorditem_active_price_source +-- @encoding: utf-8 + +ALTER TABLE orderitems ADD COLUMN active_discount_source TEXT NOT NULL DEFAULT ''; +ALTER TABLE delivery_order_items ADD COLUMN active_discount_source TEXT NOT NULL DEFAULT ''; +ALTER TABLE invoice ADD COLUMN active_discount_source TEXT NOT NULL DEFAULT ''; diff --git a/templates/webpages/oe/price_sources_dialog.html b/templates/webpages/oe/price_sources_dialog.html index 92ee4ebfe..263cfd627 100644 --- a/templates/webpages/oe/price_sources_dialog.html +++ b/templates/webpages/oe/price_sources_dialog.html @@ -3,12 +3,16 @@ [%- USE L %] [%- USE LxERP %] [% SET best_price = price_source.best_price %] +[% SET best_discount = price_source.best_discount %] +

[% 'Prices' | $T8 %]

+ + [%- IF price_source.record_item.active_price_source %] @@ -19,23 +23,67 @@ + [%- FOREACH price IN price_source.available_prices %] [%- IF price_source.record_item.active_price_source != price.source %] -[%- ELSIF price_source.record_item.sellprice_as_number != price.price_as_number %] +[%- ELSIF price_source.record_item.sellprice != price.price %] [%- ELSE %] [% END %] - + [% IF price.source == best_price.source %] [% ELSE %] [% END %] + + + [%- END %] +
[% 'Price Source' | $T8 %] [% 'Price' | $T8 %] [% 'Best Price' | $T8 %][% 'Details' | $T8 %]
[% 'None (PriceSource)' | $T8 %] -
[% L.button_tag('kivi.io.update_price_source(' _ FORM.row _ ', \'' _ price.source _ '\', \'' _ LxERP.format_amount(price.price, -2) _ '\')', LxERP.t8('Select')) %][% L.button_tag('kivi.io.update_price_source(' _ FORM.row _ ', \'' _ price.source _ '\', \'' _ LxERP.format_amount(price.price, -2) _ '\')', LxERP.t8('Update Price')) %][% 'Selected' | $T8 %][% price.full_description | html %][% price.source_description | html %] [% price.price_as_number %][% price.description | html %]
+ +

[% 'Discounts' | $T8 %]

+ + + + + + + + + + +[%- IF price_source.record_item.active_discount_source %] + +[%- ELSE %] + +[%- END %] + + + + + + [%- FOREACH price IN price_source.available_discounts %] + +[%- IF price_source.record_item.active_discount_source != price.source %] + +[%- ELSIF price_source.record_item.discount != price.discount * 100 %] + +[%- ELSE %] + +[% END %] + + +[% IF price.source == best_discount.source %] + +[% ELSE %] + +[% END %] + [%- END %]
[% 'Price Source' | $T8 %][% 'Discount' | $T8 %][% 'Best Discount' | $T8 %][% 'Details' | $T8 %]
[% L.button_tag('kivi.io.update_discount_source(' _ FORM.row _ ', \'\')', LxERP.t8('Select')) %][% 'Selected' | $T8 %][% 'None (PriceSource Discount)' | $T8 %]-
[% L.button_tag('kivi.io.update_discount_source(' _ FORM.row _ ', \'' _ price.source _ '\', \'' _ price.discount_as_percent _ '\')', LxERP.t8('Select')) %][% L.button_tag('kivi.io.update_discount_source(' _ FORM.row _ ', \'' _ price.source _ '\', \'' _ price.discount_as_percent _ '\')', LxERP.t8('Update Discount')) %][% 'Selected' | $T8 %][% price.source_description | html %][% price.discount_as_percent %] %[% price.description | html %]