]> wagnertech.de Git - kivitendo-erp.git/commitdiff
PriceSource: Rabattbehandlung
authorSven Schöling <s.schoeling@linet-services.de>
Tue, 14 Oct 2014 13:45:01 +0000 (15:45 +0200)
committerSven Schöling <s.schoeling@linet-services.de>
Thu, 18 Dec 2014 15:18:22 +0000 (16:18 +0100)
27 files changed:
SL/DB/MetaSetup/DeliveryOrderItem.pm
SL/DB/MetaSetup/InvoiceItem.pm
SL/DB/MetaSetup/OrderItem.pm
SL/DO.pm
SL/IR.pm
SL/IS.pm
SL/OE.pm
SL/PriceSource.pm
SL/PriceSource/ALL.pm
SL/PriceSource/Base.pm
SL/PriceSource/Business.pm [new file with mode: 0644]
SL/PriceSource/Customer.pm [new file with mode: 0644]
SL/PriceSource/Discount.pm [new file with mode: 0644]
SL/PriceSource/Makemodel.pm
SL/PriceSource/MasterData.pm
SL/PriceSource/Price.pm
SL/PriceSource/Pricegroup.pm
SL/PriceSource/Vendor.pm [new file with mode: 0644]
bin/mozilla/do.pl
bin/mozilla/io.pl
bin/mozilla/ir.pl
bin/mozilla/is.pl
bin/mozilla/oe.pl
js/kivi.io.js
locale/de/all
sql/Pg-upgrade2/recorditem_active_dicount_source.sql [new file with mode: 0644]
templates/webpages/oe/price_sources_dialog.html

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