Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision eebe8e90

Von Sven Schöling vor etwa 10 Jahren hinzugefügt

  • ID eebe8e90991eacadb6fbd20a648c152017a620c7
  • Vorgänger c21d2acc
  • Nachfolger 0409db7c

PriceSource: Erste Version

- Preisgruppen und Stammdaten sind implementiert
- Persistenz in allen Belegen funktioniert
- Rudimentäre Visualisierung funktioniert
- Klassen sind alle da

- Doku fehlt
- Verkauf/Einkaufweiche fehlt
- best_price ungetestet
- Preisgruppen hängen noch nicht von Verkäufer ab
- dependancy system fehlt
- verhalten bei fehlerhaften sources
- pricegroup -> active_source migration

Unterschiede anzeigen:

SL/DB/Helper/PriceTaxCalculator.pm
3 3
use strict;
4 4

  
5 5
use parent qw(Exporter);
6
our @EXPORT = qw(calculate_prices_and_taxes);
6
our @EXPORT = qw(calculate_prices_and_taxes _calculate_item);
7 7

  
8 8
use Carp;
9 9
use List::Util qw(sum min max);
......
75 75
  my ($self, $item, $idx, $data, %params) = @_;
76 76

  
77 77
  my $part       = SL::DB::Part->load_cached($item->parts_id);
78
  return unless $item->part;
79

  
78 80
  my $part_unit  = $data->{units_by_name}->{ $part->unit };
79 81
  my $item_unit  = $data->{units_by_name}->{ $item->unit };
80 82

  
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 },
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 },
35 36
);
36 37

  
37 38
__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 },
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 },
42 43
);
43 44

  
44 45
__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 },
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 },
39 40
);
40 41

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

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

  
347 349
    my $stock_info = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_$i"});
......
688 690
         doi.reqdate, doi.project_id, doi.serialnumber, doi.lastcost,
689 691
         doi.ordnumber, doi.transdate, doi.cusordnumber, doi.longdescription,
690 692
         doi.price_factor_id, doi.price_factor, doi.marge_price_factor, doi.pricegroup_id,
693
         doi.active_price_source,
691 694
         pr.projectnumber, dord.transdate AS dord_transdate, dord.donumber,
692 695
         pg.partsgroup
693 696
       FROM delivery_order_items doi
SL/IS.pm
2131 2131
  $main::lxdebug->leave_sub();
2132 2132
}
2133 2133

  
2134
##########################
2135
# get pricegroups from database
2136
# build up selected pricegroup
2137
# if an exchange rate - change price
2138
# for each part
2139
#
2140
sub get_pricegroups_for_parts {
2141

  
2142
  $main::lxdebug->enter_sub();
2143

  
2144
  my ($self, $myconfig, $form) = @_;
2145

  
2146
  my $dbh = $form->get_standard_dbh;
2147

  
2148
  $form->{"PRICES"} = {};
2149

  
2150
  my $i  = 1;
2151
  my $id = 0;
2152
  my $all_units = AM->retrieve_units($myconfig, $form);
2153
  while (($form->{"id_$i"}) or ($form->{"new_id_$i"})) {
2154
    $form->{"PRICES"}{$i} = [];
2155

  
2156
    $id = $form->{"id_$i"};
2157

  
2158
    if (!($form->{"id_$i"}) and $form->{"new_id_$i"}) {
2159
      $id = $form->{"new_id_$i"};
2160
    }
2161

  
2162
    my ($price, $selectedpricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
2163

  
2164
    my $pricegroup_old = $form->{"pricegroup_old_$i"};
2165

  
2166
    # sellprice has format 13,0000 or 0,00000,  can't check for 0 numerically
2167
    my $sellprice = $form->{"sellprice_$i"};
2168
    my $pricegroup_id = $form->{"pricegroup_id_$i"};
2169
    $form->{"new_pricegroup_$i"} = $selectedpricegroup_id;
2170
    $form->{"old_pricegroup_$i"} = $pricegroup_old;
2171

  
2172
    my $price_new = $form->{"price_new_$i"};
2173
    my $price_old = $form->{"price_old_$i"};
2174

  
2175
    if (!$form->{"unit_old_$i"}) {
2176
      # Neue Ware aus der Datenbank. In diesem Fall ist unit_$i die
2177
      # Einheit, wie sie in den Stammdaten hinterlegt wurde.
2178
      # Es sollte also angenommen werden, dass diese ausgewaehlt war.
2179
      $form->{"unit_old_$i"} = $form->{"unit_$i"};
2180
    }
2181

  
2182
    # Die zuletzt ausgewaehlte mit der aktuell ausgewaehlten Einheit
2183
    # vergleichen und bei Unterschied den Preis entsprechend umrechnen.
2184
    $form->{"selected_unit_$i"} = $form->{"unit_$i"} unless ($form->{"selected_unit_$i"});
2185

  
2186
    if (!$all_units->{$form->{"selected_unit_$i"}} ||
2187
        ($all_units->{$form->{"selected_unit_$i"}}->{"base_unit"} ne
2188
         $all_units->{$form->{"unit_old_$i"}}->{"base_unit"})) {
2189
      # Die ausgewaehlte Einheit ist fuer diesen Artikel nicht gueltig
2190
      # (z.B. Dimensionseinheit war ausgewaehlt, es handelt sich aber
2191
      # um eine Dienstleistung). Dann keinerlei Umrechnung vornehmen.
2192
      $form->{"unit_old_$i"} = $form->{"selected_unit_$i"} = $form->{"unit_$i"};
2193
    }
2194

  
2195
    my $basefactor = 1;
2196

  
2197
    if ($form->{"unit_old_$i"} ne $form->{"selected_unit_$i"}) {
2198
      if (defined($all_units->{$form->{"unit_old_$i"}}->{"factor"}) &&
2199
          $all_units->{$form->{"unit_old_$i"}}->{"factor"}) {
2200
        $basefactor = $all_units->{$form->{"selected_unit_$i"}}->{"factor"} /
2201
          $all_units->{$form->{"unit_old_$i"}}->{"factor"};
2202
      }
2203
    }
2204

  
2205
    if (!$form->{"basefactor_$i"}) {
2206
      $form->{"basefactor_$i"} = 1;
2207
    }
2208

  
2209
    my $query =
2210
       qq|SELECT
2211
            0 as pricegroup_id,
2212
            sellprice AS default_sellprice,
2213
            '' AS pricegroup,
2214
            sellprice AS price,
2215
            'selected' AS selected
2216
          FROM parts
2217
          WHERE id = ?
2218
          UNION ALL
2219
          SELECT
2220
           pricegroup_id,
2221
           parts.sellprice AS default_sellprice,
2222
           pricegroup.pricegroup,
2223
           price,
2224
           '' AS selected
2225
          FROM prices
2226
          LEFT JOIN parts ON parts.id = parts_id
2227
          LEFT JOIN pricegroup ON pricegroup.id = pricegroup_id
2228
          WHERE parts_id = ?
2229
          ORDER BY pricegroup|;
2230
    my @values = (conv_i($id), conv_i($id));
2231
    my $pkq = prepare_execute_query($form, $dbh, $query, @values);
2232

  
2233
    while (my $pkr = $pkq->fetchrow_hashref('NAME_lc')) {
2234
      $pkr->{id}       = $id;
2235
      $pkr->{selected} = '';
2236

  
2237
      # if there is an exchange rate change price
2238
      if (($form->{exchangerate} * 1) != 0) {
2239
        $pkr->{price} /= $form->{exchangerate};
2240
      }
2241

  
2242
      $pkr->{price} *= $form->{"basefactor_$i"};
2243
      $pkr->{price} *= $basefactor;
2244
      $pkr->{price_ufmt} = $pkr->{price};
2245
      $pkr->{price} = $form->format_amount($myconfig, $pkr->{price}, 5);
2246

  
2247
      if (!defined $selectedpricegroup_id) {
2248
        # new entries in article list, either old invoice was loaded (edit) or a new article was added
2249
        # Case A: open old invoice, no pricegroup selected
2250
        # Case B: add new article to invoice, no pricegroup selected
2251

  
2252
        # to distinguish case A and B the variable pricegroup_id_$i is used
2253
        # for new articles this variable isn't defined, for loaded articles it is
2254
        # sellprice can't be used, as it already has 0,00 set
2255

  
2256
        if ($pkr->{pricegroup_id} eq $form->{"pricegroup_id_$i"} and defined $form->{"pricegroup_id_$i"}) {
2257
          # Case A
2258
          $pkr->{selected}  = ' selected';
2259
        } elsif ($pkr->{pricegroup_id} eq $form->{customer_klass}
2260
                 and not defined $form->{"pricegroup_id_$i"}
2261
                 and $pkr->{price_ufmt} != 0    # only use customer pricegroup price if it has a value, else use default_sellprice
2262
                                                # for the case where pricegroup prices haven't been set
2263
                ) {
2264
          # Case B: use default pricegroup of customer
2265

  
2266
          $pkr->{selected}  = ' selected'; # unless $form->{selected};
2267
          # no customer pricesgroup set
2268
          if ($pkr->{price_ufmt} == $pkr->{default_sellprice}) {
2269

  
2270
            $pkr->{price} = $form->{"sellprice_$i"};
2271

  
2272
          } else {
2273

  
2274
# this sub should not set anything and only return. --sschoeling, 20090506
2275
# is this correct? put in again... -- grichardson 20110119
2276
            $form->{"sellprice_$i"} = $pkr->{price};
2277
          }
2278

  
2279
        } elsif ($pkr->{price_ufmt} == $pkr->{default_sellprice} and $pkr->{default_sellprice} != 0) {
2280
          $pkr->{price}    = $form->{"sellprice_$i"};
2281
          $pkr->{selected} = ' selected';
2282
        }
2283
      }
2284

  
2285
      # existing article: pricegroup or price changed
2286
      if ($selectedpricegroup_id or $selectedpricegroup_id == 0) {
2287
        if ($selectedpricegroup_id ne $pricegroup_old) {
2288
          # pricegroup has changed
2289
          if ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
2290
            $pkr->{selected}  = ' selected';
2291
          }
2292
        } elsif ( ($form->parse_amount($myconfig, $price_new)
2293
                 != $form->parse_amount($myconfig, $form->{"sellprice_$i"}))
2294
                  and ($price_new ne 0) and defined $price_new) {
2295
          # sellprice has changed
2296
          # when loading existing invoices $price_new is NULL
2297
          if ($pkr->{pricegroup_id} == 0) {
2298
            $pkr->{price}     = $form->{"sellprice_$i"};
2299
            $pkr->{selected}  = ' selected';
2300
          }
2301
        } elsif ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
2302
          # neither sellprice nor pricegroup changed
2303
          $pkr->{selected}  = ' selected';
2304
          if (    ($pkr->{pricegroup_id} == 0) and ($pkr->{price} == $form->{"sellprice_$i"})) {
2305
            # $pkr->{price}                         = $form->{"sellprice_$i"};
2306
          } else {
2307
            $pkr->{price} = $form->{"sellprice_$i"};
2308
          }
2309
        }
2310
      }
2311
      push @{ $form->{PRICES}{$i} }, $pkr;
2312

  
2313
    }
2314
    $form->{"basefactor_$i"} *= $basefactor;
2315

  
2316
    $i++;
2317

  
2318
    $pkq->finish;
2319
  }
2320

  
2321
  $main::lxdebug->leave_sub();
2322
}
2323

  
2324 2134
sub has_storno {
2325 2135
  $main::lxdebug->enter_sub();
2326 2136

  
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 536
          price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ?
536 537
        WHERE id = ?
537 538
SQL
......
546 547
           $form->{"cusordnumber_$i"}, $form->{"subtotal_$i"} ? 't' : 'f',
547 548
           $form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
548 549
           $form->{"lastcost_$i"},
550
           $form->{"active_price_source_$i"},
549 551
           conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
550 552
           conv_i($form->{"marge_price_factor_$i"}),
551 553
           conv_i($orderitems_id),
......
945 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,
946 948
           o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
947 949
           o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
948
           o.price_factor_id, o.price_factor, o.marge_price_factor,
950
           o.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_source,
949 951
           pr.projectnumber, p.formel,
950 952
           pg.partsgroup, o.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=o.pricegroup_id) as pricegroup
951 953
         FROM orderitems o
SL/PriceSource.pm
1
package SL::PriceSource;
2

  
3
use strict;
4
use parent 'SL::DB::Object';
5
use Rose::Object::MakeMethods::Generic (
6
  scalar => [ qw(record_item) ],
7
);
8

  
9
use List::UtilsBy qw(min_by);
10
use SL::PriceSource::ALL;
11
use SL::PriceSource::Price;
12
use SL::Locale::String;
13

  
14
sub all_price_sources {
15
  my ($self) = @_;
16

  
17
  return map {
18
    $_->new(record_item => $self->record_item)
19
  } SL::PriceSource::ALL->all_price_sources
20
}
21

  
22
sub price_from_source {
23
  my ($self, $source) = @_;
24
  my ($source_name, $spec) = split m{/}, $source, 2;
25

  
26
  my $class = SL::PriceSource::ALL->price_source_class_by_name($source_name);
27

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

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

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

  
41
sub empty_price {
42
  SL::PriceSource::Price->new(
43
    source      => '',
44
    description => t8('None (PriceSource)'),
45
  );
46
}
47

  
48
1;
49

  
50
__END__
51

  
52
=encoding utf-8
53

  
54
=head1 NAME
55

  
56
SL::PriceSource - mixin for price_sources in record items
57

  
58
=head1 SYNOPSIS
59

  
60
  # in record item class
61

  
62
  use SL::PriceSource;
63

  
64
  # later on:
65

  
66
  $record_item->all_price_sources
67
  $record_item->price_source      # get
68
  $record_item->price_source($c)  # set
69

  
70
  $record_item->update_price_source # set price to calculated
71

  
72
=head1 DESCRIPTION
73

  
74
This mixin provides a way to use price_source objects from within a record item.
75
Record items in this contest mean OrderItems, InvoiceItems and
76
DeliveryOrderItems.
77

  
78
=head1 FUNCTIONS
79

  
80
price_sources
81

  
82
returns a list of price_source objects which are created with the current record
83
item.
84

  
85
active_price_source
86

  
87
returns the object representing the currently chosen price_source method or
88
undef if custom price is chosen. Note that this must not necessarily be the
89
active price, if something affecting the price_source has changed, the price
90
calculated can differ from the price in the record. It is the responsibility of
91
the implementing code to decide what to do in this case.
92

  
93
=head1 BUGS
94

  
95
None yet. :)
96

  
97
=head1 AUTHOR
98

  
99
Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>
100

  
101
=cut
SL/PriceSource/ALL.pm
1
package SL::PriceSource::ALL;
2

  
3
use strict;
4
use SL::PriceSource::Pricegroup;
5
use SL::PriceSource::MasterData;
6

  
7
my %price_sources_by_name = (
8
  master_data => 'SL::PriceSource::MasterData',
9
  pricegroup  => 'SL::PriceSource::Pricegroup',
10
);
11

  
12
my @price_sources_order = qw(
13
  master_data
14
  pricegroup
15
);
16

  
17
sub all_price_sources {
18
  map { $price_sources_by_name{$_} } @price_sources_order;
19
}
20

  
21
sub price_source_class_by_name {
22
  $price_sources_by_name{$_[1]};
23
}
24

  
25
1;
SL/PriceSource/Base.pm
1
package SL::PriceSource::Base;
2

  
3
use strict;
4

  
5
use parent qw(SL::DB::Object);
6
use Rose::Object::MakeMethods::Generic (
7
  scalar => [ qw(record_item record) ],
8
);
9

  
10
sub name { die 'name needs to be implemented' }
11

  
12
sub description { die 'description needs to be implemented' }
13

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

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

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

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

  
24
1;
25

  
26
__END__
27

  
28
=encoding utf-8
29

  
30
=head1 NAME
31

  
32
SL::PriceSource::Base - <oneliner description>
33

  
34
=head1 SYNOPSIS
35

  
36
  # in consuming module
37
# TODO: thats bullshit, theres no need to have this pollute the namespace
38
# make a manager that handles this
39

  
40
  my @list_of_price_sources = $record_item->price_sources;
41
  for (@list_of_price_sources) {
42
    my $internal_name   = $_->name;
43
    my $translated_name = $_->description;
44
    my $price           = $_->price;
45
  }
46

  
47
  $record_item->set_active_price_source($price_source)  # equivalent to:
48
  $record_item->active_price_source($price_source->name);
49
  $record_item->sellprice($price_source->price);
50

  
51
  # for finer control
52
  $price_source->needed_params
53
  $price_source->supported_params
54

  
55
=head1 DESCRIPTION
56

  
57
PriceSource is an interface that allows generic algorithms to be used, to
58
calculate a price for a position in a record.
59

  
60
If any such price_source algorithm is known to the system, a user can chose
61
which of them should be used to claculate the price displayed in the record.
62

  
63
The algorithm is saved togetherwith the target price, so that changes in the
64
record can recalculate the price accordingly, and otherwise manual changes to
65
the price can reset the price_source used to custom (aka no price_source).
66

  
67
=head1 INTERFACE METHODS
68

  
69
=over 4
70

  
71
=item C<name>
72

  
73
Should return a unique internal name. Should be entered in
74
L<SL::PriceSource::ALL> so that a name_to_class lookup works.
75

  
76
=item C<description>
77

  
78
Should return a translated name.
79

  
80
=item C<needed_params>
81

  
82
Should return a list of elements that a record_item NEEDS to be used with this calulation.
83

  
84
Both C<needed_params> nad C<supported_params> are purely informational at this point.
85

  
86
=item C<supported_params>
87

  
88
Should return a list of elements that a record_item MAY HAVE to be used with this calulation.
89

  
90
Both C<needed_params> nad C<supported_params> are purely informational at this point.
91

  
92
=item C<price>
93

  
94
Calculate a price and return. Do not mutate the record_item. Should will return
95
undef if price is not applicable to the current record_item.
96

  
97
=back
98

  
99
=head1 BUGS
100

  
101
None yet. :)
102

  
103
=head1 AUTHOR
104

  
105
Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>
106

  
107
=cut
SL/PriceSource/MasterData.pm
1
package SL::PriceSource::MasterData;
2

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

  
6
use SL::PriceSource::Price;
7
use SL::Locale::String;
8

  
9
sub name { 'master_data' }
10

  
11
sub description { t8('Master Data') }
12

  
13
sub available_prices {
14
  my ($self, %params) = @_;
15

  
16
  my $part = $self->part;
17

  
18
  return () unless $part;
19

  
20
  # TODO: sellprice only in sales, lastcost in purchase
21
  return $self->make_sellprice($part);
22
}
23

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

  
27
  if ($spec eq 'sellprice') {
28
    return $self->make_sellprice($self->part);
29
  }
30
}
31

  
32
sub make_sellprice {
33
  my ($self, $part) = @_;
34

  
35
  return SL::PriceSource::Price->new(
36
    price        => $part->sellprice,
37
    source       => 'master_data/sellprice',
38
    description  => t8('Sellprice'),
39
    price_source => $self,
40
  );
41
}
42

  
43
1;
SL/PriceSource/Price.pm
1
package SL::PriceSource::Price;
2

  
3
use strict;
4

  
5
use parent 'SL::DB::Object';
6
use Rose::Object::MakeMethods::Generic (
7
  scalar => [ qw(price description source price_source) ],
8
  array => [ qw(depends_on) ]
9
);
10

  
11
sub full_description {
12
  my ($self) = @_;
13

  
14
  $self->price_source
15
    ? $self->price_source->description . ': ' . $self->description
16
    : $self->description
17
}
18

  
19
1;
SL/PriceSource/Pricegroup.pm
1
package SL::PriceSource::Pricegroup;
2

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

  
6
use SL::PriceSource::Price;
7
use SL::Locale::String;
8

  
9
sub name { 'pricegroup' }
10

  
11
sub description { t8('Pricegroup') }
12

  
13
sub available_prices {
14
  my ($self, %params) = @_;
15

  
16
  my $item = $self->record_item;
17

  
18
  my $prices = SL::DB::Manager::Price->get_all(
19
    query        => [ parts_id => $item->parts_id, price => { gt => 0 } ],
20
    with_objects => 'pricegroup',
21
    order_by     => 'pricegroun.id',
22
  );
23

  
24
  return () unless @$prices;
25

  
26
  return map {
27
    $self->make_price($_);
28
  } @$prices;
29
}
30

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

  
34
  my $price = SL::DB::Manager::Price->find_by(id => $spec);
35

  
36
  return $self->make_price($price);
37
}
38

  
39
sub make_price {
40
  my ($self, $price_obj) = @_;
41

  
42
  SL::PriceSource::Price->new(
43
    price        => $price_obj->price,
44
    source       => 'pricegroup/' . $price_obj->id,
45
    description  => $price_obj->pricegroup->pricegroup,
46
    price_source => $self,
47
  )
48
}
49

  
50
1;
bin/mozilla/do.pl
390 390
  #            Kunde mit Rabatt 20 -> Rabatt 5,5 i.O.
391 391
  $form->{payment_id} = $payment_id if $form->{payment_id} eq "";
392 392

  
393
  # for pricegroups
394 393
  my $i = $form->{rowcount};
395 394

  
396 395
  if (   ($form->{"partnumber_$i"} eq "")
......
431 430
        $form->{"sellprice_$i"}          = $form->format_amount(\%myconfig, $form->{"sellprice_$i"} * (1 - $form->{tradediscount}));
432 431
        $form->{"lastcost_$i"}          = $form->format_amount(\%myconfig, $form->{"lastcost_$i"});
433 432
        $form->{"qty_$i"}                = $form->format_amount(\%myconfig, $form->{"qty_$i"});
434

  
435
        # get pricegroups for parts
436
        IS->get_pricegroups_for_parts(\%myconfig, \%$form);
437

  
438
        # build up html code for prices_$i
439
        &set_pricegroup($i);
440 433
      }
441 434

  
442 435
      display_form();
......
849 842

  
850 843
  }
851 844

  
852
  #  show pricegroup in newly loaded invoice when creating invoice from delivery order
853
  for my $i (1 .. $form->{rowcount}) {
854
    $form->{"sellprice_pg_$i"} = join '--', $form->{"sellprice_$i"}, $form->{"pricegroup_id_$i"};
855
  }
856
  IS->get_pricegroups_for_parts(\%myconfig, \%$form);
857
  set_pricegroup($form->{rowcount});
858

  
859 845
  display_form();
860 846

  
861 847
  $main::lxdebug->leave_sub();
......
957 943
  invoice_links();
958 944
  prepare_invoice();
959 945

  
960
  #  show pricegroup in newly loaded invoice when creating invoice from delivery order
961
  for my $i (1 .. $form->{rowcount}) {
962
    $form->{"sellprice_pg_$i"} = join '--', $form->{"sellprice_$i"}, $form->{"pricegroup_id_$i"};
963
  }
964
  IS->get_pricegroups_for_parts(\%myconfig, \%$form);
965
  set_pricegroup($_) for 1 .. $form->{rowcount};
966

  
967 946
  display_form();
968 947

  
969 948
  $main::lxdebug->leave_sub();
bin/mozilla/invoice_io.pl
88 88
use SL::AM;
89 89
use Data::Dumper;
90 90

  
91
sub set_pricegroup {
92
  $main::lxdebug->enter_sub();
93

  
94
  my $form     = $main::form;
95
  my %myconfig = %main::myconfig;
96
  my $locale   = $main::locale;
97

  
98
  my $rowcount = shift;
99
  for my $j (1 .. $rowcount) {
100
    my $pricegroup_old = $form->{"pricegroup_old_$j"};
101
    if ($form->{PRICES}{$j}) {
102
      my $len    = 0;
103
      my $prices = '<option value="--">' . $locale->text("none (pricegroup)") . '</option>';
104
      my $price  = 0;
105
      foreach my $item (@{ $form->{PRICES}{$j} }) {
106

  
107
        #$price = $form->round_amount($myconfig,  $item->{price}, 5);
108
        #$price = $form->format_amount($myconfig, $item->{price}, 2);
109
        my $price         = $item->{price};
110
        my $pricegroup_id = $item->{pricegroup_id};
111
        my $pricegroup    = $item->{pricegroup};
112

  
113
        # build drop down list for pricegroups
114
        $prices .=
115
          qq|<option value="$price--$pricegroup_id"$item->{selected}>$pricegroup</option>\n|;
116

  
117
        $len += 1;
118

  
119
        #        map {
120
        #               $form->{"${_}_$j"} =
121
        #               $form->format_amount(\%myconfig, $form->{"${_}_$j"})
122
        #              } qw(sellprice price_new price_old);
123

  
124
        # set new selectedpricegroup_id and prices for "Preis"
125
        if ($item->{selected} && ($pricegroup_id != 0)) {
126
          $form->{"pricegroup_old_$j"} = $pricegroup_id;
127
          $form->{"price_new_$j"}      = $price;
128
          # edit: don't change the sellprice here
129
          # $form->{"sellprice_$j"}      = $price;   # this must only be updated for existing articles, not new ones
130
        }
131
        if ($pricegroup_id == 0) {
132
          $form->{"price_new_$j"} = $form->{"sellprice_$j"};
133
        }
134
      }
135
      $form->{"prices_$j"} = $prices;
136
    }
137
  }
138
  $main::lxdebug->leave_sub();
139
}
140

  
141 91
sub display_form {
142 92
  $main::lxdebug->enter_sub();
143 93

  
......
188 138
  #     $form->{rowcount}--;
189 139
  #     my $rowcount = $form->{rowcount};
190 140
  #
191
  #     # get pricegroups for parts
192
  #     IS->get_pricegroups_for_parts(\%myconfig, \%$form);
193
  #
194
  #     # build up html code for prices_$i
195
  #     set_pricegroup($rowcount);
196
  #
197 141
  #     $form->{resubmit} = 1;
198 142
  #
199 143
  #   }
bin/mozilla/io.pl
38 38

  
39 39
use Carp;
40 40
use CGI;
41
use List::MoreUtils qw(any uniq);
41
use List::MoreUtils qw(any uniq apply);
42 42
use List::Util qw(min max first);
43 43

  
44 44
use SL::CVar;
......
46 46
use SL::CT;
47 47
use SL::IC;
48 48
use SL::IO;
49
use SL::PriceSource;
49 50

  
50 51
use SL::DB::Customer;
51 52
use SL::DB::Default;
......
149 150
  }
150 151

  
151 152
  # column_index
152
  my @header_sort = qw(runningnumber partnumber description ship qty unit weight sellprice_pg sellprice discount linetotal);
153
  my @header_sort = qw(runningnumber partnumber description ship qty unit weight sellprice discount linetotal);
153 154
  my @HEADER = (
154 155
    {  id => 'runningnumber', width => 5,     value => $locale->text('No.'),                  display => 1, },
155 156
    {  id => 'partnumber',    width => 8,     value => $locale->text('Number'),               display => 1, },
......
162 163
    {  id => 'serialnr',      width => 10,    value => $locale->text('Serial No.'),           display => 0, },
163 164
    {  id => 'projectnr',     width => 10,    value => $locale->text('Project'),              display => 0, },
164 165
    {  id => 'sellprice',     width => 15,    value => $locale->text('Price'),                display => !$is_delivery_order, },
165
    {  id => 'sellprice_pg',  width => 8,     value => $locale->text('Pricegroup'),           display => !$is_delivery_order && !$is_purchase, },
166
    {  id => 'price_source',  width => 5,     value => $locale->text('Price Source'),         display => !$is_delivery_order, },
166 167
    {  id => 'discount',      width => 5,     value => $locale->text('Discount'),             display => !$is_delivery_order, },
167 168
    {  id => 'linetotal',     width => 10,    value => $locale->text('Extended'),             display => !$is_delivery_order, },
168 169
    {  id => 'bin',           width => 10,    value => $locale->text('Bin'),                  display => 0, },
......
196 197
  my $deliverydate  = $locale->text('Required by');
197 198

  
198 199
  # special alignings
199
  my %align  = map { $_ => 'right' } qw(qty ship right sellprice_pg discount linetotal stock_in_out weight);
200
  my %align  = map { $_ => 'right' } qw(qty ship right discount linetotal stock_in_out weight);
200 201
  my %nowrap = map { $_ => 1 }       qw(description unit);
201 202

  
202 203
  $form->{marge_total}           = 0;
......
232 233
      $form->{"sellprice_$i"} = $form->{"price_new_$i"};
233 234
    }
234 235

  
236
    my $record_item = _make_record_item($i);
237

  
235 238
# unit begin
236 239
    $form->{"unit_old_$i"}      ||= $form->{"unit_$i"};
237 240
    $form->{"selected_unit_$i"} ||= $form->{"unit_$i"};
......
240 243
        || !AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units)) { # (z.B. Dimensionseinheit war ausgewaehlt, es handelt sich aber
241 244
      $form->{"unit_old_$i"} = $form->{"selected_unit_$i"} = $form->{"unit_$i"};                 # um eine Dienstleistung). Dann keinerlei Umrechnung vornehmen.
242 245
    }
243
    # adjust prices by unit, ignore if pricegroup changed
244
    if ((!$form->{"prices_$i"}) || ($form->{"new_pricegroup_$i"} == $form->{"old_pricegroup_$i"})) {
245
        $form->{"sellprice_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1;
246
        $form->{"lastcost_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1;
247
        $form->{"unit_old_$i"}   = $form->{"selected_unit_$i"};
248
    }
246

  
247
    $form->{"sellprice_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1;
248
    $form->{"lastcost_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1;
249
    $form->{"unit_old_$i"}   = $form->{"selected_unit_$i"};
250

  
249 251
    my $this_unit = $form->{"unit_$i"};
250 252
    $this_unit    = $form->{"selected_unit_$i"} if AM->convert_unit($this_unit, $form->{"selected_unit_$i"}, $all_units);
251 253

  
......
305 307
      $column_data{ship}  = $form->format_amount(\%myconfig, $form->round_amount($ship_qty, 2) * 1) . ' ' . $form->{"unit_$i"};
306 308
    }
307 309

  
308
    # build in drop down list for pricesgroups
309
    # $sellprice_value setzt den Wert etwas unabhängiger von der Darstellung.
310
    # Hintergrund: Preisgruppen werden hier überprüft und neu berechnet.
311
    # Vorher wurde der ganze cgi->textfield Block zweimal identisch eingebaut, dass passiert
312
    # jetzt nach der Abfrage.
313
    my $sellprice_value;
314
    if ($form->{"prices_$i"}) {
315
      $column_data{sellprice_pg} = qq|<select name="sellprice_pg_$i" style="width: 8em">$form->{"prices_$i"}</select>|;
316
      $sellprice_value           =($form->{"new_pricegroup_$i"} != $form->{"old_pricegroup_$i"})
317
                                      ? $form->format_amount(\%myconfig, $form->{"price_new_$i"}, $decimalplaces)
318
                                      : $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
319
    } else {
320
      # for last row and report
321
      # set pricegroup drop down list from report menu
322
      if ($form->{"sellprice_$i"} != 0) {
323
        # remember the pricegroup_id in pricegroup_old
324
        # but don't overwrite it
325
        $form->{"pricegroup_old_$i"} = $form->{"pricegroup_id_$i"};
326
        my $default_option           = $form->{"sellprice_$i"}.'--'.$form->{"pricegroup_id_$i"};
327
        $column_data{sellprice_pg}   = NTI($cgi->popup_menu("sellprice_pg_$i", [ $default_option ], $default_option, { $default_option => $form->{"pricegroup_$i"} || '' }));
328
      } else {
329
        $column_data{sellprice_pg} = qq|&nbsp;|;
330
      }
331
      $sellprice_value = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
332

  
333
    }
334
    # Falls der Benutzer die Preise nicht anpassen sollte, wird das entsprechende
335
    # Textfield auf readonly gesetzt. Anm. von Sven: Manipulation der Preise ist
336
    # immer noch möglich, konsequenterweise sollten diese NUR aus der Datenbank
337
    # geholt werden.
338
    my $edit_prices = $main::auth->assert('edit_prices', 1);
339
    $column_data{sellprice} = (!$edit_prices)
340
                                ? $cgi->textfield(-readonly => "readonly",
341
                                                  -name => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value => $sellprice_value)
342
                                : $cgi->textfield(-name => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value => $sellprice_value);
310
    my $sellprice_value = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
311
    my $edit_prices     = $main::auth->assert('edit_prices', 1) && !$::form->{"active_price_source_$i"};
312
    $column_data{sellprice}   = (!$edit_prices)
313
                                ? $cgi->hidden(   -name => "sellprice_$i", -id => "sellprice_$i", -value => $sellprice_value) . $sellprice_value
314
                                : $cgi->textfield(-name => "sellprice_$i", -id => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value => $sellprice_value);
343 315
    $column_data{discount}    = (!$edit_prices)
344 316
                                  ? $cgi->textfield(-readonly => "readonly",
345 317
                                                    -name => "discount_$i", -size => 3, -value => $form->format_amount(\%myconfig, $form->{"discount_$i"}))
......
349 321

  
350 322
    $column_data{weight}      = $form->format_amount(\%myconfig, $form->{"qty_$i"} * $form->{"weight_$i"}, 3) . ' ' . $defaults->{weightunit} if $defaults->{show_weight};
351 323

  
324
    if ($form->{"id_${i}"}) {
325
      my $price_source = SL::PriceSource->new(record_item => $record_item);
326
      my $price = $price_source->price_from_source($::form->{"active_price_source_$i"});
327
      $::form->{price_sources}[$i] = $price_source;
328
      $column_data{price_source} .= $cgi->button(-value => $price->full_description, -onClick => "toggle_price_source($i)");
329
    }
330

  
352 331
    if ($is_delivery_order) {
353 332
      $column_data{stock_in_out} =  calculate_stock_in_out($i);
354 333
    }
......
433 412

  
434 413
    if ($is_delivery_order) {
435 414
      map { $form->{"${_}_${i}"} = $form->format_amount(\%myconfig, $form->{"${_}_${i}"}) } qw(sellprice discount lastcost);
436
      $form->{"pricegroup_id_$i"} = $form->{"pricegroup_old_$i"} if $form->{"pricegroup_old_$i"};
437
      $form->{"sellprice_pg_$i"}  = $form->{"hidden_prices_$i"}  if $form->{"hidden_prices_$i"};
438
      push @hidden_vars, grep { defined $form->{"${_}_${i}"} } qw(sellprice discount not_discountable price_factor_id lastcost pricegroup_id sellprice_pg);
415
      push @hidden_vars, grep { defined $form->{"${_}_${i}"} } qw(sellprice discount not_discountable price_factor_id lastcost);
439 416
      push @hidden_vars, "stock_${stock_in_out}_sum_qty", "stock_${stock_in_out}";
440 417
    }
441 418

  
......
443 420
          $cgi->hidden("-name" => "unit_old_$i", "-value" => $form->{"selected_unit_$i"}),
444 421
          $cgi->hidden("-name" => "price_new_$i", "-value" => $form->format_amount(\%myconfig, $form->{"price_new_$i"})),
445 422
          map { ($cgi->hidden("-name" => $_, "-id" => $_, "-value" => $form->{$_})); } map { $_."_$i" }
446
            (qw(orderitems_id bo pricegroup_old price_old id inventory_accno bin partsgroup partnotes
423
            (qw(orderitems_id bo price_old id inventory_accno bin partsgroup partnotes active_price_source
447 424
                income_accno expense_accno listprice assembly taxaccounts ordnumber donumber transdate cusordnumber
448 425
                longdescription basefactor marge_absolut marge_percent marge_price_factor weight), @hidden_vars)
449 426
    );
......
455 432
    # Benutzerdefinierte Variablen für Waren/Dienstleistungen/Erzeugnisse
456 433
    _render_custom_variables_inputs(ROW2 => \@ROW2, row => $i, part_id => $form->{"id_$i"});
457 434

  
458
    push @ROWS, { ROW1 => \@ROW1, ROW2 => \@ROW2, HIDDENS => \@HIDDENS, colspan => $colspan, error => $form->{"row_error_$i"}, };
435
    push @ROWS, { ROW1 => \@ROW1, ROW2 => \@ROW2, HIDDENS => \@HIDDENS, colspan => $colspan, error => $form->{"row_error_$i"}, obj => $record_item };
459 436
  }
460 437

  
461 438
  $form->{totalweight} = $totalweight;
......
471 448
  $main::lxdebug->leave_sub();
472 449
}
473 450

  
474
##################################################
475
# build html-code for pricegroups in variable $form->{prices_$j}
476

  
477
sub set_pricegroup {
478
  $main::lxdebug->enter_sub();
479

  
480
  my $form     = $main::form;
481
  my $locale   = $main::locale;
482
  my $cgi      = $::request->{cgi};
483

  
484
  _check_io_auth();
485

  
486
  my $rowcount = shift;
487
  for my $j (1 .. $rowcount) {
488
    next unless $form->{PRICES}{$j};
489
    # build drop down list for pricegroups
490
    my $option_tmpl = qq|<option value="%s--%s" %s>%s</option>|;
491
    $form->{"prices_$j"}  = join '', map { sprintf $option_tmpl, @$_{qw(price pricegroup_id selected pricegroup)} }
492
                                         (+{ pricegroup => $locale->text("none (pricegroup)") }, @{ $form->{PRICES}{$j} });
493

  
494
    foreach my $item (@{ $form->{PRICES}{$j} }) {
495
      # set new selectedpricegroup_id and prices for "Preis"
496
      $form->{"pricegroup_old_$j"} = $item->{pricegroup_id}   if $item->{selected} &&  $item->{pricegroup_id};
497
      $form->{"sellprice_$j"}      = $item->{price}           if $item->{selected} &&  $item->{pricegroup_id};
498
      $form->{"price_new_$j"}      = $form->{"sellprice_$j"}  if $item->{selected} || !$item->{pricegroup_id};
499
    }
500

  
501
    # save hidden pricegroups for delivery_orders
502
    next unless my @selected_prices = grep { $_->{selected} } @{ $form->{PRICES}{$j} };
503
    $form->{"hidden_prices_$j"} = $selected_prices[-1]{price} . "--" . $selected_prices[-1]{pricegroup_id};
504
  }
505
  $main::lxdebug->leave_sub();
506
}
507

  
508 451
sub select_item {
509 452
  $main::lxdebug->enter_sub();
510 453

  
......
633 576
      $form->format_amount(\%myconfig, $form->{"${_}_$i"}, $decimalplaces)
634 577
  } qw(sellprice listprice lastcost) if $form->{item} ne 'assembly';
635 578

  
636
  # get pricegroups for parts
637
  IS->get_pricegroups_for_parts(\%myconfig, \%$form);
638

  
639
  # build up html code for prices_$i
640
  set_pricegroup($form->{rowcount});
641

  
642 579
  &display_form;
643 580

  
644 581
  $main::lxdebug->leave_sub();
......
733 670
      or (($form->{level} eq undef) and ($form->{type} =~ /invoice/))
734 671
      or ($form->{type} =~ /sales_order/)) {
735 672

  
736
    # get pricegroups for parts
737
    IS->get_pricegroups_for_parts(\%myconfig, \%$form);
738

  
739
    # build up html code for prices_$i
740
    set_pricegroup($form->{rowcount});
741

  
742 673
  }
743 674

  
744 675
  &display_form;
......
757 688
                taxaccounts bin assembly weight projectnumber project_id
758 689
                oldprojectnumber runningnumber serialnumber partsgroup payment_id
759 690
                not_discountable shop ve gv buchungsgruppen_id language_values
760
                sellprice_pg pricegroup_old price_old price_new unit_old ordnumber donumber
691
                price_old price_new unit_old ordnumber donumber
761 692
                transdate longdescription basefactor marge_total marge_percent
762 693
                marge_price_factor lastcost price_factor_id partnotes
763 694
                stock_out stock_in has_sernumber reqdate orderitems_id);
......
1714 1645
  # get details for customer/vendor
1715 1646
  call_sub($::form->{vc} . "_details", qw(name department_1 department_2 street zipcode city country contact email phone fax), $::form->{vc} . "number");
1716 1647

  
1717
  # get pricegroups for parts
1718
  IS->get_pricegroups_for_parts(\%::myconfig, \%$::form);
1719

  
1720
  # build up html code for prices_$i
1721
  set_pricegroup($::form->{rowcount});
1722

  
1723 1648
  $::form->{rowcount}--;
1724 1649

  
1725 1650
  my @shipto_vars   = qw(shiptoname shiptostreet shiptozipcode shiptocity shiptocountry
......
1964 1889
  $::form->redo_rows(\@fields, \@new_rows, scalar(@new_rows), $::form->{rowcount});
1965 1890
  $::form->{rowcount} -= $removed_rows;
1966 1891
}
1892

  
1893
sub _make_record_item {
1894
  my ($row) = @_;
1895

  
1896
  my $class = {
1897
    sales_order             => 'OrderItem',
1898
    purchase_oder           => 'OrderItem',
1899
    sales_quotation         => 'OrderItem',
1900
    request_quotation       => 'OrderItem',
1901
    invoice                 => 'InvoiceItem',
1902
    purchase_invoice        => 'InvoiceItem',
1903
    purchase_delivery_order => 'DeliveryOrderItem',
1904
    sales_delivery_order    => 'DeliveryOrderItem',
1905
  }->{$::form->{type}};
1906

  
1907
  return unless $class;
1908

  
1909
  $class = 'SL::DB::' . $class;
1910

  
1911
  eval "require $class";
1912

  
1913
  my $obj = $::form->{"orderitems_id_$row"}
1914
          ? $class->meta->convention_manager->auto_manager_class_name->find_by(id => $::form->{"orderitems_id_$row"})
1915
          : $class->new;
1916

  
1917
  for my $method (apply { s/_$row$// } grep { /_$row$/ } keys %$::form) {
1918
    next unless $obj->meta->column($method);
1919
    if ($obj->meta->column($method)->isa('Rose::DB::Object::Metadata::Column::Date')) {
1920
      $obj->${\"$method\_as_date"}($::form->{"$method\_$row"});
1921
    } else {
1922
      $obj->$method($::form->{"$method\_$row"});
1923
    }
1924
  }
1925

  
1926
  if ($::form->{"id_$row"}) {
1927
    $obj->part(SL::DB::Part->load_cached($::form->{"id_$row"}));
1928
  }
1929

  
1930
  return $obj;
1931
}
bin/mozilla/is.pl
273 273
      $form->{rowcount}        = $i;
274 274

  
275 275
    }
276

  
277
    # get pricegroups for parts
278
    IS->get_pricegroups_for_parts(\%myconfig, \%$form);
279

  
280
    # Problem: set_pricegroup resets the sellprice of old invoices to the price
281
    # currently defined in the pricegroup, which is a problem if the price has
282
    # changed, as the old invoice gets the new price
283
    # set_pricegroup must never be called, when an old invoice is initially loaded
284

  
285
    # set_pricegroup($_) for 1 .. $form->{rowcount};
286 276
  }
287 277
  $main::lxdebug->leave_sub();
288 278
}
......
614 604
        map { $form->{"${_}_$i"} = $form->format_amount(\%myconfig, $form->{"${_}_$i"}, $decimalplaces) } qw(sellprice lastcost);
615 605

  
616 606
        $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"});
617

  
618
        # get pricegroups for parts
619
        IS->get_pricegroups_for_parts(\%myconfig, \%$form);
620

  
621
        # build up html code for prices_$i
622
        &set_pricegroup($i);
623 607
      }
624 608

  
625 609
      &display_form;
......
837 821
  $form->{forex}        = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, 'buy');
838 822
  $form->{exchangerate} = $form->{forex} if $form->{forex};
839 823

  
840
  # remember pricegroups for "use as new"
841
  IS->get_pricegroups_for_parts(\%myconfig, \%$form);
842
  set_pricegroup($_) for 1 .. $form->{rowcount};
843

  
844 824
  &display_form;
845 825

  
846 826
  $main::lxdebug->leave_sub();
bin/mozilla/oe.pl
594 594

  
595 595
  check_oe_access();
596 596

  
597
  my $order = _make_record();
598

  
597 599
  set_headings($form->{"id"} ? "edit" : "add");
598 600

  
599 601
  $form->{update} = 1;
......
692 694
        $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
693 695
        $form->{"lastcost_$i"}  = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}, $decimalplaces);
694 696
        $form->{"qty_$i"}       = $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
695

  
696
        # get pricegroups for parts
697
        IS->get_pricegroups_for_parts(\%myconfig, \%$form);
698

  
699
        # build up html code for prices_$i
700
        &set_pricegroup($i);
701 697
      }
702 698

  
703 699
      display_form();
......
1510 1506
      $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
1511 1507
  }
1512 1508

  
1513
  #  show pricegroup in newly loaded invoice when creating invoice from quotation/order
1514
  IS->get_pricegroups_for_parts(\%myconfig, \%$form);
1515
  set_pricegroup($_) for 1 .. $form->{rowcount};
1516

  
1517 1509
  &display_form;
1518 1510

  
1519 1511
  $main::lxdebug->leave_sub();
......
2118 2110

  
2119 2111
  $::form->error($::locale->text('No action defined.'));
2120 2112
}
2113

  
2114
sub _make_record {
2115
  my $obj = SL::DB::Order->new;
2116

  
2117
  for my $method (keys %$::form) {
2118
    next unless $obj->can($method);
2119
    next unless $obj->meta->column($method);
2120

  
2121
    if ($obj->meta->column($method)->isa('Rose::DB::Object::Metadata::Column::Date')) {
2122
      $obj->${\"$method\_as_date"}($::form->{$method});
2123
    } elsif ((ref $obj->meta->column($method)) =~ /^Rose::DB::Object::Metadata::Column::(?:Integer|Numeric|Float|DoublePrecsion)$/) {
2124
      $obj->$method($::form->{$method});
2125
    }
2126
  }
2127

  
2128
  my @items;
2129
  for my $i (1 .. $::form->{rowcount}) {
2130
    next unless $::form->{"id_$i"};
2131
    push @items, _make_record_item($i)
2132
  }
2133

  
2134
  $obj->orderitems(@items);
2135

  
2136
  return $obj;
2137
}
2138

  
locale/de/all
1596 1596
  'No.'                         => 'Position',
1597 1597
  'No/individual shipping address' => 'Keine/individuelle Lieferadresse',
1598 1598
  'None'                        => 'Kein',
1599
  'None (PriceSource)'          => 'Freier Preis',
1599 1600
  'Normal users cannot log in.' => 'Normale Benutzer können sich nicht anmelden.',
1600 1601
  'Normalize Customer / Vendor names' => 'Normalisierung Kunden- / Lieferantennamen',
1601 1602
  'Normalize part description and part notes' => 'Normalisierung Artikelbeschreibung und Artikellangtext (Bemerkung)',
......
1832 1833
  'Price'                       => 'Preis',
1833 1834
  'Price Factor'                => 'Preisfaktor',
1834 1835
  'Price Factors'               => 'Preisfaktoren',
1836
  'Price Source'                => 'Preisquelle',
1835 1837
  'Price factor (database ID)'  => 'Preisfaktor (Datenbank-ID)',
1836 1838
  'Price factor (name)'         => 'Preisfaktor (Name)',
1837 1839
  'Price factor deleted!'       => 'Preisfaktor gel&ouml;scht.',
......
3051 3053
  'no article assigned yet'     => 'noch kein Artikel zugewiesen',
3052 3054
  'no bestbefore'               => 'keine Mindesthaltbarkeit',
3053 3055
  'no chargenumber'             => 'keine Chargennummer',
3054
  'none (pricegroup)'           => 'keine',
3055 3056
  'not configured'              => 'nicht konfiguriert',
3056 3057
  'not delivered'               => 'nicht geliefert',
3057 3058
  'not executed'                => 'nicht ausgeführt',
sql/Pg-upgrade2/recorditem_active_price_source.sql
1
-- @tag: recorditem_active_price_source
2
-- @description: Preisquelle in Belegpositionen
3
-- @depends: release_2_6_2
4
-- @encoding: utf-8
5

  
6
ALTER TABLE orderitems ADD COLUMN active_price_source TEXT NOT NULL DEFAULT '';
7
ALTER TABLE delivery_order_items ADD COLUMN active_price_source TEXT NOT NULL DEFAULT '';
8
ALTER TABLE invoice ADD COLUMN active_price_source TEXT NOT NULL DEFAULT '';
templates/webpages/oe/_price_sources_row.html
1
[%- USE T8 %]
2
[%- USE HTML %]
3
[%- USE L %]
4
[%- USE LxERP %]
5
<tr class="listrow[% i % 2 %]" id="row[% i %]_3" style='display:none'>
6
 <td colspan="[% row.colspan %]">
7
   <span class="[% IF !row.obj.active_price_source %]bold[% END %]">
8
   [% L.radio_button_tag('active_price_source_' _ i, label=LxERP.t8('None (PriceSource)'), checked=!row.obj.active_price_source, value='', onChange='update_price_source(' _ i _ ', \'\')') %]
9
   </span>
10
   [%- FOREACH price IN price_sources.$i.available_prices %]
11
     <div class="[% IF price.source == row.obj.active_price_source %]bold[% END %]">
12
     [% L.radio_button_tag('active_price_source_' _ i, value=price.source, checked=price.source == row.obj.active_price_source, label=LxERP.format_amount(price.price, 2) _ ' (' _ price.full_description _ ')', onChange='update_price_source(' _ i _ ', \'' _ price.source _ '\', \'' _ LxERP.format_amount(price.price, -2) _ '\')' ) %]
13
     </div>
14
   [%- END %]
15
 </td>
16
</tr>
templates/webpages/oe/sales_order.html
1 1
[%- USE T8 %]
2 2
[%- USE HTML %]
3

  
3
[%- USE L %]
4
[%- USE LxERP %]
4 5
[%- PROCESS 'amcvar/render_inputs_block.html' %]
5 6
<tr>
6 7
 <td>
......
74 75

  
75 76
      </td>
76 77
     </tr>
... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen überschreitet.

Auch abrufbar als: Unified diff