From: Sven Schöling Date: Fri, 25 Jul 2014 14:38:00 +0000 (+0200) Subject: PriceSource: Erste Version X-Git-Tag: release-3.2.0beta~174 X-Git-Url: http://wagnertech.de/git?a=commitdiff_plain;h=eebe8e90991eacadb6fbd20a648c152017a620c7;p=kivitendo-erp.git PriceSource: Erste Version - Preisgruppen und Stammdaten sind implementiert - Persistenz in allen Belegen funktioniert - Rudimentäre Visualisierung funktioniert - Klassen sind alle da - Doku fehlt - Verkauf/Einkaufweiche fehlt - best_price ungetestet - Preisgruppen hängen noch nicht von Verkäufer ab - dependancy system fehlt - verhalten bei fehlerhaften sources - pricegroup -> active_source migration --- diff --git a/SL/DB/Helper/PriceTaxCalculator.pm b/SL/DB/Helper/PriceTaxCalculator.pm index 07ad5b11d..fca37f59b 100644 --- a/SL/DB/Helper/PriceTaxCalculator.pm +++ b/SL/DB/Helper/PriceTaxCalculator.pm @@ -3,7 +3,7 @@ package SL::DB::Helper::PriceTaxCalculator; use strict; use parent qw(Exporter); -our @EXPORT = qw(calculate_prices_and_taxes); +our @EXPORT = qw(calculate_prices_and_taxes _calculate_item); use Carp; use List::Util qw(sum min max); @@ -75,6 +75,8 @@ sub _calculate_item { my ($self, $item, $idx, $data, %params) = @_; my $part = SL::DB::Part->load_cached($item->parts_id); + return unless $item->part; + my $part_unit = $data->{units_by_name}->{ $part->unit }; my $item_unit = $data->{units_by_name}->{ $item->unit }; diff --git a/SL/DB/MetaSetup/DeliveryOrderItem.pm b/SL/DB/MetaSetup/DeliveryOrderItem.pm index 76c890abc..d4789d640 100644 --- a/SL/DB/MetaSetup/DeliveryOrderItem.pm +++ b/SL/DB/MetaSetup/DeliveryOrderItem.pm @@ -9,29 +9,30 @@ 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 }, + 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 }, ); __PACKAGE__->meta->primary_key_columns([ 'id' ]); diff --git a/SL/DB/MetaSetup/InvoiceItem.pm b/SL/DB/MetaSetup/InvoiceItem.pm index 7cd853ec2..26ce472f9 100644 --- a/SL/DB/MetaSetup/InvoiceItem.pm +++ b/SL/DB/MetaSetup/InvoiceItem.pm @@ -9,36 +9,37 @@ 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 }, + 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 }, ); __PACKAGE__->meta->primary_key_columns([ 'id' ]); diff --git a/SL/DB/MetaSetup/OrderItem.pm b/SL/DB/MetaSetup/OrderItem.pm index 85bf5bee6..2bd6bf542 100644 --- a/SL/DB/MetaSetup/OrderItem.pm +++ b/SL/DB/MetaSetup/OrderItem.pm @@ -9,33 +9,34 @@ 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 }, + 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 }, ); __PACKAGE__->meta->primary_key_columns([ 'id' ]); diff --git a/SL/DO.pm b/SL/DO.pm index 89f2d92b6..a37e516f1 100644 --- a/SL/DO.pm +++ b/SL/DO.pm @@ -284,9 +284,10 @@ sub save { id, delivery_order_id, parts_id, description, longdescription, qty, base_qty, sellprice, discount, unit, reqdate, project_id, serialnumber, ordnumber, transdate, cusordnumber, - lastcost, price_factor_id, price_factor, marge_price_factor, pricegroup_id) + lastcost, price_factor_id, price_factor, marge_price_factor, pricegroup_id, + active_price_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 = @@ -341,7 +342,8 @@ sub save { $form->{"lastcost_$i"}, conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}), - $pricegroup_id); + $pricegroup_id, + $form->{"active_price_source_$i"}); do_statement($form, $h_item, $q_item, @values); my $stock_info = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_$i"}); @@ -688,6 +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, pr.projectnumber, dord.transdate AS dord_transdate, dord.donumber, pg.partsgroup FROM delivery_order_items doi diff --git a/SL/IS.pm b/SL/IS.pm index 29886a5bb..5c156a675 100644 --- a/SL/IS.pm +++ b/SL/IS.pm @@ -2131,196 +2131,6 @@ sub retrieve_item { $main::lxdebug->leave_sub(); } -########################## -# get pricegroups from database -# build up selected pricegroup -# if an exchange rate - change price -# for each part -# -sub get_pricegroups_for_parts { - - $main::lxdebug->enter_sub(); - - my ($self, $myconfig, $form) = @_; - - my $dbh = $form->get_standard_dbh; - - $form->{"PRICES"} = {}; - - my $i = 1; - my $id = 0; - my $all_units = AM->retrieve_units($myconfig, $form); - while (($form->{"id_$i"}) or ($form->{"new_id_$i"})) { - $form->{"PRICES"}{$i} = []; - - $id = $form->{"id_$i"}; - - if (!($form->{"id_$i"}) and $form->{"new_id_$i"}) { - $id = $form->{"new_id_$i"}; - } - - my ($price, $selectedpricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"}); - - my $pricegroup_old = $form->{"pricegroup_old_$i"}; - - # sellprice has format 13,0000 or 0,00000, can't check for 0 numerically - my $sellprice = $form->{"sellprice_$i"}; - my $pricegroup_id = $form->{"pricegroup_id_$i"}; - $form->{"new_pricegroup_$i"} = $selectedpricegroup_id; - $form->{"old_pricegroup_$i"} = $pricegroup_old; - - my $price_new = $form->{"price_new_$i"}; - my $price_old = $form->{"price_old_$i"}; - - if (!$form->{"unit_old_$i"}) { - # Neue Ware aus der Datenbank. In diesem Fall ist unit_$i die - # Einheit, wie sie in den Stammdaten hinterlegt wurde. - # Es sollte also angenommen werden, dass diese ausgewaehlt war. - $form->{"unit_old_$i"} = $form->{"unit_$i"}; - } - - # Die zuletzt ausgewaehlte mit der aktuell ausgewaehlten Einheit - # vergleichen und bei Unterschied den Preis entsprechend umrechnen. - $form->{"selected_unit_$i"} = $form->{"unit_$i"} unless ($form->{"selected_unit_$i"}); - - if (!$all_units->{$form->{"selected_unit_$i"}} || - ($all_units->{$form->{"selected_unit_$i"}}->{"base_unit"} ne - $all_units->{$form->{"unit_old_$i"}}->{"base_unit"})) { - # Die ausgewaehlte Einheit ist fuer diesen Artikel nicht gueltig - # (z.B. Dimensionseinheit war ausgewaehlt, es handelt sich aber - # um eine Dienstleistung). Dann keinerlei Umrechnung vornehmen. - $form->{"unit_old_$i"} = $form->{"selected_unit_$i"} = $form->{"unit_$i"}; - } - - my $basefactor = 1; - - if ($form->{"unit_old_$i"} ne $form->{"selected_unit_$i"}) { - if (defined($all_units->{$form->{"unit_old_$i"}}->{"factor"}) && - $all_units->{$form->{"unit_old_$i"}}->{"factor"}) { - $basefactor = $all_units->{$form->{"selected_unit_$i"}}->{"factor"} / - $all_units->{$form->{"unit_old_$i"}}->{"factor"}; - } - } - - if (!$form->{"basefactor_$i"}) { - $form->{"basefactor_$i"} = 1; - } - - my $query = - qq|SELECT - 0 as pricegroup_id, - sellprice AS default_sellprice, - '' AS pricegroup, - sellprice AS price, - 'selected' AS selected - FROM parts - WHERE id = ? - UNION ALL - SELECT - pricegroup_id, - parts.sellprice AS default_sellprice, - pricegroup.pricegroup, - price, - '' AS selected - FROM prices - LEFT JOIN parts ON parts.id = parts_id - LEFT JOIN pricegroup ON pricegroup.id = pricegroup_id - WHERE parts_id = ? - ORDER BY pricegroup|; - my @values = (conv_i($id), conv_i($id)); - my $pkq = prepare_execute_query($form, $dbh, $query, @values); - - while (my $pkr = $pkq->fetchrow_hashref('NAME_lc')) { - $pkr->{id} = $id; - $pkr->{selected} = ''; - - # if there is an exchange rate change price - if (($form->{exchangerate} * 1) != 0) { - $pkr->{price} /= $form->{exchangerate}; - } - - $pkr->{price} *= $form->{"basefactor_$i"}; - $pkr->{price} *= $basefactor; - $pkr->{price_ufmt} = $pkr->{price}; - $pkr->{price} = $form->format_amount($myconfig, $pkr->{price}, 5); - - if (!defined $selectedpricegroup_id) { - # new entries in article list, either old invoice was loaded (edit) or a new article was added - # Case A: open old invoice, no pricegroup selected - # Case B: add new article to invoice, no pricegroup selected - - # to distinguish case A and B the variable pricegroup_id_$i is used - # for new articles this variable isn't defined, for loaded articles it is - # sellprice can't be used, as it already has 0,00 set - - if ($pkr->{pricegroup_id} eq $form->{"pricegroup_id_$i"} and defined $form->{"pricegroup_id_$i"}) { - # Case A - $pkr->{selected} = ' selected'; - } elsif ($pkr->{pricegroup_id} eq $form->{customer_klass} - and not defined $form->{"pricegroup_id_$i"} - and $pkr->{price_ufmt} != 0 # only use customer pricegroup price if it has a value, else use default_sellprice - # for the case where pricegroup prices haven't been set - ) { - # Case B: use default pricegroup of customer - - $pkr->{selected} = ' selected'; # unless $form->{selected}; - # no customer pricesgroup set - if ($pkr->{price_ufmt} == $pkr->{default_sellprice}) { - - $pkr->{price} = $form->{"sellprice_$i"}; - - } else { - -# this sub should not set anything and only return. --sschoeling, 20090506 -# is this correct? put in again... -- grichardson 20110119 - $form->{"sellprice_$i"} = $pkr->{price}; - } - - } elsif ($pkr->{price_ufmt} == $pkr->{default_sellprice} and $pkr->{default_sellprice} != 0) { - $pkr->{price} = $form->{"sellprice_$i"}; - $pkr->{selected} = ' selected'; - } - } - - # existing article: pricegroup or price changed - if ($selectedpricegroup_id or $selectedpricegroup_id == 0) { - if ($selectedpricegroup_id ne $pricegroup_old) { - # pricegroup has changed - if ($pkr->{pricegroup_id} eq $selectedpricegroup_id) { - $pkr->{selected} = ' selected'; - } - } elsif ( ($form->parse_amount($myconfig, $price_new) - != $form->parse_amount($myconfig, $form->{"sellprice_$i"})) - and ($price_new ne 0) and defined $price_new) { - # sellprice has changed - # when loading existing invoices $price_new is NULL - if ($pkr->{pricegroup_id} == 0) { - $pkr->{price} = $form->{"sellprice_$i"}; - $pkr->{selected} = ' selected'; - } - } elsif ($pkr->{pricegroup_id} eq $selectedpricegroup_id) { - # neither sellprice nor pricegroup changed - $pkr->{selected} = ' selected'; - if ( ($pkr->{pricegroup_id} == 0) and ($pkr->{price} == $form->{"sellprice_$i"})) { - # $pkr->{price} = $form->{"sellprice_$i"}; - } else { - $pkr->{price} = $form->{"sellprice_$i"}; - } - } - } - push @{ $form->{PRICES}{$i} }, $pkr; - - } - $form->{"basefactor_$i"} *= $basefactor; - - $i++; - - $pkq->finish; - } - - $main::lxdebug->leave_sub(); -} - sub has_storno { $main::lxdebug->enter_sub(); diff --git a/SL/OE.pm b/SL/OE.pm index faaaa3383..506db6bdc 100644 --- a/SL/OE.pm +++ b/SL/OE.pm @@ -532,6 +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 = ?, price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ? WHERE id = ? SQL @@ -546,6 +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"}, 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), @@ -945,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.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_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 new file mode 100644 index 000000000..9a3a978c5 --- /dev/null +++ b/SL/PriceSource.pm @@ -0,0 +1,101 @@ +package SL::PriceSource; + +use strict; +use parent 'SL::DB::Object'; +use Rose::Object::MakeMethods::Generic ( + scalar => [ qw(record_item) ], +); + +use List::UtilsBy qw(min_by); +use SL::PriceSource::ALL; +use SL::PriceSource::Price; +use SL::Locale::String; + +sub all_price_sources { + my ($self) = @_; + + return map { + $_->new(record_item => $self->record_item) + } SL::PriceSource::ALL->all_price_sources +} + +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 + ? $class->new(record_item => $self->record_item)->price_from_source($source, $spec) + : empty_price(); +} + +sub available_prices { + map { $_->available_prices } $_[0]->all_price_sources; +} + +sub best_price { + min_by { $_->price } map { $_->best_price } $_[0]->all_price_sources; +} + +sub empty_price { + SL::PriceSource::Price->new( + source => '', + description => t8('None (PriceSource)'), + ); +} + +1; + +__END__ + +=encoding utf-8 + +=head1 NAME + +SL::PriceSource - mixin for price_sources in record items + +=head1 SYNOPSIS + + # in record item class + + use SL::PriceSource; + + # later on: + + $record_item->all_price_sources + $record_item->price_source # get + $record_item->price_source($c) # set + + $record_item->update_price_source # set price to calculated + +=head1 DESCRIPTION + +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. + +=head1 FUNCTIONS + +price_sources + +returns a list of price_source objects which are created with the current record +item. + +active_price_source + +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. + +=head1 BUGS + +None yet. :) + +=head1 AUTHOR + +Sven Schoeling Es.schoeling@linet-services.deE + +=cut diff --git a/SL/PriceSource/ALL.pm b/SL/PriceSource/ALL.pm new file mode 100644 index 000000000..f39df0abc --- /dev/null +++ b/SL/PriceSource/ALL.pm @@ -0,0 +1,25 @@ +package SL::PriceSource::ALL; + +use strict; +use SL::PriceSource::Pricegroup; +use SL::PriceSource::MasterData; + +my %price_sources_by_name = ( + master_data => 'SL::PriceSource::MasterData', + pricegroup => 'SL::PriceSource::Pricegroup', +); + +my @price_sources_order = qw( + master_data + pricegroup +); + +sub all_price_sources { + map { $price_sources_by_name{$_} } @price_sources_order; +} + +sub price_source_class_by_name { + $price_sources_by_name{$_[1]}; +} + +1; diff --git a/SL/PriceSource/Base.pm b/SL/PriceSource/Base.pm new file mode 100644 index 000000000..618512d94 --- /dev/null +++ b/SL/PriceSource/Base.pm @@ -0,0 +1,107 @@ +package SL::PriceSource::Base; + +use strict; + +use parent qw(SL::DB::Object); +use Rose::Object::MakeMethods::Generic ( + scalar => [ qw(record_item record) ], +); + +sub name { die 'name needs to be implemented' } + +sub description { die 'description needs to be implemented' } + +sub available_prices { die 'available_prices needs to be implemented' } + +sub best_price { die 'best_price needs to be implemented' } + +sub price_from_source { die 'price_from_source needs to be implemented:' . "@_" } + +sub part { + $_[0]->record_item->part; +} + +1; + +__END__ + +=encoding utf-8 + +=head1 NAME + +SL::PriceSource::Base - + +=head1 SYNOPSIS + + # in consuming module +# TODO: thats bullshit, theres no need to have this pollute the namespace +# make a manager that handles this + + 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; + } + + $record_item->set_active_price_source($price_source) # equivalent to: + $record_item->active_price_source($price_source->name); + $record_item->sellprice($price_source->price); + + # for finer control + $price_source->needed_params + $price_source->supported_params + +=head1 DESCRIPTION + +PriceSource is an interface that allows generic algorithms to be used, to +calculate a price for a position in a record. + +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. + +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). + +=head1 INTERFACE METHODS + +=over 4 + +=item C + +Should return a unique internal name. Should be entered in +L so that a name_to_class lookup works. + +=item C + +Should return a translated name. + +=item C + +Should return a list of elements that a record_item NEEDS to be used with this calulation. + +Both C nad C are purely informational at this point. + +=item C + +Should return a list of elements that a record_item MAY HAVE to be used with this calulation. + +Both C nad C are purely informational at this point. + +=item C + +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. + +=back + +=head1 BUGS + +None yet. :) + +=head1 AUTHOR + +Sven Schoeling Es.schoeling@linet-services.deE + +=cut diff --git a/SL/PriceSource/MasterData.pm b/SL/PriceSource/MasterData.pm new file mode 100644 index 000000000..56b0265ad --- /dev/null +++ b/SL/PriceSource/MasterData.pm @@ -0,0 +1,43 @@ +package SL::PriceSource::MasterData; + +use strict; +use parent qw(SL::PriceSource::Base); + +use SL::PriceSource::Price; +use SL::Locale::String; + +sub name { 'master_data' } + +sub description { t8('Master Data') } + +sub available_prices { + my ($self, %params) = @_; + + my $part = $self->part; + + return () unless $part; + + # TODO: sellprice only in sales, lastcost in purchase + return $self->make_sellprice($part); +} + +sub price_from_source { + my ($self, $source, $spec) = @_; + + if ($spec eq 'sellprice') { + return $self->make_sellprice($self->part); + } +} + +sub make_sellprice { + my ($self, $part) = @_; + + return SL::PriceSource::Price->new( + price => $part->sellprice, + source => 'master_data/sellprice', + description => t8('Sellprice'), + price_source => $self, + ); +} + +1; diff --git a/SL/PriceSource/Price.pm b/SL/PriceSource/Price.pm new file mode 100644 index 000000000..ccfdb07ba --- /dev/null +++ b/SL/PriceSource/Price.pm @@ -0,0 +1,19 @@ +package SL::PriceSource::Price; + +use strict; + +use parent 'SL::DB::Object'; +use Rose::Object::MakeMethods::Generic ( + scalar => [ qw(price description source price_source) ], + array => [ qw(depends_on) ] +); + +sub full_description { + my ($self) = @_; + + $self->price_source + ? $self->price_source->description . ': ' . $self->description + : $self->description +} + +1; diff --git a/SL/PriceSource/Pricegroup.pm b/SL/PriceSource/Pricegroup.pm new file mode 100644 index 000000000..4b73ac8ff --- /dev/null +++ b/SL/PriceSource/Pricegroup.pm @@ -0,0 +1,50 @@ +package SL::PriceSource::Pricegroup; + +use strict; +use parent qw(SL::PriceSource::Base); + +use SL::PriceSource::Price; +use SL::Locale::String; + +sub name { 'pricegroup' } + +sub description { t8('Pricegroup') } + +sub available_prices { + my ($self, %params) = @_; + + my $item = $self->record_item; + + my $prices = SL::DB::Manager::Price->get_all( + query => [ parts_id => $item->parts_id, price => { gt => 0 } ], + with_objects => 'pricegroup', + order_by => 'pricegroun.id', + ); + + return () unless @$prices; + + return map { + $self->make_price($_); + } @$prices; +} + +sub price_from_source { + my ($self, $source, $spec) = @_; + + my $price = SL::DB::Manager::Price->find_by(id => $spec); + + return $self->make_price($price); +} + +sub make_price { + my ($self, $price_obj) = @_; + + SL::PriceSource::Price->new( + price => $price_obj->price, + source => 'pricegroup/' . $price_obj->id, + description => $price_obj->pricegroup->pricegroup, + price_source => $self, + ) +} + +1; diff --git a/bin/mozilla/do.pl b/bin/mozilla/do.pl index b25cdadb4..1bda974ab 100644 --- a/bin/mozilla/do.pl +++ b/bin/mozilla/do.pl @@ -390,7 +390,6 @@ sub update_delivery_order { # Kunde mit Rabatt 20 -> Rabatt 5,5 i.O. $form->{payment_id} = $payment_id if $form->{payment_id} eq ""; - # for pricegroups my $i = $form->{rowcount}; if ( ($form->{"partnumber_$i"} eq "") @@ -431,12 +430,6 @@ sub update_delivery_order { $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"} * (1 - $form->{tradediscount})); $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}); $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"}); - - # get pricegroups for parts - IS->get_pricegroups_for_parts(\%myconfig, \%$form); - - # build up html code for prices_$i - &set_pricegroup($i); } display_form(); @@ -849,13 +842,6 @@ sub invoice { } - # show pricegroup in newly loaded invoice when creating invoice from delivery order - for my $i (1 .. $form->{rowcount}) { - $form->{"sellprice_pg_$i"} = join '--', $form->{"sellprice_$i"}, $form->{"pricegroup_id_$i"}; - } - IS->get_pricegroups_for_parts(\%myconfig, \%$form); - set_pricegroup($form->{rowcount}); - display_form(); $main::lxdebug->leave_sub(); @@ -957,13 +943,6 @@ sub invoice_multi { invoice_links(); prepare_invoice(); - # show pricegroup in newly loaded invoice when creating invoice from delivery order - for my $i (1 .. $form->{rowcount}) { - $form->{"sellprice_pg_$i"} = join '--', $form->{"sellprice_$i"}, $form->{"pricegroup_id_$i"}; - } - IS->get_pricegroups_for_parts(\%myconfig, \%$form); - set_pricegroup($_) for 1 .. $form->{rowcount}; - display_form(); $main::lxdebug->leave_sub(); diff --git a/bin/mozilla/invoice_io.pl b/bin/mozilla/invoice_io.pl index 7ff034702..a1098020d 100644 --- a/bin/mozilla/invoice_io.pl +++ b/bin/mozilla/invoice_io.pl @@ -88,56 +88,6 @@ use SL::PE; use SL::AM; use Data::Dumper; -sub set_pricegroup { - $main::lxdebug->enter_sub(); - - my $form = $main::form; - my %myconfig = %main::myconfig; - my $locale = $main::locale; - - my $rowcount = shift; - for my $j (1 .. $rowcount) { - my $pricegroup_old = $form->{"pricegroup_old_$j"}; - if ($form->{PRICES}{$j}) { - my $len = 0; - my $prices = ''; - my $price = 0; - foreach my $item (@{ $form->{PRICES}{$j} }) { - - #$price = $form->round_amount($myconfig, $item->{price}, 5); - #$price = $form->format_amount($myconfig, $item->{price}, 2); - my $price = $item->{price}; - my $pricegroup_id = $item->{pricegroup_id}; - my $pricegroup = $item->{pricegroup}; - - # build drop down list for pricegroups - $prices .= - qq|\n|; - - $len += 1; - - # map { - # $form->{"${_}_$j"} = - # $form->format_amount(\%myconfig, $form->{"${_}_$j"}) - # } qw(sellprice price_new price_old); - - # set new selectedpricegroup_id and prices for "Preis" - if ($item->{selected} && ($pricegroup_id != 0)) { - $form->{"pricegroup_old_$j"} = $pricegroup_id; - $form->{"price_new_$j"} = $price; - # edit: don't change the sellprice here - # $form->{"sellprice_$j"} = $price; # this must only be updated for existing articles, not new ones - } - if ($pricegroup_id == 0) { - $form->{"price_new_$j"} = $form->{"sellprice_$j"}; - } - } - $form->{"prices_$j"} = $prices; - } - } - $main::lxdebug->leave_sub(); -} - sub display_form { $main::lxdebug->enter_sub(); @@ -188,12 +138,6 @@ sub display_form { # $form->{rowcount}--; # my $rowcount = $form->{rowcount}; # - # # get pricegroups for parts - # IS->get_pricegroups_for_parts(\%myconfig, \%$form); - # - # # build up html code for prices_$i - # set_pricegroup($rowcount); - # # $form->{resubmit} = 1; # # } diff --git a/bin/mozilla/io.pl b/bin/mozilla/io.pl index 5a17d680a..e9fbad82a 100644 --- a/bin/mozilla/io.pl +++ b/bin/mozilla/io.pl @@ -38,7 +38,7 @@ use Carp; use CGI; -use List::MoreUtils qw(any uniq); +use List::MoreUtils qw(any uniq apply); use List::Util qw(min max first); use SL::CVar; @@ -46,6 +46,7 @@ use SL::Common; use SL::CT; use SL::IC; use SL::IO; +use SL::PriceSource; use SL::DB::Customer; use SL::DB::Default; @@ -149,7 +150,7 @@ sub display_row { } # column_index - my @header_sort = qw(runningnumber partnumber description ship qty unit weight sellprice_pg sellprice discount linetotal); + my @header_sort = qw(runningnumber partnumber description ship qty unit weight sellprice discount linetotal); my @HEADER = ( { id => 'runningnumber', width => 5, value => $locale->text('No.'), display => 1, }, { id => 'partnumber', width => 8, value => $locale->text('Number'), display => 1, }, @@ -162,7 +163,7 @@ sub display_row { { id => 'serialnr', width => 10, value => $locale->text('Serial No.'), display => 0, }, { id => 'projectnr', width => 10, value => $locale->text('Project'), display => 0, }, { id => 'sellprice', width => 15, value => $locale->text('Price'), display => !$is_delivery_order, }, - { id => 'sellprice_pg', width => 8, value => $locale->text('Pricegroup'), display => !$is_delivery_order && !$is_purchase, }, + { id => 'price_source', width => 5, value => $locale->text('Price Source'), display => !$is_delivery_order, }, { id => 'discount', width => 5, value => $locale->text('Discount'), display => !$is_delivery_order, }, { id => 'linetotal', width => 10, value => $locale->text('Extended'), display => !$is_delivery_order, }, { id => 'bin', width => 10, value => $locale->text('Bin'), display => 0, }, @@ -196,7 +197,7 @@ sub display_row { my $deliverydate = $locale->text('Required by'); # special alignings - my %align = map { $_ => 'right' } qw(qty ship right sellprice_pg discount linetotal stock_in_out weight); + my %align = map { $_ => 'right' } qw(qty ship right discount linetotal stock_in_out weight); my %nowrap = map { $_ => 1 } qw(description unit); $form->{marge_total} = 0; @@ -232,6 +233,8 @@ sub display_row { $form->{"sellprice_$i"} = $form->{"price_new_$i"}; } + my $record_item = _make_record_item($i); + # unit begin $form->{"unit_old_$i"} ||= $form->{"unit_$i"}; $form->{"selected_unit_$i"} ||= $form->{"unit_$i"}; @@ -240,12 +243,11 @@ sub display_row { || !AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units)) { # (z.B. Dimensionseinheit war ausgewaehlt, es handelt sich aber $form->{"unit_old_$i"} = $form->{"selected_unit_$i"} = $form->{"unit_$i"}; # um eine Dienstleistung). Dann keinerlei Umrechnung vornehmen. } - # adjust prices by unit, ignore if pricegroup changed - if ((!$form->{"prices_$i"}) || ($form->{"new_pricegroup_$i"} == $form->{"old_pricegroup_$i"})) { - $form->{"sellprice_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1; - $form->{"lastcost_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1; - $form->{"unit_old_$i"} = $form->{"selected_unit_$i"}; - } + + $form->{"sellprice_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1; + $form->{"lastcost_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1; + $form->{"unit_old_$i"} = $form->{"selected_unit_$i"}; + my $this_unit = $form->{"unit_$i"}; $this_unit = $form->{"selected_unit_$i"} if AM->convert_unit($this_unit, $form->{"selected_unit_$i"}, $all_units); @@ -305,41 +307,11 @@ sub display_row { $column_data{ship} = $form->format_amount(\%myconfig, $form->round_amount($ship_qty, 2) * 1) . ' ' . $form->{"unit_$i"}; } - # build in drop down list for pricesgroups - # $sellprice_value setzt den Wert etwas unabhängiger von der Darstellung. - # Hintergrund: Preisgruppen werden hier überprüft und neu berechnet. - # Vorher wurde der ganze cgi->textfield Block zweimal identisch eingebaut, dass passiert - # jetzt nach der Abfrage. - my $sellprice_value; - if ($form->{"prices_$i"}) { - $column_data{sellprice_pg} = qq||; - $sellprice_value =($form->{"new_pricegroup_$i"} != $form->{"old_pricegroup_$i"}) - ? $form->format_amount(\%myconfig, $form->{"price_new_$i"}, $decimalplaces) - : $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces); - } else { - # for last row and report - # set pricegroup drop down list from report menu - if ($form->{"sellprice_$i"} != 0) { - # remember the pricegroup_id in pricegroup_old - # but don't overwrite it - $form->{"pricegroup_old_$i"} = $form->{"pricegroup_id_$i"}; - my $default_option = $form->{"sellprice_$i"}.'--'.$form->{"pricegroup_id_$i"}; - $column_data{sellprice_pg} = NTI($cgi->popup_menu("sellprice_pg_$i", [ $default_option ], $default_option, { $default_option => $form->{"pricegroup_$i"} || '' })); - } else { - $column_data{sellprice_pg} = qq| |; - } - $sellprice_value = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces); - - } - # Falls der Benutzer die Preise nicht anpassen sollte, wird das entsprechende - # Textfield auf readonly gesetzt. Anm. von Sven: Manipulation der Preise ist - # immer noch möglich, konsequenterweise sollten diese NUR aus der Datenbank - # geholt werden. - my $edit_prices = $main::auth->assert('edit_prices', 1); - $column_data{sellprice} = (!$edit_prices) - ? $cgi->textfield(-readonly => "readonly", - -name => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value => $sellprice_value) - : $cgi->textfield(-name => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value => $sellprice_value); + my $sellprice_value = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces); + my $edit_prices = $main::auth->assert('edit_prices', 1) && !$::form->{"active_price_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"})) @@ -349,6 +321,13 @@ sub display_row { $column_data{weight} = $form->format_amount(\%myconfig, $form->{"qty_$i"} * $form->{"weight_$i"}, 3) . ' ' . $defaults->{weightunit} if $defaults->{show_weight}; + if ($form->{"id_${i}"}) { + my $price_source = SL::PriceSource->new(record_item => $record_item); + my $price = $price_source->price_from_source($::form->{"active_price_source_$i"}); + $::form->{price_sources}[$i] = $price_source; + $column_data{price_source} .= $cgi->button(-value => $price->full_description, -onClick => "toggle_price_source($i)"); + } + if ($is_delivery_order) { $column_data{stock_in_out} = calculate_stock_in_out($i); } @@ -433,9 +412,7 @@ sub display_row { if ($is_delivery_order) { map { $form->{"${_}_${i}"} = $form->format_amount(\%myconfig, $form->{"${_}_${i}"}) } qw(sellprice discount lastcost); - $form->{"pricegroup_id_$i"} = $form->{"pricegroup_old_$i"} if $form->{"pricegroup_old_$i"}; - $form->{"sellprice_pg_$i"} = $form->{"hidden_prices_$i"} if $form->{"hidden_prices_$i"}; - push @hidden_vars, grep { defined $form->{"${_}_${i}"} } qw(sellprice discount not_discountable price_factor_id lastcost pricegroup_id sellprice_pg); + push @hidden_vars, grep { defined $form->{"${_}_${i}"} } qw(sellprice discount not_discountable price_factor_id lastcost); push @hidden_vars, "stock_${stock_in_out}_sum_qty", "stock_${stock_in_out}"; } @@ -443,7 +420,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 pricegroup_old price_old id inventory_accno bin partsgroup partnotes + (qw(orderitems_id bo price_old id inventory_accno bin partsgroup partnotes active_price_source income_accno expense_accno listprice assembly taxaccounts ordnumber donumber transdate cusordnumber longdescription basefactor marge_absolut marge_percent marge_price_factor weight), @hidden_vars) ); @@ -455,7 +432,7 @@ sub display_row { # Benutzerdefinierte Variablen für Waren/Dienstleistungen/Erzeugnisse _render_custom_variables_inputs(ROW2 => \@ROW2, row => $i, part_id => $form->{"id_$i"}); - push @ROWS, { ROW1 => \@ROW1, ROW2 => \@ROW2, HIDDENS => \@HIDDENS, colspan => $colspan, error => $form->{"row_error_$i"}, }; + push @ROWS, { ROW1 => \@ROW1, ROW2 => \@ROW2, HIDDENS => \@HIDDENS, colspan => $colspan, error => $form->{"row_error_$i"}, obj => $record_item }; } $form->{totalweight} = $totalweight; @@ -471,40 +448,6 @@ sub display_row { $main::lxdebug->leave_sub(); } -################################################## -# build html-code for pricegroups in variable $form->{prices_$j} - -sub set_pricegroup { - $main::lxdebug->enter_sub(); - - my $form = $main::form; - my $locale = $main::locale; - my $cgi = $::request->{cgi}; - - _check_io_auth(); - - my $rowcount = shift; - for my $j (1 .. $rowcount) { - next unless $form->{PRICES}{$j}; - # build drop down list for pricegroups - my $option_tmpl = qq||; - $form->{"prices_$j"} = join '', map { sprintf $option_tmpl, @$_{qw(price pricegroup_id selected pricegroup)} } - (+{ pricegroup => $locale->text("none (pricegroup)") }, @{ $form->{PRICES}{$j} }); - - foreach my $item (@{ $form->{PRICES}{$j} }) { - # set new selectedpricegroup_id and prices for "Preis" - $form->{"pricegroup_old_$j"} = $item->{pricegroup_id} if $item->{selected} && $item->{pricegroup_id}; - $form->{"sellprice_$j"} = $item->{price} if $item->{selected} && $item->{pricegroup_id}; - $form->{"price_new_$j"} = $form->{"sellprice_$j"} if $item->{selected} || !$item->{pricegroup_id}; - } - - # save hidden pricegroups for delivery_orders - next unless my @selected_prices = grep { $_->{selected} } @{ $form->{PRICES}{$j} }; - $form->{"hidden_prices_$j"} = $selected_prices[-1]{price} . "--" . $selected_prices[-1]{pricegroup_id}; - } - $main::lxdebug->leave_sub(); -} - sub select_item { $main::lxdebug->enter_sub(); @@ -633,12 +576,6 @@ sub item_selected { $form->format_amount(\%myconfig, $form->{"${_}_$i"}, $decimalplaces) } qw(sellprice listprice lastcost) if $form->{item} ne 'assembly'; - # get pricegroups for parts - IS->get_pricegroups_for_parts(\%myconfig, \%$form); - - # build up html code for prices_$i - set_pricegroup($form->{rowcount}); - &display_form; $main::lxdebug->leave_sub(); @@ -733,12 +670,6 @@ sub check_form { or (($form->{level} eq undef) and ($form->{type} =~ /invoice/)) or ($form->{type} =~ /sales_order/)) { - # get pricegroups for parts - IS->get_pricegroups_for_parts(\%myconfig, \%$form); - - # build up html code for prices_$i - set_pricegroup($form->{rowcount}); - } &display_form; @@ -757,7 +688,7 @@ sub remove_emptied_rows { taxaccounts bin assembly weight projectnumber project_id oldprojectnumber runningnumber serialnumber partsgroup payment_id not_discountable shop ve gv buchungsgruppen_id language_values - sellprice_pg pricegroup_old price_old price_new unit_old ordnumber donumber + price_old price_new unit_old ordnumber donumber transdate longdescription basefactor marge_total marge_percent marge_price_factor lastcost price_factor_id partnotes stock_out stock_in has_sernumber reqdate orderitems_id); @@ -1714,12 +1645,6 @@ sub ship_to { # get details for customer/vendor call_sub($::form->{vc} . "_details", qw(name department_1 department_2 street zipcode city country contact email phone fax), $::form->{vc} . "number"); - # get pricegroups for parts - IS->get_pricegroups_for_parts(\%::myconfig, \%$::form); - - # build up html code for prices_$i - set_pricegroup($::form->{rowcount}); - $::form->{rowcount}--; my @shipto_vars = qw(shiptoname shiptostreet shiptozipcode shiptocity shiptocountry @@ -1964,3 +1889,43 @@ sub _remove_billed_or_delivered_rows { $::form->redo_rows(\@fields, \@new_rows, scalar(@new_rows), $::form->{rowcount}); $::form->{rowcount} -= $removed_rows; } + +sub _make_record_item { + my ($row) = @_; + + my $class = { + sales_order => 'OrderItem', + purchase_oder => 'OrderItem', + sales_quotation => 'OrderItem', + request_quotation => 'OrderItem', + invoice => 'InvoiceItem', + purchase_invoice => 'InvoiceItem', + purchase_delivery_order => 'DeliveryOrderItem', + sales_delivery_order => 'DeliveryOrderItem', + }->{$::form->{type}}; + + return unless $class; + + $class = 'SL::DB::' . $class; + + eval "require $class"; + + my $obj = $::form->{"orderitems_id_$row"} + ? $class->meta->convention_manager->auto_manager_class_name->find_by(id => $::form->{"orderitems_id_$row"}) + : $class->new; + + for my $method (apply { s/_$row$// } grep { /_$row$/ } keys %$::form) { + next unless $obj->meta->column($method); + if ($obj->meta->column($method)->isa('Rose::DB::Object::Metadata::Column::Date')) { + $obj->${\"$method\_as_date"}($::form->{"$method\_$row"}); + } else { + $obj->$method($::form->{"$method\_$row"}); + } + } + + if ($::form->{"id_$row"}) { + $obj->part(SL::DB::Part->load_cached($::form->{"id_$row"})); + } + + return $obj; +} diff --git a/bin/mozilla/is.pl b/bin/mozilla/is.pl index d2408ffff..fa97118d9 100644 --- a/bin/mozilla/is.pl +++ b/bin/mozilla/is.pl @@ -273,16 +273,6 @@ sub prepare_invoice { $form->{rowcount} = $i; } - - # get pricegroups for parts - IS->get_pricegroups_for_parts(\%myconfig, \%$form); - - # Problem: set_pricegroup resets the sellprice of old invoices to the price - # currently defined in the pricegroup, which is a problem if the price has - # changed, as the old invoice gets the new price - # set_pricegroup must never be called, when an old invoice is initially loaded - - # set_pricegroup($_) for 1 .. $form->{rowcount}; } $main::lxdebug->leave_sub(); } @@ -614,12 +604,6 @@ sub update { map { $form->{"${_}_$i"} = $form->format_amount(\%myconfig, $form->{"${_}_$i"}, $decimalplaces) } qw(sellprice lastcost); $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"}); - - # get pricegroups for parts - IS->get_pricegroups_for_parts(\%myconfig, \%$form); - - # build up html code for prices_$i - &set_pricegroup($i); } &display_form; @@ -837,10 +821,6 @@ sub use_as_new { $form->{forex} = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, 'buy'); $form->{exchangerate} = $form->{forex} if $form->{forex}; - # remember pricegroups for "use as new" - IS->get_pricegroups_for_parts(\%myconfig, \%$form); - set_pricegroup($_) for 1 .. $form->{rowcount}; - &display_form; $main::lxdebug->leave_sub(); diff --git a/bin/mozilla/oe.pl b/bin/mozilla/oe.pl index ad5406c90..b9abb0f16 100644 --- a/bin/mozilla/oe.pl +++ b/bin/mozilla/oe.pl @@ -594,6 +594,8 @@ sub update { check_oe_access(); + my $order = _make_record(); + set_headings($form->{"id"} ? "edit" : "add"); $form->{update} = 1; @@ -692,12 +694,6 @@ sub update { $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces); $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}, $decimalplaces); $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty); - - # get pricegroups for parts - IS->get_pricegroups_for_parts(\%myconfig, \%$form); - - # build up html code for prices_$i - &set_pricegroup($i); } display_form(); @@ -1510,10 +1506,6 @@ sub invoice { $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty); } - # show pricegroup in newly loaded invoice when creating invoice from quotation/order - IS->get_pricegroups_for_parts(\%myconfig, \%$form); - set_pricegroup($_) for 1 .. $form->{rowcount}; - &display_form; $main::lxdebug->leave_sub(); @@ -2118,3 +2110,29 @@ sub dispatcher { $::form->error($::locale->text('No action defined.')); } + +sub _make_record { + my $obj = SL::DB::Order->new; + + for my $method (keys %$::form) { + next unless $obj->can($method); + next unless $obj->meta->column($method); + + if ($obj->meta->column($method)->isa('Rose::DB::Object::Metadata::Column::Date')) { + $obj->${\"$method\_as_date"}($::form->{$method}); + } elsif ((ref $obj->meta->column($method)) =~ /^Rose::DB::Object::Metadata::Column::(?:Integer|Numeric|Float|DoublePrecsion)$/) { + $obj->$method($::form->{$method}); + } + } + + my @items; + for my $i (1 .. $::form->{rowcount}) { + next unless $::form->{"id_$i"}; + push @items, _make_record_item($i) + } + + $obj->orderitems(@items); + + return $obj; +} + diff --git a/locale/de/all b/locale/de/all index 4c2c0b7e5..a70f79176 100755 --- a/locale/de/all +++ b/locale/de/all @@ -1596,6 +1596,7 @@ $self->{texts} = { 'No.' => 'Position', 'No/individual shipping address' => 'Keine/individuelle Lieferadresse', 'None' => 'Kein', + 'None (PriceSource)' => 'Freier Preis', 'Normal users cannot log in.' => 'Normale Benutzer können sich nicht anmelden.', 'Normalize Customer / Vendor names' => 'Normalisierung Kunden- / Lieferantennamen', 'Normalize part description and part notes' => 'Normalisierung Artikelbeschreibung und Artikellangtext (Bemerkung)', @@ -1832,6 +1833,7 @@ $self->{texts} = { 'Price' => 'Preis', 'Price Factor' => 'Preisfaktor', 'Price Factors' => 'Preisfaktoren', + 'Price Source' => 'Preisquelle', 'Price factor (database ID)' => 'Preisfaktor (Datenbank-ID)', 'Price factor (name)' => 'Preisfaktor (Name)', 'Price factor deleted!' => 'Preisfaktor gelöscht.', @@ -3051,7 +3053,6 @@ $self->{texts} = { 'no article assigned yet' => 'noch kein Artikel zugewiesen', 'no bestbefore' => 'keine Mindesthaltbarkeit', 'no chargenumber' => 'keine Chargennummer', - 'none (pricegroup)' => 'keine', 'not configured' => 'nicht konfiguriert', 'not delivered' => 'nicht geliefert', 'not executed' => 'nicht ausgeführt', diff --git a/sql/Pg-upgrade2/recorditem_active_price_source.sql b/sql/Pg-upgrade2/recorditem_active_price_source.sql new file mode 100644 index 000000000..c391335b1 --- /dev/null +++ b/sql/Pg-upgrade2/recorditem_active_price_source.sql @@ -0,0 +1,8 @@ +-- @tag: recorditem_active_price_source +-- @description: Preisquelle in Belegpositionen +-- @depends: release_2_6_2 +-- @encoding: utf-8 + +ALTER TABLE orderitems ADD COLUMN active_price_source TEXT NOT NULL DEFAULT ''; +ALTER TABLE delivery_order_items ADD COLUMN active_price_source TEXT NOT NULL DEFAULT ''; +ALTER TABLE invoice ADD COLUMN active_price_source TEXT NOT NULL DEFAULT ''; diff --git a/templates/webpages/oe/_price_sources_row.html b/templates/webpages/oe/_price_sources_row.html new file mode 100644 index 000000000..a58e13973 --- /dev/null +++ b/templates/webpages/oe/_price_sources_row.html @@ -0,0 +1,16 @@ +[%- USE T8 %] +[%- USE HTML %] +[%- USE L %] +[%- USE LxERP %] + + + + [% L.radio_button_tag('active_price_source_' _ i, label=LxERP.t8('None (PriceSource)'), checked=!row.obj.active_price_source, value='', onChange='update_price_source(' _ i _ ', \'\')') %] + + [%- FOREACH price IN price_sources.$i.available_prices %] +
+ [% L.radio_button_tag('active_price_source_' _ i, value=price.source, checked=price.source == row.obj.active_price_source, label=LxERP.format_amount(price.price, 2) _ ' (' _ price.full_description _ ')', onChange='update_price_source(' _ i _ ', \'' _ price.source _ '\', \'' _ LxERP.format_amount(price.price, -2) _ '\')' ) %] +
+ [%- END %] + + diff --git a/templates/webpages/oe/sales_order.html b/templates/webpages/oe/sales_order.html index f8db6ba82..91f0a56c6 100644 --- a/templates/webpages/oe/sales_order.html +++ b/templates/webpages/oe/sales_order.html @@ -1,6 +1,7 @@ [%- USE T8 %] [%- USE HTML %] - +[%- USE L %] +[%- USE LxERP %] [%- PROCESS 'amcvar/render_inputs_block.html' %] @@ -74,6 +75,7 @@ + [% PROCESS 'oe/_price_sources_row.html' i = loop.count %] [%- END %] @@ -93,6 +95,14 @@ [% END %] }, 1); }); + function toggle_price_source(row) { + $('#row' + row + '_3').toggle(); + } + function update_price_source(row, source, price_str){ + $('#active_price_source_' + row).val(source); + if (price_str) $('#sellprice_' + row).val(price_str); + $('#update_button').click(); + }