PriceSource: Erste Version
authorSven Schöling <s.schoeling@linet-services.de>
Fri, 25 Jul 2014 14:38:00 +0000 (16:38 +0200)
committerSven Schöling <s.schoeling@linet-services.de>
Thu, 18 Dec 2014 15:18:21 +0000 (16:18 +0100)
- 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

22 files changed:
SL/DB/Helper/PriceTaxCalculator.pm
SL/DB/MetaSetup/DeliveryOrderItem.pm
SL/DB/MetaSetup/InvoiceItem.pm
SL/DB/MetaSetup/OrderItem.pm
SL/DO.pm
SL/IS.pm
SL/OE.pm
SL/PriceSource.pm [new file with mode: 0644]
SL/PriceSource/ALL.pm [new file with mode: 0644]
SL/PriceSource/Base.pm [new file with mode: 0644]
SL/PriceSource/MasterData.pm [new file with mode: 0644]
SL/PriceSource/Price.pm [new file with mode: 0644]
SL/PriceSource/Pricegroup.pm [new file with mode: 0644]
bin/mozilla/do.pl
bin/mozilla/invoice_io.pl
bin/mozilla/io.pl
bin/mozilla/is.pl
bin/mozilla/oe.pl
locale/de/all
sql/Pg-upgrade2/recorditem_active_price_source.sql [new file with mode: 0644]
templates/webpages/oe/_price_sources_row.html [new file with mode: 0644]
templates/webpages/oe/sales_order.html

index 07ad5b1..fca37f5 100644 (file)
@@ -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 };
 
index 76c890a..d4789d6 100644 (file)
@@ -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' ]);
index 7cd853e..26ce472 100644 (file)
@@ -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' ]);
index 85bf5be..2bd6bf5 100644 (file)
@@ -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' ]);
index 89f2d92..a37e516 100644 (file)
--- 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
index 29886a5..5c156a6 100644 (file)
--- 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();
 
index faaaa33..506db6b 100644 (file)
--- 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 (file)
index 0000000..9a3a978
--- /dev/null
@@ -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 E<lt>s.schoeling@linet-services.deE<gt>
+
+=cut
diff --git a/SL/PriceSource/ALL.pm b/SL/PriceSource/ALL.pm
new file mode 100644 (file)
index 0000000..f39df0a
--- /dev/null
@@ -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 (file)
index 0000000..618512d
--- /dev/null
@@ -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 - <oneliner description>
+
+=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<name>
+
+Should return a unique internal name. Should be entered in
+L<SL::PriceSource::ALL> so that a name_to_class lookup works.
+
+=item C<description>
+
+Should return a translated name.
+
+=item C<needed_params>
+
+Should return a list of elements that a record_item NEEDS to be used with this calulation.
+
+Both C<needed_params> nad C<supported_params> are purely informational at this point.
+
+=item C<supported_params>
+
+Should return a list of elements that a record_item MAY HAVE to be used with this calulation.
+
+Both C<needed_params> nad C<supported_params> are purely informational at this point.
+
+=item C<price>
+
+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 E<lt>s.schoeling@linet-services.deE<gt>
+
+=cut
diff --git a/SL/PriceSource/MasterData.pm b/SL/PriceSource/MasterData.pm
new file mode 100644 (file)
index 0000000..56b0265
--- /dev/null
@@ -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 (file)
index 0000000..ccfdb07
--- /dev/null
@@ -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 (file)
index 0000000..4b73ac8
--- /dev/null
@@ -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;
index b25cdad..1bda974 100644 (file)
@@ -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();
index 7ff0347..a109802 100644 (file)
@@ -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 = '<option value="--">' . $locale->text("none (pricegroup)") . '</option>';
-      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|<option value="$price--$pricegroup_id"$item->{selected}>$pricegroup</option>\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;
   #
   #   }
index 5a17d68..e9fbad8 100644 (file)
@@ -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|<select name="sellprice_pg_$i" style="width: 8em">$form->{"prices_$i"}</select>|;
-      $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|&nbsp;|;
-      }
-      $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|<option value="%s--%s" %s>%s</option>|;
-    $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;
+}
index d2408ff..fa97118 100644 (file)
@@ -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();
index ad5406c..b9abb0f 100644 (file)
@@ -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;
+}
+
index 4c2c0b7..a70f791 100755 (executable)
@@ -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&ouml;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 (file)
index 0000000..c391335
--- /dev/null
@@ -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 (file)
index 0000000..a58e139
--- /dev/null
@@ -0,0 +1,16 @@
+[%- USE T8 %]
+[%- USE HTML %]
+[%- USE L %]
+[%- USE LxERP %]
+<tr class="listrow[% i % 2 %]" id="row[% i %]_3" style='display:none'>
+ <td colspan="[% row.colspan %]">
+   <span class="[% IF !row.obj.active_price_source %]bold[% END %]">
+   [% 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 _ ', \'\')') %]
+   </span>
+   [%- FOREACH price IN price_sources.$i.available_prices %]
+     <div class="[% IF price.source == row.obj.active_price_source %]bold[% END %]">
+     [% 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) _ '\')' ) %]
+     </div>
+   [%- END %]
+ </td>
+</tr>
index f8db6ba..91f0a56 100644 (file)
@@ -1,6 +1,7 @@
 [%- USE T8 %]
 [%- USE HTML %]
-
+[%- USE L %]
+[%- USE LxERP %]
 [%- PROCESS 'amcvar/render_inputs_block.html' %]
 <tr>
  <td>
@@ -74,6 +75,7 @@
 
       </td>
      </tr>
+ [% PROCESS 'oe/_price_sources_row.html' i = loop.count %]
 [%- END %]
 
   </table>
         [% 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();
+    }
   </script>
 
  </td>