Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 89b26688

Von Sven Schöling vor mehr als 9 Jahren hinzugefügt

  • ID 89b2668811eac6023ad58322e2f9970ddb6a27c9
  • Vorgänger c653b98f
  • Nachfolger 766ed2a6

PriceSource: Rabattbehandlung

Unterschiede anzeigen:

SL/DB/MetaSetup/DeliveryOrderItem.pm
9 9
__PACKAGE__->meta->table('delivery_order_items');
10 10

  
11 11
__PACKAGE__->meta->columns(
12
  base_qty            => { type => 'float', scale => 4 },
13
  cusordnumber        => { type => 'text' },
14
  delivery_order_id   => { type => 'integer', not_null => 1 },
15
  description         => { type => 'text' },
16
  discount            => { type => 'float', scale => 4 },
17
  id                  => { type => 'integer', not_null => 1, sequence => 'delivery_order_items_id' },
18
  itime               => { type => 'timestamp', default => 'now()' },
19
  lastcost            => { type => 'numeric', precision => 15, scale => 5 },
20
  longdescription     => { type => 'text' },
21
  marge_price_factor  => { type => 'numeric', default => 1, precision => 15, scale => 5 },
22
  mtime               => { type => 'timestamp' },
23
  ordnumber           => { type => 'text' },
24
  parts_id            => { type => 'integer', not_null => 1 },
25
  price_factor        => { type => 'numeric', default => 1, precision => 15, scale => 5 },
26
  price_factor_id     => { type => 'integer' },
27
  pricegroup_id       => { type => 'integer' },
28
  project_id          => { type => 'integer' },
29
  qty                 => { type => 'numeric', precision => 25, scale => 5 },
30
  reqdate             => { type => 'date' },
31
  sellprice           => { type => 'numeric', precision => 15, scale => 5 },
32
  serialnumber        => { type => 'text' },
33
  transdate           => { type => 'text' },
34
  unit                => { type => 'varchar', length => 20 },
35
  active_price_source => { type => 'text', default => '', not_null => 1 },
12
  active_discount_source => { type => 'text', default => '', not_null => 1 },
13
  active_price_source    => { type => 'text', default => '', not_null => 1 },
14
  base_qty               => { type => 'float', scale => 4 },
15
  cusordnumber           => { type => 'text' },
16
  delivery_order_id      => { type => 'integer', not_null => 1 },
17
  description            => { type => 'text' },
18
  discount               => { type => 'float', scale => 4 },
19
  id                     => { type => 'integer', not_null => 1, sequence => 'delivery_order_items_id' },
20
  itime                  => { type => 'timestamp', default => 'now()' },
21
  lastcost               => { type => 'numeric', precision => 15, scale => 5 },
22
  longdescription        => { type => 'text' },
23
  marge_price_factor     => { type => 'numeric', default => 1, precision => 15, scale => 5 },
24
  mtime                  => { type => 'timestamp' },
25
  ordnumber              => { type => 'text' },
26
  parts_id               => { type => 'integer', not_null => 1 },
27
  price_factor           => { type => 'numeric', default => 1, precision => 15, scale => 5 },
28
  price_factor_id        => { type => 'integer' },
29
  pricegroup_id          => { type => 'integer' },
30
  project_id             => { type => 'integer' },
31
  qty                    => { type => 'numeric', precision => 25, scale => 5 },
32
  reqdate                => { type => 'date' },
33
  sellprice              => { type => 'numeric', precision => 15, scale => 5 },
34
  serialnumber           => { type => 'text' },
35
  transdate              => { type => 'text' },
36
  unit                   => { type => 'varchar', length => 20 },
36 37
);
37 38

  
38 39
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
SL/DB/MetaSetup/InvoiceItem.pm
9 9
__PACKAGE__->meta->table('invoice');
10 10

  
11 11
__PACKAGE__->meta->columns(
12
  allocated           => { type => 'float', scale => 4 },
13
  assemblyitem        => { type => 'boolean', default => 'false' },
14
  base_qty            => { type => 'float', scale => 4 },
15
  cusordnumber        => { type => 'text' },
16
  deliverydate        => { type => 'date' },
17
  description         => { type => 'text' },
18
  discount            => { type => 'float', scale => 4 },
19
  donumber            => { type => 'text' },
20
  fxsellprice         => { type => 'numeric', precision => 15, scale => 5 },
21
  id                  => { type => 'integer', not_null => 1, sequence => 'invoiceid' },
22
  itime               => { type => 'timestamp', default => 'now()' },
23
  lastcost            => { type => 'numeric', precision => 15, scale => 5 },
24
  longdescription     => { type => 'text' },
25
  marge_percent       => { type => 'numeric', precision => 15, scale => 5 },
26
  marge_price_factor  => { type => 'numeric', default => 1, precision => 15, scale => 5 },
27
  marge_total         => { type => 'numeric', precision => 15, scale => 5 },
28
  mtime               => { type => 'timestamp' },
29
  ordnumber           => { type => 'text' },
30
  parts_id            => { type => 'integer' },
31
  price_factor        => { type => 'numeric', default => 1, precision => 15, scale => 5 },
32
  price_factor_id     => { type => 'integer' },
33
  pricegroup_id       => { type => 'integer' },
34
  project_id          => { type => 'integer' },
35
  qty                 => { type => 'float', scale => 4 },
36
  sellprice           => { type => 'numeric', precision => 15, scale => 5 },
37
  serialnumber        => { type => 'text' },
38
  subtotal            => { type => 'boolean', default => 'false' },
39
  trans_id            => { type => 'integer' },
40
  transdate           => { type => 'text' },
41
  unit                => { type => 'varchar', length => 20 },
42
  active_price_source => { type => 'text', default => '', not_null => 1 },
12
  active_discount_source => { type => 'text', default => '', not_null => 1 },
13
  active_price_source    => { type => 'text', default => '', not_null => 1 },
14
  allocated              => { type => 'float', scale => 4 },
15
  assemblyitem           => { type => 'boolean', default => 'false' },
16
  base_qty               => { type => 'float', scale => 4 },
17
  cusordnumber           => { type => 'text' },
18
  deliverydate           => { type => 'date' },
19
  description            => { type => 'text' },
20
  discount               => { type => 'float', scale => 4 },
21
  donumber               => { type => 'text' },
22
  fxsellprice            => { type => 'numeric', precision => 15, scale => 5 },
23
  id                     => { type => 'integer', not_null => 1, sequence => 'invoiceid' },
24
  itime                  => { type => 'timestamp', default => 'now()' },
25
  lastcost               => { type => 'numeric', precision => 15, scale => 5 },
26
  longdescription        => { type => 'text' },
27
  marge_percent          => { type => 'numeric', precision => 15, scale => 5 },
28
  marge_price_factor     => { type => 'numeric', default => 1, precision => 15, scale => 5 },
29
  marge_total            => { type => 'numeric', precision => 15, scale => 5 },
30
  mtime                  => { type => 'timestamp' },
31
  ordnumber              => { type => 'text' },
32
  parts_id               => { type => 'integer' },
33
  price_factor           => { type => 'numeric', default => 1, precision => 15, scale => 5 },
34
  price_factor_id        => { type => 'integer' },
35
  pricegroup_id          => { type => 'integer' },
36
  project_id             => { type => 'integer' },
37
  qty                    => { type => 'float', scale => 4 },
38
  sellprice              => { type => 'numeric', precision => 15, scale => 5 },
39
  serialnumber           => { type => 'text' },
40
  subtotal               => { type => 'boolean', default => 'false' },
41
  trans_id               => { type => 'integer' },
42
  transdate              => { type => 'text' },
43
  unit                   => { type => 'varchar', length => 20 },
43 44
);
44 45

  
45 46
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
SL/DB/MetaSetup/OrderItem.pm
9 9
__PACKAGE__->meta->table('orderitems');
10 10

  
11 11
__PACKAGE__->meta->columns(
12
  base_qty            => { type => 'float', scale => 4 },
13
  cusordnumber        => { type => 'text' },
14
  description         => { type => 'text' },
15
  discount            => { type => 'float', scale => 4 },
16
  id                  => { type => 'integer', not_null => 1, sequence => 'orderitemsid' },
17
  itime               => { type => 'timestamp', default => 'now()' },
18
  lastcost            => { type => 'numeric', precision => 15, scale => 5 },
19
  longdescription     => { type => 'text' },
20
  marge_percent       => { type => 'numeric', precision => 15, scale => 5 },
21
  marge_price_factor  => { type => 'numeric', default => 1, precision => 15, scale => 5 },
22
  marge_total         => { type => 'numeric', precision => 15, scale => 5 },
23
  mtime               => { type => 'timestamp' },
24
  ordnumber           => { type => 'text' },
25
  parts_id            => { type => 'integer' },
26
  price_factor        => { type => 'numeric', default => 1, precision => 15, scale => 5 },
27
  price_factor_id     => { type => 'integer' },
28
  pricegroup_id       => { type => 'integer' },
29
  project_id          => { type => 'integer' },
30
  qty                 => { type => 'float', scale => 4 },
31
  reqdate             => { type => 'date' },
32
  sellprice           => { type => 'numeric', precision => 15, scale => 5 },
33
  serialnumber        => { type => 'text' },
34
  ship                => { type => 'float', scale => 4 },
35
  subtotal            => { type => 'boolean', default => 'false' },
36
  trans_id            => { type => 'integer' },
37
  transdate           => { type => 'text' },
38
  unit                => { type => 'varchar', length => 20 },
39
  active_price_source => { type => 'text', default => '', not_null => 1 },
12
  active_discount_source => { type => 'text', default => '', not_null => 1 },
13
  active_price_source    => { type => 'text', default => '', not_null => 1 },
14
  base_qty               => { type => 'float', scale => 4 },
15
  cusordnumber           => { type => 'text' },
16
  description            => { type => 'text' },
17
  discount               => { type => 'float', scale => 4 },
18
  id                     => { type => 'integer', not_null => 1, sequence => 'orderitemsid' },
19
  itime                  => { type => 'timestamp', default => 'now()' },
20
  lastcost               => { type => 'numeric', precision => 15, scale => 5 },
21
  longdescription        => { type => 'text' },
22
  marge_percent          => { type => 'numeric', precision => 15, scale => 5 },
23
  marge_price_factor     => { type => 'numeric', default => 1, precision => 15, scale => 5 },
24
  marge_total            => { type => 'numeric', precision => 15, scale => 5 },
25
  mtime                  => { type => 'timestamp' },
26
  ordnumber              => { type => 'text' },
27
  parts_id               => { type => 'integer' },
28
  price_factor           => { type => 'numeric', default => 1, precision => 15, scale => 5 },
29
  price_factor_id        => { type => 'integer' },
30
  pricegroup_id          => { type => 'integer' },
31
  project_id             => { type => 'integer' },
32
  qty                    => { type => 'float', scale => 4 },
33
  reqdate                => { type => 'date' },
34
  sellprice              => { type => 'numeric', precision => 15, scale => 5 },
35
  serialnumber           => { type => 'text' },
36
  ship                   => { type => 'float', scale => 4 },
37
  subtotal               => { type => 'boolean', default => 'false' },
38
  trans_id               => { type => 'integer' },
39
  transdate              => { type => 'text' },
40
  unit                   => { type => 'varchar', length => 20 },
40 41
);
41 42

  
42 43
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
SL/DO.pm
285 285
         sellprice, discount, unit, reqdate, project_id, serialnumber,
286 286
         ordnumber, transdate, cusordnumber,
287 287
         lastcost, price_factor_id, price_factor, marge_price_factor, pricegroup_id,
288
         active_price_source)
288
         active_price_source, active_discount_source)
289 289
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
290
         (SELECT factor FROM price_factors WHERE id = ?), ?, ?, ?)|;
290
         (SELECT factor FROM price_factors WHERE id = ?), ?, ?, ?, ?)|;
291 291
  my $h_item = prepare_query($form, $dbh, $q_item);
292 292

  
293 293
  my $q_item_stock =
......
343 343
               conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
344 344
               conv_i($form->{"marge_price_factor_$i"}),
345 345
               $pricegroup_id,
346
               $form->{"active_price_source_$i"});
346
               $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"});
347 347
    do_statement($form, $h_item, $q_item, @values);
348 348

  
349 349
    my $stock_info = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_$i"});
......
690 690
         doi.reqdate, doi.project_id, doi.serialnumber, doi.lastcost,
691 691
         doi.ordnumber, doi.transdate, doi.cusordnumber, doi.longdescription,
692 692
         doi.price_factor_id, doi.price_factor, doi.marge_price_factor, doi.pricegroup_id,
693
         doi.active_price_source,
693
         doi.active_price_source, doi.active_discount_source,
694 694
         pr.projectnumber, dord.transdate AS dord_transdate, dord.donumber,
695 695
         pg.partsgroup
696 696
       FROM delivery_order_items doi
SL/IR.pm
381 381
    $query =
382 382
      qq|INSERT INTO invoice (id, trans_id, parts_id, description, longdescription, qty, base_qty,
383 383
                              sellprice, fxsellprice, discount, allocated, unit, deliverydate,
384
                              project_id, serialnumber, price_factor_id, price_factor, marge_price_factor)
385
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT factor FROM price_factors WHERE id = ?), ?, ?)|;
384
                              project_id, serialnumber, price_factor_id, price_factor, marge_price_factor,
385
                              active_price_source, active_discount_source)
386
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, (SELECT factor FROM price_factors WHERE id = ?), ?, ?, ?)|;
386 387
    @values = ($invoice_id, conv_i($form->{id}), conv_i($form->{"id_$i"}),
387 388
               $form->{"description_$i"}, $restricter->process($form->{"longdescription_$i"}), $form->{"qty_$i"} * -1,
388 389
               $baseqty * -1, $form->{"sellprice_$i"}, $fxsellprice, $form->{"discount_$i"}, $allocated,
389 390
               $form->{"unit_$i"}, conv_date($form->{deliverydate}),
390 391
               conv_i($form->{"project_id_$i"}), $form->{"serialnumber_$i"},
391 392
               conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"marge_price_factor_$i"}),
392
               conv_i($form->{"active_price_source_$i"}),
393
               conv_i($form->{"active_price_source_$i"}), conv_i($form->{"active_discount_source_$i"}),
393 394
               );
394 395
    do_query($form, $dbh, $query, @values);
395 396

  
......
978 979

  
979 980
        i.id AS invoice_id,
980 981
        i.description, i.longdescription, i.qty, i.fxsellprice AS sellprice, i.parts_id AS id, i.unit, i.deliverydate, i.project_id, i.serialnumber,
981
        i.price_factor_id, i.price_factor, i.marge_price_factor, i.discount, i.active_price_source,
982
        i.price_factor_id, i.price_factor, i.marge_price_factor, i.discount, i.active_price_source, i.active_discount_source,
982 983
        p.partnumber, p.inventory_accno_id AS part_inventory_accno_id,  pr.projectnumber, pg.partsgroup
983 984

  
984 985
        FROM invoice i
SL/IS.pm
761 761
                                sellprice, fxsellprice, discount, allocated, assemblyitem,
762 762
                                unit, deliverydate, project_id, serialnumber, pricegroup_id,
763 763
                                ordnumber, donumber, transdate, cusordnumber, base_qty, subtotal,
764
                                marge_percent, marge_total, lastcost, active_price_source,
764
                                marge_percent, marge_total, lastcost, active_price_source, active_discount_source,
765

  
765 766
                                price_factor_id, price_factor, marge_price_factor)
766
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
767
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
767 768
                   (SELECT factor FROM price_factors WHERE id = ?), ?)|;
768 769

  
769 770
      @values = ($invoice_id, conv_i($form->{id}), conv_i($form->{"id_$i"}),
......
776 777
                 $form->{"cusordnumber_$i"}, $baseqty, $form->{"subtotal_$i"} ? 't' : 'f',
777 778
                 $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
778 779
                 $form->{"lastcost_$i"},
779
                 $form->{"active_price_source_$i"},
780
                 $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"},
780 781
                 conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
781 782
                 conv_i($form->{"marge_price_factor_$i"}));
782 783
      do_query($form, $dbh, $query, @values);
......
1678 1679
           i.id AS invoice_id,
1679 1680
           i.description, i.longdescription, i.qty, i.fxsellprice AS sellprice, i.discount, i.parts_id AS id, i.unit, i.deliverydate AS reqdate,
1680 1681
           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,
1681
           i.price_factor_id, i.price_factor, i.marge_price_factor, i.active_price_source,
1682
           i.price_factor_id, i.price_factor, i.marge_price_factor, i.active_price_source, i.active_discount_source,
1682 1683
           p.partnumber, p.assembly, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id, p.formel, p.listprice,
1683 1684
           pr.projectnumber, pg.partsgroup, prg.pricegroup
1684 1685

  
SL/OE.pm
532 532
          sellprice = ?, discount = ?, unit = ?, reqdate = ?, project_id = ?, serialnumber = ?, ship = ?,
533 533
          pricegroup_id = ?, ordnumber = ?, transdate = ?, cusordnumber = ?, subtotal = ?,
534 534
          marge_percent = ?, marge_total = ?, lastcost = ?, price_factor_id = ?,
535
          active_price_source = ?,
535
          active_price_source = ?, active_discount_source = ?,
536 536
          price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ?
537 537
        WHERE id = ?
538 538
SQL
......
547 547
           $form->{"cusordnumber_$i"}, $form->{"subtotal_$i"} ? 't' : 'f',
548 548
           $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
549 549
           $form->{"lastcost_$i"},
550
           $form->{"active_price_source_$i"},
550
           $form->{"active_price_source_$i"}, $form->{"active_discount_source_$i"},
551 551
           conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
552 552
           conv_i($form->{"marge_price_factor_$i"}),
553 553
           conv_i($orderitems_id),
......
947 947
           o.sellprice, o.parts_id AS id, o.unit, o.discount, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id,
948 948
           o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
949 949
           o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
950
           o.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_source,
950
           o.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_source, o.active_discount_source,
951 951
           pr.projectnumber, p.formel,
952 952
           pg.partsgroup, o.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=o.pricegroup_id) as pricegroup
953 953
         FROM orderitems o
SL/PriceSource.pm
6 6
  scalar => [ qw(record_item record) ],
7 7
);
8 8

  
9
use List::UtilsBy qw(min_by);
9
use List::UtilsBy qw(min_by max_by);
10 10
use SL::PriceSource::ALL;
11 11
use SL::PriceSource::Price;
12 12
use SL::Locale::String;
......
14 14
sub all_price_sources {
15 15
  my ($self) = @_;
16 16

  
17
  return map {
17
  map {
18 18
    $_->new(record_item => $self->record_item, record => $self->record)
19 19
  } SL::PriceSource::ALL->all_enabled_price_sources
20 20
}
......
26 26
  my $class = SL::PriceSource::ALL->price_source_class_by_name($source_name);
27 27

  
28 28
  return $class
29
    ? $class->new(record_item => $self->record_item)->price_from_source($source, $spec)
29
    ? $class->new(record_item => $self->record_item, record => $self->record)->price_from_source($source, $spec)
30 30
    : empty_price();
31 31
}
32 32

  
......
34 34
  map { $_->available_prices } $_[0]->all_price_sources;
35 35
}
36 36

  
37
sub available_discounts {
38
  map { $_->available_discounts } $_[0]->all_price_sources;
39
}
40

  
37 41
sub best_price {
38
  min_by { $_->price } grep { $_->price > 0 } map { $_->best_price } $_[0]->all_price_sources;
42
  min_by { $_->price } grep { $_->price > 0 } grep { $_ } map { $_->best_price } $_[0]->all_price_sources;
43
}
44

  
45
sub best_discount {
46
  max_by { $_->discount } grep { $_->discount } grep { $_ } map { $_->best_discount } $_[0]->all_price_sources;
39 47
}
40 48

  
41 49
sub empty_price {
SL/PriceSource/ALL.pm
4 4
use SL::PriceSource::Pricegroup;
5 5
use SL::PriceSource::MasterData;
6 6
use SL::PriceSource::Makemodel;
7
use SL::PriceSource::Customer;
8
use SL::PriceSource::Vendor;
9
use SL::PriceSource::Business;
7 10

  
8 11
my %price_sources_by_name = (
9 12
  master_data => 'SL::PriceSource::MasterData',
13
  customer    => 'SL::PriceSource::Customer',
14
  vendor      => 'SL::PriceSource::Vendor',
10 15
  pricegroup  => 'SL::PriceSource::Pricegroup',
11 16
  makemodel   => 'SL::PriceSource::Makemodel',
17
  business    => 'SL::PriceSource::Business',
12 18
);
13 19

  
14 20
my @price_sources_order = qw(
15 21
  master_data
22
  customer
23
  vendor
16 24
  pricegroup
17 25
  makemodel
26
  business
18 27
);
19 28

  
20 29
sub all_enabled_price_sources {
SL/PriceSource/Base.pm
13 13

  
14 14
sub available_prices { die 'available_prices needs to be implemented' }
15 15

  
16
sub available_discounts { die 'available_discounts needs to be implemented' }
17

  
16 18
sub best_price { die 'best_price needs to be implemented' }
17 19

  
20
sub best_discounts { die 'best_discounts needs to be implemented' }
21

  
18 22
sub price_from_source { die 'price_from_source needs to be implemented:' . "@_" }
19 23

  
20 24
sub part {
21 25
  $_[0]->record_item->part;
22 26
}
23 27

  
28
sub customer_vendor {
29
  $_[0]->record->is_sales ? $_[0]->record->customer : $_[0]->record->vendor;
30
}
31

  
24 32
1;
25 33

  
26 34
__END__
......
98 106

  
99 107
Shortcut to C<< record_item->part >>
100 108

  
109
=item C<customer_vendor>
110

  
111
Shortcut to C<< record->is_sales ? record->customer : record->vendor >>
112

  
101 113
=back
102 114

  
103 115
=head1 INTERFACE METHODS
......
121 133
to recreate it later. Try to be brief, no one needs 20 different price
122 134
suggestions.
123 135

  
136
=item C<available_discounts>
137

  
138
Must return a list of all prices that you algorithm can recommend the user
139
for the current situation. Each discount must have a unique spec that can be
140
used to recreate it later. Try to be brief, no one needs 20 different discount
141
suggestions.
142

  
124 143
=item C<best_price>
125 144

  
126 145
Must return what you think of as the best matching price in your
127 146
C<available_prices>. This does not have to be the lowest price, but it will be
128 147
compared later to other price sources, and the lowest will be set.
129 148

  
149
=item C<best_discount>
150

  
151
Must return what you think of as the best matching discount in your
152
C<available_discounts>. This does not have to be the highest discount, but it
153
will be compared later to other price sources, and the highest will be set.
154

  
130 155
=item C<price_from_source SOURCE, SPEC>
131 156

  
132
Must recreate the price from C<SPEC> and return. For reference, the complete
133
C<SOURCE> entry from C<record_item.active_price_source> is included.
157
Must recreate the price or discount from C<SPEC> and return. For reference, the
158
complete C<SOURCE> entry from C<record_item.active_price_source> or
159
C<record_item.active_discount_source> is included.
134 160

  
135 161
Note that constraints from the rest of the C<record> do not apply anymore. If
136 162
information needed for the retrieval can be deleted elsewhere, then you must
SL/PriceSource/Business.pm
1
package SL::PriceSource::Business;
2

  
3
use strict;
4
use parent qw(SL::PriceSource::Base);
5

  
6
use SL::DB::Business;
7
use SL::PriceSource::Discount;
8
use SL::Locale::String;
9

  
10
sub name { 'business' }
11

  
12
sub description { t8('Business') }
13

  
14
sub available_prices { }
15

  
16
sub available_discounts {
17
  my ($self, %params) = @_;
18

  
19
  return unless $self->customer_vendor;
20
  return unless $self->customer_vendor->business;
21
  return unless $self->customer_vendor->business->discount != 0;
22

  
23
  SL::PriceSource::Discount->new(
24
    discount     => $self->customer_vendor->business->discount,
25
    spec         => $self->customer_vendor->business->id,
26
    description  => t8('Business Discount'),
27
    price_source => $self,
28
  );
29
}
30

  
31
sub price_from_source {
32
  my ($self, $source, $spec) = @_;
33

  
34
  my $business = SL::DB::Business->load_cached($spec);
35

  
36
  if (!$business) {
37
    return SL::PriceSource::Discount->new(
38
      missing      => t8('Could not load this business'),
39
      price_source => $self,
40
    )
41
  }
42

  
43
  if (!$self->customer_vendor) {
44
    return SL::PriceSource::Discount->new(
45
      discount     => $business->discount,
46
      spec         => $business->id,
47
      description  => t8('Business Discount'),
48
      price_source => $self,
49
      invalid      => t8('This discount is only valid in records with customer or vendor'),
50
    )
51
  }
52

  
53
  if ($business->id != $self->customer_vendor->business->id) {
54
    return SL::PriceSource::Discount->new(
55
      discount     => $business->discount,
56
      spec         => $business->id,
57
      description  => t8('Business Discount'),
58
      price_source => $self,
59
      invalid      => t8('This discount is only valid for business #1', $business->full_description),
60
    )
61
  }
62

  
63
  return SL::PriceSource::Discount->new(
64
    discount     => $business->discount,
65
    spec         => $business->id,
66
    description  => t8('Business Discount'),
67
    price_source => $self,
68
  );
69
}
70

  
71
sub best_price { }
72

  
73
sub best_discount {
74
  &available_discounts;
75
}
76

  
77
1;
78

  
SL/PriceSource/Customer.pm
1
package SL::PriceSource::Customer;
2

  
3
use strict;
4
use parent qw(SL::PriceSource::Base);
5

  
6
use SL::DB::Customer;
7
use SL::PriceSource::Discount;
8
use SL::Locale::String;
9

  
10
sub name { 'customer_discount' }
11

  
12
sub description { t8('Customer Discount') }
13

  
14
sub available_prices { }
15

  
16
sub available_discounts {
17
  my ($self, %params) = @_;
18

  
19
  return unless $self->record->is_sales;
20
  return unless $self->record->customer;
21
  return unless $self->record->customer->discount != 0;
22

  
23
  SL::PriceSource::Discount->new(
24
    discount     => $self->record->customer->discount,
25
    spec         => $self->record->customer->id,
26
    description  => t8('Customer Discount'),
27
    price_source => $self,
28
  );
29
}
30

  
31
sub price_from_source {
32
  my ($self, $source, $spec) = @_;
33

  
34
  my $customer = SL::DB::Customer->load_cached($spec);
35

  
36
  if (!$customer) {
37
    return SL::PriceSource::Discount->new(
38
      missing      => t8('Could not load this customer'),
39
      price_source => $self,
40
    )
41
  }
42

  
43
  if (!$self->record->customer) {
44
    return SL::PriceSource::Discount->new(
45
      discount     => $customer->discount,
46
      spec         => $customer->id,
47
      description  => t8('Customer Discount'),
48
      price_source => $self,
49
      invalid      => t8('This discount is only valid in sales documents'),
50
    )
51
  }
52

  
53
  if ($customer->id != $self->record->customer->id) {
54
    return SL::PriceSource::Discount->new(
55
      discount     => $customer->discount,
56
      spec         => $customer->id,
57
      description  => t8('Customer Discount'),
58
      price_source => $self,
59
      invalid      => t8('This discount is only valid for customer #1', $customer->full_description),
60
    )
61
  }
62

  
63
  return SL::PriceSource::Discount->new(
64
    discount     => $customer->discount,
65
    spec         => $customer->id,
66
    description  => t8('Customer Discount'),
67
    price_source => $self,
68
  );
69
}
70

  
71
sub best_price { }
72

  
73
sub best_discount {
74
  &available_discounts;
75
}
76

  
77
1;
78

  
SL/PriceSource/Discount.pm
1
package SL::PriceSource::Discount;
2

  
3
use strict;
4

  
5
use parent 'SL::DB::Object';
6
use Rose::Object::MakeMethods::Generic (
7
  scalar => [ qw(discount description spec price_source invalid missing) ],
8
);
9

  
10
require SL::DB::Helper::Attr;
11
SL::DB::Helper::Attr::make(__PACKAGE__,
12
  discount => 'numeric(15,5)',
13
);
14

  
15
sub source {
16
  $_[0]->price_source
17
  ? $_[0]->price_source->name . '/' . $_[0]->spec
18
  : '';
19
}
20

  
21
sub full_description {
22
  my ($self) = @_;
23

  
24
  $self->price_source
25
    ? $self->price_source->description . ': ' . $self->description
26
    : $self->description
27
}
28

  
29
sub source_description {
30
  my ($self) = @_;
31

  
32
  $self->price_source
33
    ? $self->price_source->description
34
    : $self->description
35
}
36

  
37
sub to_str {
38
  "source: @{[ $_[0]->source ]}, discount: @{[ $_[0]->discount ]}, description: @{[ $_[0]->description ]}"
39
}
40

  
41
1;
42

  
43
__END__
44

  
45
=encoding utf-8
46

  
47
=head1 NAME
48

  
49
SL::PriceSource::Price - contrainer to pass calculated prices around
50

  
51
=head1 SYNOPSIS
52

  
53
  # in PriceSource::Base implementation
54
  $price = SL::PriceSource::Price->new(
55
    discount     => 10,
56
    spec         => 'summersale2014', # something you can easily parse later
57
    description  => t8('10% discount during summer sale 2014'),
58
    price_source => $self,
59
  )
60

  
61
  # special empty price in SL::PriceSource, for internal use.
62
  SL::PriceSource::Price->new(
63
    description => t8('None (PriceSource)'),
64
  );
65

  
66
  # price can't be restored
67
  SL::PriceSource::Price->new(
68
    missing      => t8('Um, sorry, cannot find that one'),
69
    price_source => $self,
70
  );
71

  
72
  # invalid price
73
  SL::PriceSource::Price->new(
74
    price        => $original_price,
75
    spec         => $original_spec,
76
    description  => $original_description,
77
    invalid      => t8('Offer expired #1 weeks ago', $dt->delta_weeks),
78
    price_source => $self,
79
  );
80

  
81
=head1 DESCRIPTION
82

  
83
See L<SL::PriceSource> for information about the mechanism.
84

  
85
This is a container for prices that are generated by L<SL::PriceSource::Base>
86
implementations.
87

  
88
=head1 CONSTRUCTOR FIELDS
89

  
90
=over 4
91

  
92
=item C<discount>
93

  
94
The discount in percent. A discount of 0 will be ignored. If passed as
95
part of C<available_prices> it will be filtered out. If returned as
96
C<best_price> or C<price_from_source> it will trigger a warning.
97

  
98
=item C<spec>
99

  
100
A unique string that can later be understood by the creating implementation.
101
Can be empty if the implementation only supports one price for a given
102
record_item.
103

  
104
=item C<description>
105

  
106
A localized short description of the origins of this price.
107

  
108
=item C<price_source>
109

  
110
A ref to the creating algorithm.
111

  
112
=item C<missing>
113

  
114
OPTIONAL. Both indicator and localized message that the price with this spec
115
could not be reproduced and should be changed.
116

  
117
If price is missing, you do not need to supply anything except C<source>.
118

  
119
=item C<invalid>
120

  
121
OPTIONAL. Both indicator and localized message that the conditions for this
122
price are no longer valid, and that the price should be changed.
123

  
124
If price is missing, you do not need to supply anything except C<source>.
125

  
126
=back
127

  
128
=head1 SEE ALSO
129

  
130
L<SL::PriceSource>,
131
L<SL::PriceSource::Base>,
132
L<SL::PriceSource::ALL>
133

  
134
=head1 BUGS
135

  
136
None yet. :)
137

  
138
=head1 AUTHOR
139

  
140
Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>
141

  
142
=cut
SL/PriceSource/Makemodel.pm
23 23
  $self->part->makemodels;
24 24
}
25 25

  
26
sub available_discounts { }
27

  
26 28
sub price_from_source {
27 29
  my ($self, $source, $spec) = @_;
28 30

  
......
42 44

  
43 45
}
44 46

  
47
sub best_discount { }
48

  
45 49
sub make_price_from_makemodel {
46 50
  my ($self, $makemodel) = @_;
47 51

  
SL/PriceSource/MasterData.pm
20 20
    : ($self->make_lastcost,  $self->make_listprice);
21 21
}
22 22

  
23
sub available_discounts { }
24

  
23 25
sub price_from_source {
24 26
  my ($self, $source, $spec) = @_;
25 27

  
......
35 37
  : $_[0]->make_lastcost
36 38
}
37 39

  
40
sub best_discount { }
41

  
38 42
sub make_sellprice {
39 43
  my ($self) = @_;
40 44

  
SL/PriceSource/Price.pm
7 7
  scalar => [ qw(price description spec price_source invalid missing) ],
8 8
);
9 9

  
10
use SL::DB::Helper::Attr;
10
require SL::DB::Helper::Attr;
11 11
SL::DB::Helper::Attr::make(__PACKAGE__,
12 12
  price => 'numeric(15,5)',
13 13
);
14 14

  
15 15
sub source {
16 16
  $_[0]->price_source
17
  ?  $_[0]->price_source->name . '/' . $_[0]->spec
17
  ? $_[0]->price_source->name . '/' . $_[0]->spec
18 18
  : '';
19 19
}
20 20

  
......
31 31

  
32 32
  $self->price_source
33 33
    ? $self->price_source->description
34
    : $self->description 
34
    : $self->description
35 35
}
36 36

  
37 37
sub to_str {
38
  "source: @{[ $_[0]->source ]}, price: @{[ $_[0]->price]}, description: @{[ $_[0]->description ]}"
38
  "source: @{[ $_[0]->source ]}, price: @{[ $_[0]->price ]}, description: @{[ $_[0]->description ]}"
39 39
}
40 40

  
41 41
1;
......
53 53
  # in PriceSource::Base implementation
54 54
  $price = SL::PriceSource::Price->new(
55 55
    price        => 10.3,
56
    spec         => '10.3', # something you can easily parse later
57
    description  => t8('Fix price 10.3'),
56
    spec         => '3', # something you can easily parse later
57
    description  => t8('Fix price 10.3 for customer 3'),
58 58
    price_source => $self,
59 59
  )
60 60

  
61
  # special empty price in SL::PriceSource
61
  # special empty price in SL::PriceSource, for internal use.
62 62
  SL::PriceSource::Price->new(
63 63
    description => t8('None (PriceSource)'),
64 64
  );
65 65

  
66
  # invalid price
66
  # price can't be restored
67 67
  SL::PriceSource::Price->new(
68
    price        => $original_price,
69
    spec         => $original_spec,
70
    description  => $original_description,
71
    invalid      => t8('Offer expired #1 weeks ago', $dt->delta_weeks),
68
    missing      => t8('Um, sorry, cannot find that one'),
72 69
    price_source => $self,
73 70
  );
74 71

  
75
  # missing price
72
  # invalid price
76 73
  SL::PriceSource::Price->new(
77
    price        => $original_price,              # will keep last entered price
74
    price        => $original_price,
78 75
    spec         => $original_spec,
79
    description  => '',
80
    missing      => t8('Um, sorry, cannot find that one'),
76
    description  => $original_description,
77
    invalid      => t8('Offer expired #1 weeks ago', $dt->delta_weeks),
81 78
    price_source => $self,
82 79
  );
83 80

  
84

  
85 81
=head1 DESCRIPTION
86 82

  
87 83
See L<SL::PriceSource> for information about the mechanism.
......
97 93

  
98 94
The price. A price of 0 is special and is considered undesirable. If passed as
99 95
part of C<available_prices> it will be filtered out. If returned as
100
C<best_price> or C<price_from_source> it will be warned about.
96
C<best_price> or C<price_from_source> it will trigger a warning.
101 97

  
102 98
=item C<spec>
103 99

  
......
118 114
OPTIONAL. Both indicator and localized message that the price with this spec
119 115
could not be reproduced and should be changed.
120 116

  
117
If price is missing, you do not need to supply anything except C<source>.
118

  
121 119
=item C<invalid>
122 120

  
123 121
OPTIONAL. Both indicator and localized message that the conditions for this
124 122
price are no longer valid, and that the price should be changed.
125 123

  
124
If price is missing, you do not need to supply anything except C<source>.
125

  
126 126
=back
127 127

  
128 128
=head1 SEE ALSO
SL/PriceSource/Pricegroup.pm
33 33
  } @$prices;
34 34
}
35 35

  
36
sub available_discounts { }
37

  
36 38
sub price_from_source {
37 39
  my ($self, $source, $spec) = @_;
38 40

  
......
57 59
  return $best_price || ();
58 60
}
59 61

  
62
sub best_discount { }
63

  
60 64
sub make_price {
61 65
  my ($self, $price_obj) = @_;
62 66

  
SL/PriceSource/Vendor.pm
1
package SL::PriceSource::Vendor;
2

  
3
use strict;
4
use parent qw(SL::PriceSource::Base);
5

  
6
use SL::DB::Vendor;
7
use SL::PriceSource::Price;
8
use SL::Locale::String;
9

  
10
sub name { 'vendor_discount' }
11

  
12
sub description { t8('Vendor Discount') }
13

  
14
sub available_prices { }
15

  
16
sub available_discounts {
17
  my ($self, %params) = @_;
18

  
19
  return if     $self->record->is_sales;
20
  return unless $self->record->vendor;
21
  return unless $self->record->vendor->discount != 0;
22

  
23
  SL::PriceSource::Vendor->new(
24
    discount     => $self->record->vendor->discount,
25
    spec         => $self->record->vendor->id,
26
    description  => t8('Vendor Discount'),
27
    price_source => $self,
28
  );
29
}
30

  
31
sub price_from_source {
32
  my ($self, $source, $spec) = @_;
33

  
34
  my $vendor = SL::DB::Vendor->load_cached($spec);
35

  
36
  if (!$vendor) {
37
    return SL::PriceSource::Discount->new(
38
      missing      => t8('Could not load this vendor'),
39
      price_source => $self,
40
    )
41
  }
42

  
43
  if (!$self->record->vendor) {
44
    return SL::PriceSource::Discount->new(
45
      discount     => $vendor->discount,
46
      spec         => $vendor->id,
47
      description  => t8('Vendor Discount'),
48
      price_source => $self,
49
      invalid      => t8('This discount is only valid in purchase documents'),
50
    )
51
  }
52

  
53
  if ($vendor->id != $self->record->vendor->id) {
54
    return SL::PriceSource::Discount->new(
55
      discount     => $vendor->discount,
56
      spec         => $vendor->id,
57
      description  => t8('Vendor Discount'),
58
      price_source => $self,
59
      invalid      => t8('This discount is only valid for vendor #1', $vendor->full_description),
60
    )
61
  }
62

  
63
  return SL::PriceSource::Discount->new(
64
    discount     => $vendor->discount,
65
    spec         => $vendor->id,
66
    description  => t8('Vendor Discount'),
67
    price_source => $self,
68
  );
69
}
70

  
71

  
72
sub best_price { }
73

  
74
sub best_discount {
75
  &available_discounts;
76
}
77

  
78
1;
79

  
bin/mozilla/do.pl
427 427
        map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} } keys %{ $form->{item_list}[0] };
428 428

  
429 429
        $form->{"marge_price_factor_$i"} = $form->{item_list}->[0]->{price_factor};
430
        $form->{"sellprice_$i"}          = $form->format_amount(\%myconfig, $form->{"sellprice_$i"} * (1 - $form->{tradediscount}));
430
        $form->{"sellprice_$i"}          = $form->format_amount(\%myconfig, $form->{"sellprice_$i"});
431 431
        $form->{"lastcost_$i"}          = $form->format_amount(\%myconfig, $form->{"lastcost_$i"});
432 432
        $form->{"qty_$i"}                = $form->format_amount(\%myconfig, $form->{"qty_$i"});
433 433
      }
bin/mozilla/io.pl
226 226
  for my $i (1 .. $numrows) {
227 227
    my %column_data = ();
228 228

  
229
    my $record_item = $record->id && $record->items ? $record->items->[$i-1] : _make_record_item($i);
230

  
229 231
    # undo formatting
230 232
    map { $form->{"${_}_$i"} = $form->parse_amount(\%myconfig, $form->{"${_}_$i"}) }
231 233
      qw(qty discount sellprice lastcost price_new price_old)
......
235 237
      $form->{"sellprice_$i"} = $form->{"price_new_$i"};
236 238
    }
237 239

  
238
    my $record_item = $record->id && $record->items ? $record->items->[$i-1] : _make_record_item($i);
239

  
240 240
# unit begin
241 241
    $form->{"unit_old_$i"}      ||= $form->{"unit_$i"};
242 242
    $form->{"selected_unit_$i"} ||= $form->{"unit_$i"};
......
310 310
    }
311 311

  
312 312
    my $sellprice_value = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
313
    my $discount_value  = $form->format_amount(\%myconfig, $form->{"discount_$i"});
313 314
    my $edit_prices     = $main::auth->assert('edit_prices', 1) && !$::form->{"active_price_source_$i"};
315
    my $edit_discounts  = $main::auth->assert('edit_prices', 1) && !$::form->{"active_discount_source_$i"};
314 316
    $column_data{sellprice}   = (!$edit_prices)
315 317
                                ? $cgi->hidden(   -name => "sellprice_$i", -id => "sellprice_$i", -value => $sellprice_value) . $sellprice_value
316 318
                                : $cgi->textfield(-name => "sellprice_$i", -id => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value => $sellprice_value);
317
    $column_data{discount}    = (!$edit_prices)
318
                                  ? $cgi->textfield(-readonly => "readonly",
319
                                                    -name => "discount_$i", -size => 3, -value => $form->format_amount(\%myconfig, $form->{"discount_$i"}))
320
                                  : $cgi->textfield(-name => "discount_$i", -size => 3, -value => $form->format_amount(\%myconfig, $form->{"discount_$i"}));
319
    $column_data{discount}    = (!$edit_discounts)
320
                                  ? $cgi->hidden(   -name => "discount_$i", -id => "discount_$i", -value => $discount_value) . $discount_value . ' %'
321
                                  : $cgi->textfield(-name => "discount_$i", -id => "discount_$i", -size => 3, -value => $discount_value);
321 322
    $column_data{linetotal}   = $form->format_amount(\%myconfig, $linetotal, 2);
322 323
    $column_data{bin}         = $form->{"bin_$i"};
323 324

  
......
325 326

  
326 327
    if ($form->{"id_${i}"} && !$is_delivery_order) {
327 328
      my $price_source = SL::PriceSource->new(record_item => $record_item, record => $record);
328
      my $price = $price_source->price_from_source($::form->{"active_price_source_$i"});
329
      my $price    = $price_source->price_from_source($::form->{"active_price_source_$i"});
330
      my $discount = $price_source->price_from_source($::form->{"active_discount_source_$i"});
329 331
      $column_data{price_source} .= $cgi->button(-value => $price->source_description, -onClick => "kivi.io.price_chooser($i)");
330 332
      if ($price->source) {
331 333
        $column_data{price_source} .= ' ' . $cgi->img({src => 'image/flag-red.png', alt => $price->invalid, title => $price->invalid }) if $price->invalid;
332 334
        $column_data{price_source} .= ' ' . $cgi->img({src => 'image/flag-red.png', alt => $price->missing, title => $price->missing }) if $price->missing;
333
        $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;
334
        $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;
335
        $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;
335
        if (!$price->missing && !$price->invalid) {
336
          $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;
337
          $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;
338
          $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;
339
        }
340
      }
341
      if ($discount->source) {
342
        $column_data{discount_source} .= ' ' . $cgi->img({src => 'image/flag-red.png', alt => $discount->invalid, title => $discount->invalid }) if $discount->invalid;
343
        $column_data{discount_source} .= ' ' . $cgi->img({src => 'image/flag-red.png', alt => $discount->missing, title => $discount->missing }) if $discount->missing;
344
        if (!$discount->missing && !$discount->invalid) {
345
          $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;
346
          $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;
347
          $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;
348
        }
336 349
      }
337 350
    }
338 351

  
......
428 441
          $cgi->hidden("-name" => "unit_old_$i", "-value" => $form->{"selected_unit_$i"}),
429 442
          $cgi->hidden("-name" => "price_new_$i", "-value" => $form->format_amount(\%myconfig, $form->{"price_new_$i"})),
430 443
          map { ($cgi->hidden("-name" => $_, "-id" => $_, "-value" => $form->{$_})); } map { $_."_$i" }
431
            (qw(orderitems_id bo price_old id inventory_accno bin partsgroup partnotes active_price_source
444
            (qw(orderitems_id bo price_old id inventory_accno bin partsgroup partnotes active_price_source active_discount_source
432 445
                income_accno expense_accno listprice assembly taxaccounts ordnumber donumber transdate cusordnumber
433 446
                longdescription basefactor marge_absolut marge_percent marge_price_factor weight), @hidden_vars)
434 447
    );
......
469 482
  $::form->header;
470 483

  
471 484
  my @item_list = map {
472
    $_->{display_sellprice}  = $_->{sellprice} * (1 - $::form->{tradediscount});
473 485
    $_->{display_sellprice} /= $_->{price_factor} if ($_->{price_factor});
474 486
    $_;
475 487
  } @{ $::form->{item_list} };
......
536 548
    $::form->{"active_price_source_$i"} = $best_price->source;
537 549
  }
538 550

  
551
  my $best_discount = $price_source->best_discount;
552

  
553
  if ($best_discount) {
554
    $::form->{"discount_$i"}               = $best_discount->discount;
555
    $::form->{"active_discount_source_$i"} = $best_discount->source;
556
  }
557

  
539 558

  
540 559
  $form->{"marge_price_factor_$i"} = $new_item->{price_factor};
541 560

  
......
557 576
      $form->{"sellprice_$i"} =
558 577
        $form->round_amount($form->{"sellprice_$i"}, $decimalplaces);
559 578
    }
560

  
561
    # tradediscount
562
    if ($::form->{tradediscount}) {
563
      $::form->{"sellprice_$i"} *= 1 - $::form->{tradediscount};
564
    }
565 579
  }
566 580

  
567 581
  map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
......
710 724
                transdate longdescription basefactor marge_total marge_percent
711 725
                marge_price_factor lastcost price_factor_id partnotes
712 726
                stock_out stock_in has_sernumber reqdate orderitems_id
713
                active_price_source);
727
                active_price_source active_discount_source);
714 728

  
715 729
  my $ic_cvar_configs = CVar->get_configs(module => 'IC');
716 730
  push @flds, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
bin/mozilla/ir.pl
511 511
        if ($sellprice) {
512 512
          $form->{"sellprice_$i"} = $sellprice;
513 513
        } else {
514
          my $record        = _make_record();
515
          my $price_source  = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
516
          my $best_price    = $price_source->best_price;
517
          my $best_discount = $price_source->best_discount;
518

  
519
          if ($best_price) {
520
            $::form->{"sellprice_$i"}           = $best_price->price;
521
            $::form->{"active_price_source_$i"} = $best_price->source;
522
          }
523
          if ($best_discount) {
524
            $::form->{"discount_$i"}               = $best_discount->discount;
525
            $::form->{"active_discount_source_$i"} = $best_discount->source;
526
          }
527

  
514 528
          # if there is an exchange rate adjust sellprice
515 529
          $form->{"sellprice_$i"} /= $exchangerate;
516 530
        }
bin/mozilla/is.pl
587 587
        if ($sellprice) {
588 588
          $form->{"sellprice_$i"} = $sellprice;
589 589
        } else {
590
          my $record        = _make_record();
591
          my $price_source  = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
592
          my $best_price    = $price_source->best_price;
593
          my $best_discount = $price_source->best_discount;
594

  
595
          if ($best_price) {
596
            $::form->{"sellprice_$i"}           = $best_price->price;
597
            $::form->{"active_price_source_$i"} = $best_price->source;
598
          }
599
          if ($best_discount) {
600
            $::form->{"discount_$i"}               = $best_discount->discount;
601
            $::form->{"active_discount_source_$i"} = $best_discount->source;
602
          }
603

  
590 604
          # if there is an exchange rate adjust sellprice
591
          $form->{"sellprice_$i"} *= (1 - $form->{tradediscount});
592 605
          $form->{"sellprice_$i"} /= $exchangerate;
593 606
        }
594 607

  
bin/mozilla/oe.pl
678 678
        if ($sellprice) {
679 679
          $form->{"sellprice_$i"} = $sellprice;
680 680
        } else {
681
          my $record       = _make_record();
682
          my $price_source = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
683
          my $best_price   = $price_source->best_price;
681
          my $record        = _make_record();
682
          my $price_source  = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
683
          my $best_price    = $price_source->best_price;
684
          my $best_discount = $price_source->best_discount;
684 685

  
685 686
          if ($best_price) {
686 687
            $::form->{"sellprice_$i"}           = $best_price->price;
687 688
            $::form->{"active_price_source_$i"} = $best_price->source;
688 689
          }
690
          if ($best_discount) {
691
            $::form->{"discount_$i"}               = $best_discount->discount;
692
            $::form->{"active_discount_source_$i"} = $best_discount->source;
693
          }
689 694

  
690
          $form->{"sellprice_$i"} *= (1 - $form->{tradediscount});
691 695
          $form->{"sellprice_$i"} /= $exchangerate;   # if there is an exchange rate adjust sellprice
692 696
        }
693 697

  
js/kivi.io.js
43 43
    if (price_str) $('#sellprice_' + row).val(price_str);
44 44
    $('#update_button').click();
45 45
  }
46

  
47
  ns.update_discount_source = function(row, source, discount_str) {
48
    $('#active_discount_source_' + row).val(source);
49
    if (discount_str) $('#discount_' + row).val(discount_str);
50
    $('#update_button').click();
51
  }
46 52
});
locale/de/all
342 342
  'Beratername'                 => 'Beratername',
343 343
  'Beraternummer'               => 'Beraternummer',
344 344
  'Best Before'                 => 'Mindesthaltbarkeit',
345
  'Best Discount'               => 'Bester Rabatt',
345 346
  'Best Price'                  => 'Bester Preis',
346 347
  'Bilanz'                      => 'Bilanz',
347 348
  'Billable amount'             => 'Abrechenbarer Betrag',
......
383 384
  'Buchungsgruppen'             => 'Buchungsgruppen',
384 385
  'Buchungskonto'               => 'Buchungskonto',
385 386
  'Buchungsnummer'              => 'Buchungsnummer',
387
  'Business'                    => 'Kunden-/Lieferantentyp',
388
  'Business Discount'           => 'Kunden-/Lieferantentyp-Rabatt',
386 389
  'Business Number'             => 'Firmennummer',
387 390
  'Business Volume'             => 'Geschäftsvolumen',
388 391
  'Business evaluation'         => 'Betriebswirtschaftliche Auswertung',
......
561 564
  'Could not load class #1 (#2): "#3"' => 'Konnte Klasse #1 (#2) nicht laden: "#3"',
562 565
  'Could not load class #1, #2' => 'Konnte Klasse #1 nicht laden: "#2"',
563 566
  'Could not load employee'     => 'Konnte Benutzer nicht laden',
567
  'Could not load this business' => 'Konnte diesen Kunden-/Lieferantentyp nicht laden',
568
  'Could not load this customer' => 'Konnte diesen Kunden nicht laden',
569
  'Could not load this vendor'  => 'Konnte diesen Lieferanten nicht laden',
564 570
  'Could not print dunning.'    => 'Die Mahnungen konnten nicht gedruckt werden.',
565 571
  'Could not spawn ghostscript.' => 'Die Anwendung "ghostscript" konnte nicht gestartet werden.',
566 572
  'Could not spawn the printer command.' => 'Die Druckanwendung konnte nicht gestartet werden.',
......
669 675
  'Customer'                    => 'Kunde',
670 676
  'Customer (database ID)'      => 'Kunde (Datenbank-ID)',
671 677
  'Customer (name)'             => 'Kunde (Name)',
678
  'Customer Discount'           => 'Kundenrabatt',
672 679
  'Customer Master Data'        => 'Kundenstammdaten',
673 680
  'Customer Name'               => 'Kundenname',
674 681
  'Customer Number'             => 'Kundennummer',
......
825 832
  'Destination warehouse'       => 'Ziellager',
826 833
  'Destination warehouse and bin' => 'Ziellager und -lagerplatz',
827 834
  'Detail view'                 => 'Detailanzeige',
835
  'Details'                     => 'Details',
828 836
  'Details (one letter abbreviation)' => 'D',
829 837
  'Dial command missing in kivitendo configuration\'s [cti] section' => 'Wählbefehl fehlt im Abschnitt [cti] der kivitendo-Konfiguration',
830 838
  'Difference'                  => 'Differenz',
......
834 842
  'Discard duplicate entries in CSV file' => 'Doppelte Einträge in CSV-Datei verwerfen',
835 843
  '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',
836 844
  'Discount'                    => 'Rabatt',
845
  'Discounts'                   => 'Rabatte',
837 846
  'Display'                     => 'Anzeigen',
838 847
  'Display file'                => 'Datei anzeigen',
839 848
  'Display options'             => 'Anzeigeoptionen',
......
1601 1610
  'No.'                         => 'Position',
1602 1611
  'No/individual shipping address' => 'Keine/individuelle Lieferadresse',
1603 1612
  'None'                        => 'Kein',
1613
  'None (PriceSource Discount)' => 'Freier Rabatt',
1604 1614
  'None (PriceSource)'          => 'Freier Preis',
1605 1615
  'Normal users cannot log in.' => 'Normale Benutzer können sich nicht anmelden.',
1606 1616
  'Normalize Customer / Vendor names' => 'Normalisierung Kunden- / Lieferantennamen',
......
1853 1863
  'Pricegroup missing!'         => 'Preisgruppe fehlt!',
1854 1864
  'Pricegroup saved!'           => 'Preisgruppe gespeichert!',
1855 1865
  'Pricegroups'                 => 'Preisgruppen',
1866
  'Prices'                      => 'Preise',
1856 1867
  'Print'                       => 'Drucken',
1857 1868
  'Print and Post'              => 'Drucken und Buchen',
1858 1869
  'Print automatically'         => 'Automatisch ausdrucken',
......
2634 2645
  '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?',
2635 2646
  'There are undefined currencies in your system.' => 'In Ihrer Datenbank wurden Währungen benutzt, die nicht ordnungsgemäß in den Währungen eingetragen wurden.',
2636 2647
  'There are usually three ways to install Perl modules.' => 'Es gibt normalerweise drei Arten, ein Perlmodul zu installieren.',
2648
  'There is a better discount available' => 'Es is ein besserer Rabatt verfügbar',
2637 2649
  'There is a better price available' => 'Es ist ein besserer Preis verfügbar',
2638 2650
  'There is already a taxkey 0 with tax rate not 0.' => 'Es existiert bereits ein Steuerschlüssel mit Steuersatz ungleich 0%.',
2639 2651
  'There is an inconsistancy in your database.' => 'In Ihrer Datenbank sind Unstimmigkeiten vorhanden.',
......
2653 2665
  'This can be done with the following query:' => 'Dies kann mit der folgenden Datenbankabfrage erreicht werden:',
2654 2666
  'This could have happened for two reasons:' => 'Dies kann aus zwei Gründen geschehen sein:',
2655 2667
  'This customer number is already in use.' => 'Diese Kundennummer wird bereits verwendet.',
2668
  'This discount has since gone down' => 'Dieser Rabatt ist mittlerweile niedriger',
2669
  'This discount has since gone up' => 'Dieser Rabatt ist mittlerweile höher',
2670
  'This discount is only valid for business #1' => 'Dieser Rabatt ist nur für Kunden-/Lieferantentyp #1 gültig',
2671
  'This discount is only valid for customer #1' => 'Dieser Rabatt ist nur für Kunde #1 gültig',
2672
  'This discount is only valid for vendor #1' => 'Dieser Rabatt ist nur für Lieferant #1 gültig',
2673
  'This discount is only valid in purchase documents' => 'Dieser Rabatt ist nur in Einkaufsdokumenten gültig',
2674
  'This discount is only valid in records with customer or vendor' => 'Dieser Rabatt ist nur in Dokumenten mit Kunde oder Lieferant gültig',
2675
  'This discount is only valid in sales documents' => 'Dieser Rabatt ist nur in Verkaufsdokumenten gültig',
2656 2676
  'This feature especially prevents mistakes by mixing up prior tax and sales tax.' => 'Dieses Feature vermeidet insbesondere Verwechslungen von Umsatz- und Vorsteuer.',
2657 2677
  '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.',
2658 2678
  'This group is valid for the following clients' => 'Diese Gruppe ist für die folgenden Mandanten gültig',
......
2779 2799
  'Unsupported image type (supported types: #1)' => 'Nicht unterstützter Bildtyp (unterstützte Typen: #1)',
2780 2800
  'Until'                       => 'Bis',
2781 2801
  'Update'                      => 'Erneuern',
2802
  'Update Discount'             => 'Rabatt übernehmen',
2782 2803
  'Update Price'                => 'Preis übernehmen',
2783 2804
  'Update Prices'               => 'Preise aktualisieren',
2784 2805
  'Update SKR04: new tax account 3804 (19%)' => 'Update SKR04: neues Steuerkonto 3804 (19%) für innergemeinschaftlichen Erwerb',
......
2830 2851
  'Vendor'                      => 'Lieferant',
2831 2852
  'Vendor (database ID)'        => '(Datenbank-ID)',
2832 2853
  'Vendor (name)'               => 'Lieferant (Name)',
2854
  'Vendor Discount'             => 'Lieferantenrabatt',
2833 2855
  'Vendor Invoice'              => 'Einkaufsrechnung',
2834 2856
  'Vendor Invoices & AP Transactions' => 'Einkaufsrechnungen & Kreditorenbuchungen',
2835 2857
  'Vendor Name'                 => 'Lieferantenname',
sql/Pg-upgrade2/recorditem_active_dicount_source.sql
1
-- @tag: recorditem_active_record_source
2
-- @description: Preisquellen: Rabatte
3
-- @depends: release_3_1_0 recorditem_active_price_source
4
-- @encoding: utf-8
5

  
6
ALTER TABLE orderitems           ADD COLUMN active_discount_source TEXT NOT NULL DEFAULT '';
7
ALTER TABLE delivery_order_items ADD COLUMN active_discount_source TEXT NOT NULL DEFAULT '';
8
ALTER TABLE invoice              ADD COLUMN active_discount_source TEXT NOT NULL DEFAULT '';
templates/webpages/oe/price_sources_dialog.html
3 3
[%- USE L %]
4 4
[%- USE LxERP %]
5 5
[% SET best_price = price_source.best_price %]
6
[% SET best_discount = price_source.best_discount %]
7
  <h2>[% 'Prices' | $T8 %]</h2>
8

  
6 9
  <table>
7 10
   <tr class='listheading'>
8 11
    <th></th>
9 12
    <th>[% 'Price Source' | $T8 %]</th>
10 13
    <th>[% 'Price' | $T8 %]</th>
11 14
    <th>[% 'Best Price' | $T8 %]</th>
15
    <th>[% 'Details' | $T8 %]</th>
12 16
   </tr>
13 17
   <tr class='listrow'>
14 18
[%- IF price_source.record_item.active_price_source %]
......
19 23
    <td>[% 'None (PriceSource)' | $T8 %]</td>
20 24
    <td>-</td>
21 25
    <td></td>
26
    <td></td>
22 27
   </tr>
23 28
   [%- FOREACH price IN price_source.available_prices %]
24 29
    <tr class='listrow'>
25 30
[%- IF price_source.record_item.active_price_source != price.source %]
26 31
     <td>[% L.button_tag('kivi.io.update_price_source(' _ FORM.row _ ', \'' _ price.source _ '\', \'' _ LxERP.format_amount(price.price, -2) _ '\')', LxERP.t8('Select')) %]</td>
27
[%- ELSIF price_source.record_item.sellprice_as_number != price.price_as_number %]
32
[%- ELSIF price_source.record_item.sellprice != price.price %]
28 33
     <td>[% L.button_tag('kivi.io.update_price_source(' _ FORM.row _ ', \'' _ price.source _ '\', \'' _ LxERP.format_amount(price.price, -2) _ '\')', LxERP.t8('Update Price')) %]</td>
29 34
[%- ELSE %]
30 35
    <td><b>[% 'Selected' | $T8 %]</b></td>
31 36
[% END %]
32
     <td>[% price.full_description | html %]</td>
37
     <td>[% price.source_description | html %]</td>
33 38
     <td>[% price.price_as_number %]</td>
34 39
[% IF price.source == best_price.source %]
35 40
     <td align='center'>&#x2022;</td>
36 41
[% ELSE %]
37 42
     <td></td>
38 43
[% END %]
44
     <td>[% price.description | html %]</td>
45
    </tr>
46
   [%- END %]
47
  </table>
48

  
49
  <h2>[% 'Discounts' | $T8 %]</h2>
50

  
51
  <table>
52
   <tr class='listheading'>
53
    <th></th>
54
    <th>[% 'Price Source' | $T8 %]</th>
55
    <th>[% 'Discount' | $T8 %]</th>
56
    <th>[% 'Best Discount' | $T8 %]</th>
57
    <th>[% 'Details' | $T8 %]</th>
58
   </tr>
59
   <tr class='listrow'>
60
[%- IF price_source.record_item.active_discount_source %]
61
    <td>[% L.button_tag('kivi.io.update_discount_source(' _ FORM.row _ ', \'\')', LxERP.t8('Select')) %]</td>
62
[%- ELSE %]
63
    <td><b>[% 'Selected' | $T8 %]</b></td>
64
[%- END %]
65
    <td>[% 'None (PriceSource Discount)' | $T8 %]</td>
66
    <td>-</td>
67
    <td></td>
68
    <td></td>
69
   </tr>
70
   [%- FOREACH price IN price_source.available_discounts %]
71
    <tr class='listrow'>
72
[%- IF price_source.record_item.active_discount_source != price.source %]
73
     <td>[% L.button_tag('kivi.io.update_discount_source(' _ FORM.row _ ', \'' _ price.source _ '\', \'' _ price.discount_as_percent _ '\')', LxERP.t8('Select')) %]</td>
74
[%- ELSIF price_source.record_item.discount != price.discount * 100 %]
75
     <td>[% L.button_tag('kivi.io.update_discount_source(' _ FORM.row _ ', \'' _ price.source _ '\', \'' _ price.discount_as_percent  _ '\')', LxERP.t8('Update Discount')) %]</td>
76
[%- ELSE %]
77
    <td><b>[% 'Selected' | $T8 %]</b></td>
... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen überschreitet.

Auch abrufbar als: Unified diff