Revision eebe8e90
Von Sven Schöling vor etwa 10 Jahren hinzugefügt
SL/DB/Helper/PriceTaxCalculator.pm | ||
---|---|---|
use strict;
|
||
|
||
use parent qw(Exporter);
|
||
our @EXPORT = qw(calculate_prices_and_taxes);
|
||
our @EXPORT = qw(calculate_prices_and_taxes _calculate_item);
|
||
|
||
use Carp;
|
||
use List::Util qw(sum min max);
|
||
... | ... | |
my ($self, $item, $idx, $data, %params) = @_;
|
||
|
||
my $part = SL::DB::Part->load_cached($item->parts_id);
|
||
return unless $item->part;
|
||
|
||
my $part_unit = $data->{units_by_name}->{ $part->unit };
|
||
my $item_unit = $data->{units_by_name}->{ $item->unit };
|
||
|
SL/DB/MetaSetup/DeliveryOrderItem.pm | ||
---|---|---|
__PACKAGE__->meta->table('delivery_order_items');
|
||
|
||
__PACKAGE__->meta->columns(
|
||
base_qty => { type => 'float', scale => 4 },
|
||
cusordnumber => { type => 'text' },
|
||
delivery_order_id => { type => 'integer', not_null => 1 },
|
||
description => { type => 'text' },
|
||
discount => { type => 'float', scale => 4 },
|
||
id => { type => 'integer', not_null => 1, sequence => 'delivery_order_items_id' },
|
||
itime => { type => 'timestamp', default => 'now()' },
|
||
lastcost => { type => 'numeric', precision => 15, scale => 5 },
|
||
longdescription => { type => 'text' },
|
||
marge_price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
|
||
mtime => { type => 'timestamp' },
|
||
ordnumber => { type => 'text' },
|
||
parts_id => { type => 'integer', not_null => 1 },
|
||
price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
|
||
price_factor_id => { type => 'integer' },
|
||
pricegroup_id => { type => 'integer' },
|
||
project_id => { type => 'integer' },
|
||
qty => { type => 'numeric', precision => 25, scale => 5 },
|
||
reqdate => { type => 'date' },
|
||
sellprice => { type => 'numeric', precision => 15, scale => 5 },
|
||
serialnumber => { type => 'text' },
|
||
transdate => { type => 'text' },
|
||
unit => { type => 'varchar', length => 20 },
|
||
base_qty => { type => 'float', scale => 4 },
|
||
cusordnumber => { type => 'text' },
|
||
delivery_order_id => { type => 'integer', not_null => 1 },
|
||
description => { type => 'text' },
|
||
discount => { type => 'float', scale => 4 },
|
||
id => { type => 'integer', not_null => 1, sequence => 'delivery_order_items_id' },
|
||
itime => { type => 'timestamp', default => 'now()' },
|
||
lastcost => { type => 'numeric', precision => 15, scale => 5 },
|
||
longdescription => { type => 'text' },
|
||
marge_price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
|
||
mtime => { type => 'timestamp' },
|
||
ordnumber => { type => 'text' },
|
||
parts_id => { type => 'integer', not_null => 1 },
|
||
price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
|
||
price_factor_id => { type => 'integer' },
|
||
pricegroup_id => { type => 'integer' },
|
||
project_id => { type => 'integer' },
|
||
qty => { type => 'numeric', precision => 25, scale => 5 },
|
||
reqdate => { type => 'date' },
|
||
sellprice => { type => 'numeric', precision => 15, scale => 5 },
|
||
serialnumber => { type => 'text' },
|
||
transdate => { type => 'text' },
|
||
unit => { type => 'varchar', length => 20 },
|
||
active_price_source => { type => 'text', default => '', not_null => 1 },
|
||
);
|
||
|
||
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
|
SL/DB/MetaSetup/InvoiceItem.pm | ||
---|---|---|
__PACKAGE__->meta->table('invoice');
|
||
|
||
__PACKAGE__->meta->columns(
|
||
allocated => { type => 'float', scale => 4 },
|
||
assemblyitem => { type => 'boolean', default => 'false' },
|
||
base_qty => { type => 'float', scale => 4 },
|
||
cusordnumber => { type => 'text' },
|
||
deliverydate => { type => 'date' },
|
||
description => { type => 'text' },
|
||
discount => { type => 'float', scale => 4 },
|
||
donumber => { type => 'text' },
|
||
fxsellprice => { type => 'numeric', precision => 15, scale => 5 },
|
||
id => { type => 'integer', not_null => 1, sequence => 'invoiceid' },
|
||
itime => { type => 'timestamp', default => 'now()' },
|
||
lastcost => { type => 'numeric', precision => 15, scale => 5 },
|
||
longdescription => { type => 'text' },
|
||
marge_percent => { type => 'numeric', precision => 15, scale => 5 },
|
||
marge_price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
|
||
marge_total => { type => 'numeric', precision => 15, scale => 5 },
|
||
mtime => { type => 'timestamp' },
|
||
ordnumber => { type => 'text' },
|
||
parts_id => { type => 'integer' },
|
||
price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
|
||
price_factor_id => { type => 'integer' },
|
||
pricegroup_id => { type => 'integer' },
|
||
project_id => { type => 'integer' },
|
||
qty => { type => 'float', scale => 4 },
|
||
sellprice => { type => 'numeric', precision => 15, scale => 5 },
|
||
serialnumber => { type => 'text' },
|
||
subtotal => { type => 'boolean', default => 'false' },
|
||
trans_id => { type => 'integer' },
|
||
transdate => { type => 'text' },
|
||
unit => { type => 'varchar', length => 20 },
|
||
allocated => { type => 'float', scale => 4 },
|
||
assemblyitem => { type => 'boolean', default => 'false' },
|
||
base_qty => { type => 'float', scale => 4 },
|
||
cusordnumber => { type => 'text' },
|
||
deliverydate => { type => 'date' },
|
||
description => { type => 'text' },
|
||
discount => { type => 'float', scale => 4 },
|
||
donumber => { type => 'text' },
|
||
fxsellprice => { type => 'numeric', precision => 15, scale => 5 },
|
||
id => { type => 'integer', not_null => 1, sequence => 'invoiceid' },
|
||
itime => { type => 'timestamp', default => 'now()' },
|
||
lastcost => { type => 'numeric', precision => 15, scale => 5 },
|
||
longdescription => { type => 'text' },
|
||
marge_percent => { type => 'numeric', precision => 15, scale => 5 },
|
||
marge_price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
|
||
marge_total => { type => 'numeric', precision => 15, scale => 5 },
|
||
mtime => { type => 'timestamp' },
|
||
ordnumber => { type => 'text' },
|
||
parts_id => { type => 'integer' },
|
||
price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
|
||
price_factor_id => { type => 'integer' },
|
||
pricegroup_id => { type => 'integer' },
|
||
project_id => { type => 'integer' },
|
||
qty => { type => 'float', scale => 4 },
|
||
sellprice => { type => 'numeric', precision => 15, scale => 5 },
|
||
serialnumber => { type => 'text' },
|
||
subtotal => { type => 'boolean', default => 'false' },
|
||
trans_id => { type => 'integer' },
|
||
transdate => { type => 'text' },
|
||
unit => { type => 'varchar', length => 20 },
|
||
active_price_source => { type => 'text', default => '', not_null => 1 },
|
||
);
|
||
|
||
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
|
SL/DB/MetaSetup/OrderItem.pm | ||
---|---|---|
__PACKAGE__->meta->table('orderitems');
|
||
|
||
__PACKAGE__->meta->columns(
|
||
base_qty => { type => 'float', scale => 4 },
|
||
cusordnumber => { type => 'text' },
|
||
description => { type => 'text' },
|
||
discount => { type => 'float', scale => 4 },
|
||
id => { type => 'integer', not_null => 1, sequence => 'orderitemsid' },
|
||
itime => { type => 'timestamp', default => 'now()' },
|
||
lastcost => { type => 'numeric', precision => 15, scale => 5 },
|
||
longdescription => { type => 'text' },
|
||
marge_percent => { type => 'numeric', precision => 15, scale => 5 },
|
||
marge_price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
|
||
marge_total => { type => 'numeric', precision => 15, scale => 5 },
|
||
mtime => { type => 'timestamp' },
|
||
ordnumber => { type => 'text' },
|
||
parts_id => { type => 'integer' },
|
||
price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
|
||
price_factor_id => { type => 'integer' },
|
||
pricegroup_id => { type => 'integer' },
|
||
project_id => { type => 'integer' },
|
||
qty => { type => 'float', scale => 4 },
|
||
reqdate => { type => 'date' },
|
||
sellprice => { type => 'numeric', precision => 15, scale => 5 },
|
||
serialnumber => { type => 'text' },
|
||
ship => { type => 'float', scale => 4 },
|
||
subtotal => { type => 'boolean', default => 'false' },
|
||
trans_id => { type => 'integer' },
|
||
transdate => { type => 'text' },
|
||
unit => { type => 'varchar', length => 20 },
|
||
base_qty => { type => 'float', scale => 4 },
|
||
cusordnumber => { type => 'text' },
|
||
description => { type => 'text' },
|
||
discount => { type => 'float', scale => 4 },
|
||
id => { type => 'integer', not_null => 1, sequence => 'orderitemsid' },
|
||
itime => { type => 'timestamp', default => 'now()' },
|
||
lastcost => { type => 'numeric', precision => 15, scale => 5 },
|
||
longdescription => { type => 'text' },
|
||
marge_percent => { type => 'numeric', precision => 15, scale => 5 },
|
||
marge_price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
|
||
marge_total => { type => 'numeric', precision => 15, scale => 5 },
|
||
mtime => { type => 'timestamp' },
|
||
ordnumber => { type => 'text' },
|
||
parts_id => { type => 'integer' },
|
||
price_factor => { type => 'numeric', default => 1, precision => 15, scale => 5 },
|
||
price_factor_id => { type => 'integer' },
|
||
pricegroup_id => { type => 'integer' },
|
||
project_id => { type => 'integer' },
|
||
qty => { type => 'float', scale => 4 },
|
||
reqdate => { type => 'date' },
|
||
sellprice => { type => 'numeric', precision => 15, scale => 5 },
|
||
serialnumber => { type => 'text' },
|
||
ship => { type => 'float', scale => 4 },
|
||
subtotal => { type => 'boolean', default => 'false' },
|
||
trans_id => { type => 'integer' },
|
||
transdate => { type => 'text' },
|
||
unit => { type => 'varchar', length => 20 },
|
||
active_price_source => { type => 'text', default => '', not_null => 1 },
|
||
);
|
||
|
||
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
|
SL/DO.pm | ||
---|---|---|
id, delivery_order_id, parts_id, description, longdescription, qty, base_qty,
|
||
sellprice, discount, unit, reqdate, project_id, serialnumber,
|
||
ordnumber, transdate, cusordnumber,
|
||
lastcost, price_factor_id, price_factor, marge_price_factor, pricegroup_id)
|
||
lastcost, price_factor_id, price_factor, marge_price_factor, pricegroup_id,
|
||
active_price_source)
|
||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||
(SELECT factor FROM price_factors WHERE id = ?), ?, ?)|;
|
||
(SELECT factor FROM price_factors WHERE id = ?), ?, ?, ?)|;
|
||
my $h_item = prepare_query($form, $dbh, $q_item);
|
||
|
||
my $q_item_stock =
|
||
... | ... | |
$form->{"lastcost_$i"},
|
||
conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
|
||
conv_i($form->{"marge_price_factor_$i"}),
|
||
$pricegroup_id);
|
||
$pricegroup_id,
|
||
$form->{"active_price_source_$i"});
|
||
do_statement($form, $h_item, $q_item, @values);
|
||
|
||
my $stock_info = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_$i"});
|
||
... | ... | |
doi.reqdate, doi.project_id, doi.serialnumber, doi.lastcost,
|
||
doi.ordnumber, doi.transdate, doi.cusordnumber, doi.longdescription,
|
||
doi.price_factor_id, doi.price_factor, doi.marge_price_factor, doi.pricegroup_id,
|
||
doi.active_price_source,
|
||
pr.projectnumber, dord.transdate AS dord_transdate, dord.donumber,
|
||
pg.partsgroup
|
||
FROM delivery_order_items doi
|
SL/IS.pm | ||
---|---|---|
$main::lxdebug->leave_sub();
|
||
}
|
||
|
||
##########################
|
||
# get pricegroups from database
|
||
# build up selected pricegroup
|
||
# if an exchange rate - change price
|
||
# for each part
|
||
#
|
||
sub get_pricegroups_for_parts {
|
||
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my ($self, $myconfig, $form) = @_;
|
||
|
||
my $dbh = $form->get_standard_dbh;
|
||
|
||
$form->{"PRICES"} = {};
|
||
|
||
my $i = 1;
|
||
my $id = 0;
|
||
my $all_units = AM->retrieve_units($myconfig, $form);
|
||
while (($form->{"id_$i"}) or ($form->{"new_id_$i"})) {
|
||
$form->{"PRICES"}{$i} = [];
|
||
|
||
$id = $form->{"id_$i"};
|
||
|
||
if (!($form->{"id_$i"}) and $form->{"new_id_$i"}) {
|
||
$id = $form->{"new_id_$i"};
|
||
}
|
||
|
||
my ($price, $selectedpricegroup_id) = split(/--/, $form->{"sellprice_pg_$i"});
|
||
|
||
my $pricegroup_old = $form->{"pricegroup_old_$i"};
|
||
|
||
# sellprice has format 13,0000 or 0,00000, can't check for 0 numerically
|
||
my $sellprice = $form->{"sellprice_$i"};
|
||
my $pricegroup_id = $form->{"pricegroup_id_$i"};
|
||
$form->{"new_pricegroup_$i"} = $selectedpricegroup_id;
|
||
$form->{"old_pricegroup_$i"} = $pricegroup_old;
|
||
|
||
my $price_new = $form->{"price_new_$i"};
|
||
my $price_old = $form->{"price_old_$i"};
|
||
|
||
if (!$form->{"unit_old_$i"}) {
|
||
# Neue Ware aus der Datenbank. In diesem Fall ist unit_$i die
|
||
# Einheit, wie sie in den Stammdaten hinterlegt wurde.
|
||
# Es sollte also angenommen werden, dass diese ausgewaehlt war.
|
||
$form->{"unit_old_$i"} = $form->{"unit_$i"};
|
||
}
|
||
|
||
# Die zuletzt ausgewaehlte mit der aktuell ausgewaehlten Einheit
|
||
# vergleichen und bei Unterschied den Preis entsprechend umrechnen.
|
||
$form->{"selected_unit_$i"} = $form->{"unit_$i"} unless ($form->{"selected_unit_$i"});
|
||
|
||
if (!$all_units->{$form->{"selected_unit_$i"}} ||
|
||
($all_units->{$form->{"selected_unit_$i"}}->{"base_unit"} ne
|
||
$all_units->{$form->{"unit_old_$i"}}->{"base_unit"})) {
|
||
# Die ausgewaehlte Einheit ist fuer diesen Artikel nicht gueltig
|
||
# (z.B. Dimensionseinheit war ausgewaehlt, es handelt sich aber
|
||
# um eine Dienstleistung). Dann keinerlei Umrechnung vornehmen.
|
||
$form->{"unit_old_$i"} = $form->{"selected_unit_$i"} = $form->{"unit_$i"};
|
||
}
|
||
|
||
my $basefactor = 1;
|
||
|
||
if ($form->{"unit_old_$i"} ne $form->{"selected_unit_$i"}) {
|
||
if (defined($all_units->{$form->{"unit_old_$i"}}->{"factor"}) &&
|
||
$all_units->{$form->{"unit_old_$i"}}->{"factor"}) {
|
||
$basefactor = $all_units->{$form->{"selected_unit_$i"}}->{"factor"} /
|
||
$all_units->{$form->{"unit_old_$i"}}->{"factor"};
|
||
}
|
||
}
|
||
|
||
if (!$form->{"basefactor_$i"}) {
|
||
$form->{"basefactor_$i"} = 1;
|
||
}
|
||
|
||
my $query =
|
||
qq|SELECT
|
||
0 as pricegroup_id,
|
||
sellprice AS default_sellprice,
|
||
'' AS pricegroup,
|
||
sellprice AS price,
|
||
'selected' AS selected
|
||
FROM parts
|
||
WHERE id = ?
|
||
UNION ALL
|
||
SELECT
|
||
pricegroup_id,
|
||
parts.sellprice AS default_sellprice,
|
||
pricegroup.pricegroup,
|
||
price,
|
||
'' AS selected
|
||
FROM prices
|
||
LEFT JOIN parts ON parts.id = parts_id
|
||
LEFT JOIN pricegroup ON pricegroup.id = pricegroup_id
|
||
WHERE parts_id = ?
|
||
ORDER BY pricegroup|;
|
||
my @values = (conv_i($id), conv_i($id));
|
||
my $pkq = prepare_execute_query($form, $dbh, $query, @values);
|
||
|
||
while (my $pkr = $pkq->fetchrow_hashref('NAME_lc')) {
|
||
$pkr->{id} = $id;
|
||
$pkr->{selected} = '';
|
||
|
||
# if there is an exchange rate change price
|
||
if (($form->{exchangerate} * 1) != 0) {
|
||
$pkr->{price} /= $form->{exchangerate};
|
||
}
|
||
|
||
$pkr->{price} *= $form->{"basefactor_$i"};
|
||
$pkr->{price} *= $basefactor;
|
||
$pkr->{price_ufmt} = $pkr->{price};
|
||
$pkr->{price} = $form->format_amount($myconfig, $pkr->{price}, 5);
|
||
|
||
if (!defined $selectedpricegroup_id) {
|
||
# new entries in article list, either old invoice was loaded (edit) or a new article was added
|
||
# Case A: open old invoice, no pricegroup selected
|
||
# Case B: add new article to invoice, no pricegroup selected
|
||
|
||
# to distinguish case A and B the variable pricegroup_id_$i is used
|
||
# for new articles this variable isn't defined, for loaded articles it is
|
||
# sellprice can't be used, as it already has 0,00 set
|
||
|
||
if ($pkr->{pricegroup_id} eq $form->{"pricegroup_id_$i"} and defined $form->{"pricegroup_id_$i"}) {
|
||
# Case A
|
||
$pkr->{selected} = ' selected';
|
||
} elsif ($pkr->{pricegroup_id} eq $form->{customer_klass}
|
||
and not defined $form->{"pricegroup_id_$i"}
|
||
and $pkr->{price_ufmt} != 0 # only use customer pricegroup price if it has a value, else use default_sellprice
|
||
# for the case where pricegroup prices haven't been set
|
||
) {
|
||
# Case B: use default pricegroup of customer
|
||
|
||
$pkr->{selected} = ' selected'; # unless $form->{selected};
|
||
# no customer pricesgroup set
|
||
if ($pkr->{price_ufmt} == $pkr->{default_sellprice}) {
|
||
|
||
$pkr->{price} = $form->{"sellprice_$i"};
|
||
|
||
} else {
|
||
|
||
# this sub should not set anything and only return. --sschoeling, 20090506
|
||
# is this correct? put in again... -- grichardson 20110119
|
||
$form->{"sellprice_$i"} = $pkr->{price};
|
||
}
|
||
|
||
} elsif ($pkr->{price_ufmt} == $pkr->{default_sellprice} and $pkr->{default_sellprice} != 0) {
|
||
$pkr->{price} = $form->{"sellprice_$i"};
|
||
$pkr->{selected} = ' selected';
|
||
}
|
||
}
|
||
|
||
# existing article: pricegroup or price changed
|
||
if ($selectedpricegroup_id or $selectedpricegroup_id == 0) {
|
||
if ($selectedpricegroup_id ne $pricegroup_old) {
|
||
# pricegroup has changed
|
||
if ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
|
||
$pkr->{selected} = ' selected';
|
||
}
|
||
} elsif ( ($form->parse_amount($myconfig, $price_new)
|
||
!= $form->parse_amount($myconfig, $form->{"sellprice_$i"}))
|
||
and ($price_new ne 0) and defined $price_new) {
|
||
# sellprice has changed
|
||
# when loading existing invoices $price_new is NULL
|
||
if ($pkr->{pricegroup_id} == 0) {
|
||
$pkr->{price} = $form->{"sellprice_$i"};
|
||
$pkr->{selected} = ' selected';
|
||
}
|
||
} elsif ($pkr->{pricegroup_id} eq $selectedpricegroup_id) {
|
||
# neither sellprice nor pricegroup changed
|
||
$pkr->{selected} = ' selected';
|
||
if ( ($pkr->{pricegroup_id} == 0) and ($pkr->{price} == $form->{"sellprice_$i"})) {
|
||
# $pkr->{price} = $form->{"sellprice_$i"};
|
||
} else {
|
||
$pkr->{price} = $form->{"sellprice_$i"};
|
||
}
|
||
}
|
||
}
|
||
push @{ $form->{PRICES}{$i} }, $pkr;
|
||
|
||
}
|
||
$form->{"basefactor_$i"} *= $basefactor;
|
||
|
||
$i++;
|
||
|
||
$pkq->finish;
|
||
}
|
||
|
||
$main::lxdebug->leave_sub();
|
||
}
|
||
|
||
sub has_storno {
|
||
$main::lxdebug->enter_sub();
|
||
|
SL/OE.pm | ||
---|---|---|
sellprice = ?, discount = ?, unit = ?, reqdate = ?, project_id = ?, serialnumber = ?, ship = ?,
|
||
pricegroup_id = ?, ordnumber = ?, transdate = ?, cusordnumber = ?, subtotal = ?,
|
||
marge_percent = ?, marge_total = ?, lastcost = ?, price_factor_id = ?,
|
||
active_price_source = ?,
|
||
price_factor = (SELECT factor FROM price_factors WHERE id = ?), marge_price_factor = ?
|
||
WHERE id = ?
|
||
SQL
|
||
... | ... | |
$form->{"cusordnumber_$i"}, $form->{"subtotal_$i"} ? 't' : 'f',
|
||
$form->{"marge_percent_$i"}, $form->{"marge_absolut_$i"},
|
||
$form->{"lastcost_$i"},
|
||
$form->{"active_price_source_$i"},
|
||
conv_i($form->{"price_factor_id_$i"}), conv_i($form->{"price_factor_id_$i"}),
|
||
conv_i($form->{"marge_price_factor_$i"}),
|
||
conv_i($orderitems_id),
|
||
... | ... | |
o.sellprice, o.parts_id AS id, o.unit, o.discount, p.notes AS partnotes, p.inventory_accno_id AS part_inventory_accno_id,
|
||
o.reqdate, o.project_id, o.serialnumber, o.ship, o.lastcost,
|
||
o.ordnumber, o.transdate, o.cusordnumber, o.subtotal, o.longdescription,
|
||
o.price_factor_id, o.price_factor, o.marge_price_factor,
|
||
o.price_factor_id, o.price_factor, o.marge_price_factor, o.active_price_source,
|
||
pr.projectnumber, p.formel,
|
||
pg.partsgroup, o.pricegroup_id, (SELECT pricegroup FROM pricegroup WHERE id=o.pricegroup_id) as pricegroup
|
||
FROM orderitems o
|
SL/PriceSource.pm | ||
---|---|---|
package SL::PriceSource;
|
||
|
||
use strict;
|
||
use parent 'SL::DB::Object';
|
||
use Rose::Object::MakeMethods::Generic (
|
||
scalar => [ qw(record_item) ],
|
||
);
|
||
|
||
use List::UtilsBy qw(min_by);
|
||
use SL::PriceSource::ALL;
|
||
use SL::PriceSource::Price;
|
||
use SL::Locale::String;
|
||
|
||
sub all_price_sources {
|
||
my ($self) = @_;
|
||
|
||
return map {
|
||
$_->new(record_item => $self->record_item)
|
||
} SL::PriceSource::ALL->all_price_sources
|
||
}
|
||
|
||
sub price_from_source {
|
||
my ($self, $source) = @_;
|
||
my ($source_name, $spec) = split m{/}, $source, 2;
|
||
|
||
my $class = SL::PriceSource::ALL->price_source_class_by_name($source_name);
|
||
|
||
return $class
|
||
? $class->new(record_item => $self->record_item)->price_from_source($source, $spec)
|
||
: empty_price();
|
||
}
|
||
|
||
sub available_prices {
|
||
map { $_->available_prices } $_[0]->all_price_sources;
|
||
}
|
||
|
||
sub best_price {
|
||
min_by { $_->price } map { $_->best_price } $_[0]->all_price_sources;
|
||
}
|
||
|
||
sub empty_price {
|
||
SL::PriceSource::Price->new(
|
||
source => '',
|
||
description => t8('None (PriceSource)'),
|
||
);
|
||
}
|
||
|
||
1;
|
||
|
||
__END__
|
||
|
||
=encoding utf-8
|
||
|
||
=head1 NAME
|
||
|
||
SL::PriceSource - mixin for price_sources in record items
|
||
|
||
=head1 SYNOPSIS
|
||
|
||
# in record item class
|
||
|
||
use SL::PriceSource;
|
||
|
||
# later on:
|
||
|
||
$record_item->all_price_sources
|
||
$record_item->price_source # get
|
||
$record_item->price_source($c) # set
|
||
|
||
$record_item->update_price_source # set price to calculated
|
||
|
||
=head1 DESCRIPTION
|
||
|
||
This mixin provides a way to use price_source objects from within a record item.
|
||
Record items in this contest mean OrderItems, InvoiceItems and
|
||
DeliveryOrderItems.
|
||
|
||
=head1 FUNCTIONS
|
||
|
||
price_sources
|
||
|
||
returns a list of price_source objects which are created with the current record
|
||
item.
|
||
|
||
active_price_source
|
||
|
||
returns the object representing the currently chosen price_source method or
|
||
undef if custom price is chosen. Note that this must not necessarily be the
|
||
active price, if something affecting the price_source has changed, the price
|
||
calculated can differ from the price in the record. It is the responsibility of
|
||
the implementing code to decide what to do in this case.
|
||
|
||
=head1 BUGS
|
||
|
||
None yet. :)
|
||
|
||
=head1 AUTHOR
|
||
|
||
Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>
|
||
|
||
=cut
|
SL/PriceSource/ALL.pm | ||
---|---|---|
package SL::PriceSource::ALL;
|
||
|
||
use strict;
|
||
use SL::PriceSource::Pricegroup;
|
||
use SL::PriceSource::MasterData;
|
||
|
||
my %price_sources_by_name = (
|
||
master_data => 'SL::PriceSource::MasterData',
|
||
pricegroup => 'SL::PriceSource::Pricegroup',
|
||
);
|
||
|
||
my @price_sources_order = qw(
|
||
master_data
|
||
pricegroup
|
||
);
|
||
|
||
sub all_price_sources {
|
||
map { $price_sources_by_name{$_} } @price_sources_order;
|
||
}
|
||
|
||
sub price_source_class_by_name {
|
||
$price_sources_by_name{$_[1]};
|
||
}
|
||
|
||
1;
|
SL/PriceSource/Base.pm | ||
---|---|---|
package SL::PriceSource::Base;
|
||
|
||
use strict;
|
||
|
||
use parent qw(SL::DB::Object);
|
||
use Rose::Object::MakeMethods::Generic (
|
||
scalar => [ qw(record_item record) ],
|
||
);
|
||
|
||
sub name { die 'name needs to be implemented' }
|
||
|
||
sub description { die 'description needs to be implemented' }
|
||
|
||
sub available_prices { die 'available_prices needs to be implemented' }
|
||
|
||
sub best_price { die 'best_price needs to be implemented' }
|
||
|
||
sub price_from_source { die 'price_from_source needs to be implemented:' . "@_" }
|
||
|
||
sub part {
|
||
$_[0]->record_item->part;
|
||
}
|
||
|
||
1;
|
||
|
||
__END__
|
||
|
||
=encoding utf-8
|
||
|
||
=head1 NAME
|
||
|
||
SL::PriceSource::Base - <oneliner description>
|
||
|
||
=head1 SYNOPSIS
|
||
|
||
# in consuming module
|
||
# TODO: thats bullshit, theres no need to have this pollute the namespace
|
||
# make a manager that handles this
|
||
|
||
my @list_of_price_sources = $record_item->price_sources;
|
||
for (@list_of_price_sources) {
|
||
my $internal_name = $_->name;
|
||
my $translated_name = $_->description;
|
||
my $price = $_->price;
|
||
}
|
||
|
||
$record_item->set_active_price_source($price_source) # equivalent to:
|
||
$record_item->active_price_source($price_source->name);
|
||
$record_item->sellprice($price_source->price);
|
||
|
||
# for finer control
|
||
$price_source->needed_params
|
||
$price_source->supported_params
|
||
|
||
=head1 DESCRIPTION
|
||
|
||
PriceSource is an interface that allows generic algorithms to be used, to
|
||
calculate a price for a position in a record.
|
||
|
||
If any such price_source algorithm is known to the system, a user can chose
|
||
which of them should be used to claculate the price displayed in the record.
|
||
|
||
The algorithm is saved togetherwith the target price, so that changes in the
|
||
record can recalculate the price accordingly, and otherwise manual changes to
|
||
the price can reset the price_source used to custom (aka no price_source).
|
||
|
||
=head1 INTERFACE METHODS
|
||
|
||
=over 4
|
||
|
||
=item C<name>
|
||
|
||
Should return a unique internal name. Should be entered in
|
||
L<SL::PriceSource::ALL> so that a name_to_class lookup works.
|
||
|
||
=item C<description>
|
||
|
||
Should return a translated name.
|
||
|
||
=item C<needed_params>
|
||
|
||
Should return a list of elements that a record_item NEEDS to be used with this calulation.
|
||
|
||
Both C<needed_params> nad C<supported_params> are purely informational at this point.
|
||
|
||
=item C<supported_params>
|
||
|
||
Should return a list of elements that a record_item MAY HAVE to be used with this calulation.
|
||
|
||
Both C<needed_params> nad C<supported_params> are purely informational at this point.
|
||
|
||
=item C<price>
|
||
|
||
Calculate a price and return. Do not mutate the record_item. Should will return
|
||
undef if price is not applicable to the current record_item.
|
||
|
||
=back
|
||
|
||
=head1 BUGS
|
||
|
||
None yet. :)
|
||
|
||
=head1 AUTHOR
|
||
|
||
Sven Schoeling E<lt>s.schoeling@linet-services.deE<gt>
|
||
|
||
=cut
|
SL/PriceSource/MasterData.pm | ||
---|---|---|
package SL::PriceSource::MasterData;
|
||
|
||
use strict;
|
||
use parent qw(SL::PriceSource::Base);
|
||
|
||
use SL::PriceSource::Price;
|
||
use SL::Locale::String;
|
||
|
||
sub name { 'master_data' }
|
||
|
||
sub description { t8('Master Data') }
|
||
|
||
sub available_prices {
|
||
my ($self, %params) = @_;
|
||
|
||
my $part = $self->part;
|
||
|
||
return () unless $part;
|
||
|
||
# TODO: sellprice only in sales, lastcost in purchase
|
||
return $self->make_sellprice($part);
|
||
}
|
||
|
||
sub price_from_source {
|
||
my ($self, $source, $spec) = @_;
|
||
|
||
if ($spec eq 'sellprice') {
|
||
return $self->make_sellprice($self->part);
|
||
}
|
||
}
|
||
|
||
sub make_sellprice {
|
||
my ($self, $part) = @_;
|
||
|
||
return SL::PriceSource::Price->new(
|
||
price => $part->sellprice,
|
||
source => 'master_data/sellprice',
|
||
description => t8('Sellprice'),
|
||
price_source => $self,
|
||
);
|
||
}
|
||
|
||
1;
|
SL/PriceSource/Price.pm | ||
---|---|---|
package SL::PriceSource::Price;
|
||
|
||
use strict;
|
||
|
||
use parent 'SL::DB::Object';
|
||
use Rose::Object::MakeMethods::Generic (
|
||
scalar => [ qw(price description source price_source) ],
|
||
array => [ qw(depends_on) ]
|
||
);
|
||
|
||
sub full_description {
|
||
my ($self) = @_;
|
||
|
||
$self->price_source
|
||
? $self->price_source->description . ': ' . $self->description
|
||
: $self->description
|
||
}
|
||
|
||
1;
|
SL/PriceSource/Pricegroup.pm | ||
---|---|---|
package SL::PriceSource::Pricegroup;
|
||
|
||
use strict;
|
||
use parent qw(SL::PriceSource::Base);
|
||
|
||
use SL::PriceSource::Price;
|
||
use SL::Locale::String;
|
||
|
||
sub name { 'pricegroup' }
|
||
|
||
sub description { t8('Pricegroup') }
|
||
|
||
sub available_prices {
|
||
my ($self, %params) = @_;
|
||
|
||
my $item = $self->record_item;
|
||
|
||
my $prices = SL::DB::Manager::Price->get_all(
|
||
query => [ parts_id => $item->parts_id, price => { gt => 0 } ],
|
||
with_objects => 'pricegroup',
|
||
order_by => 'pricegroun.id',
|
||
);
|
||
|
||
return () unless @$prices;
|
||
|
||
return map {
|
||
$self->make_price($_);
|
||
} @$prices;
|
||
}
|
||
|
||
sub price_from_source {
|
||
my ($self, $source, $spec) = @_;
|
||
|
||
my $price = SL::DB::Manager::Price->find_by(id => $spec);
|
||
|
||
return $self->make_price($price);
|
||
}
|
||
|
||
sub make_price {
|
||
my ($self, $price_obj) = @_;
|
||
|
||
SL::PriceSource::Price->new(
|
||
price => $price_obj->price,
|
||
source => 'pricegroup/' . $price_obj->id,
|
||
description => $price_obj->pricegroup->pricegroup,
|
||
price_source => $self,
|
||
)
|
||
}
|
||
|
||
1;
|
bin/mozilla/do.pl | ||
---|---|---|
# Kunde mit Rabatt 20 -> Rabatt 5,5 i.O.
|
||
$form->{payment_id} = $payment_id if $form->{payment_id} eq "";
|
||
|
||
# for pricegroups
|
||
my $i = $form->{rowcount};
|
||
|
||
if ( ($form->{"partnumber_$i"} eq "")
|
||
... | ... | |
$form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"} * (1 - $form->{tradediscount}));
|
||
$form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"});
|
||
$form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"});
|
||
|
||
# get pricegroups for parts
|
||
IS->get_pricegroups_for_parts(\%myconfig, \%$form);
|
||
|
||
# build up html code for prices_$i
|
||
&set_pricegroup($i);
|
||
}
|
||
|
||
display_form();
|
||
... | ... | |
|
||
}
|
||
|
||
# show pricegroup in newly loaded invoice when creating invoice from delivery order
|
||
for my $i (1 .. $form->{rowcount}) {
|
||
$form->{"sellprice_pg_$i"} = join '--', $form->{"sellprice_$i"}, $form->{"pricegroup_id_$i"};
|
||
}
|
||
IS->get_pricegroups_for_parts(\%myconfig, \%$form);
|
||
set_pricegroup($form->{rowcount});
|
||
|
||
display_form();
|
||
|
||
$main::lxdebug->leave_sub();
|
||
... | ... | |
invoice_links();
|
||
prepare_invoice();
|
||
|
||
# show pricegroup in newly loaded invoice when creating invoice from delivery order
|
||
for my $i (1 .. $form->{rowcount}) {
|
||
$form->{"sellprice_pg_$i"} = join '--', $form->{"sellprice_$i"}, $form->{"pricegroup_id_$i"};
|
||
}
|
||
IS->get_pricegroups_for_parts(\%myconfig, \%$form);
|
||
set_pricegroup($_) for 1 .. $form->{rowcount};
|
||
|
||
display_form();
|
||
|
||
$main::lxdebug->leave_sub();
|
bin/mozilla/invoice_io.pl | ||
---|---|---|
use SL::AM;
|
||
use Data::Dumper;
|
||
|
||
sub set_pricegroup {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $form = $main::form;
|
||
my %myconfig = %main::myconfig;
|
||
my $locale = $main::locale;
|
||
|
||
my $rowcount = shift;
|
||
for my $j (1 .. $rowcount) {
|
||
my $pricegroup_old = $form->{"pricegroup_old_$j"};
|
||
if ($form->{PRICES}{$j}) {
|
||
my $len = 0;
|
||
my $prices = '<option value="--">' . $locale->text("none (pricegroup)") . '</option>';
|
||
my $price = 0;
|
||
foreach my $item (@{ $form->{PRICES}{$j} }) {
|
||
|
||
#$price = $form->round_amount($myconfig, $item->{price}, 5);
|
||
#$price = $form->format_amount($myconfig, $item->{price}, 2);
|
||
my $price = $item->{price};
|
||
my $pricegroup_id = $item->{pricegroup_id};
|
||
my $pricegroup = $item->{pricegroup};
|
||
|
||
# build drop down list for pricegroups
|
||
$prices .=
|
||
qq|<option value="$price--$pricegroup_id"$item->{selected}>$pricegroup</option>\n|;
|
||
|
||
$len += 1;
|
||
|
||
# map {
|
||
# $form->{"${_}_$j"} =
|
||
# $form->format_amount(\%myconfig, $form->{"${_}_$j"})
|
||
# } qw(sellprice price_new price_old);
|
||
|
||
# set new selectedpricegroup_id and prices for "Preis"
|
||
if ($item->{selected} && ($pricegroup_id != 0)) {
|
||
$form->{"pricegroup_old_$j"} = $pricegroup_id;
|
||
$form->{"price_new_$j"} = $price;
|
||
# edit: don't change the sellprice here
|
||
# $form->{"sellprice_$j"} = $price; # this must only be updated for existing articles, not new ones
|
||
}
|
||
if ($pricegroup_id == 0) {
|
||
$form->{"price_new_$j"} = $form->{"sellprice_$j"};
|
||
}
|
||
}
|
||
$form->{"prices_$j"} = $prices;
|
||
}
|
||
}
|
||
$main::lxdebug->leave_sub();
|
||
}
|
||
|
||
sub display_form {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
... | ... | |
# $form->{rowcount}--;
|
||
# my $rowcount = $form->{rowcount};
|
||
#
|
||
# # get pricegroups for parts
|
||
# IS->get_pricegroups_for_parts(\%myconfig, \%$form);
|
||
#
|
||
# # build up html code for prices_$i
|
||
# set_pricegroup($rowcount);
|
||
#
|
||
# $form->{resubmit} = 1;
|
||
#
|
||
# }
|
bin/mozilla/io.pl | ||
---|---|---|
|
||
use Carp;
|
||
use CGI;
|
||
use List::MoreUtils qw(any uniq);
|
||
use List::MoreUtils qw(any uniq apply);
|
||
use List::Util qw(min max first);
|
||
|
||
use SL::CVar;
|
||
... | ... | |
use SL::CT;
|
||
use SL::IC;
|
||
use SL::IO;
|
||
use SL::PriceSource;
|
||
|
||
use SL::DB::Customer;
|
||
use SL::DB::Default;
|
||
... | ... | |
}
|
||
|
||
# column_index
|
||
my @header_sort = qw(runningnumber partnumber description ship qty unit weight sellprice_pg sellprice discount linetotal);
|
||
my @header_sort = qw(runningnumber partnumber description ship qty unit weight sellprice discount linetotal);
|
||
my @HEADER = (
|
||
{ id => 'runningnumber', width => 5, value => $locale->text('No.'), display => 1, },
|
||
{ id => 'partnumber', width => 8, value => $locale->text('Number'), display => 1, },
|
||
... | ... | |
{ id => 'serialnr', width => 10, value => $locale->text('Serial No.'), display => 0, },
|
||
{ id => 'projectnr', width => 10, value => $locale->text('Project'), display => 0, },
|
||
{ id => 'sellprice', width => 15, value => $locale->text('Price'), display => !$is_delivery_order, },
|
||
{ id => 'sellprice_pg', width => 8, value => $locale->text('Pricegroup'), display => !$is_delivery_order && !$is_purchase, },
|
||
{ id => 'price_source', width => 5, value => $locale->text('Price Source'), display => !$is_delivery_order, },
|
||
{ id => 'discount', width => 5, value => $locale->text('Discount'), display => !$is_delivery_order, },
|
||
{ id => 'linetotal', width => 10, value => $locale->text('Extended'), display => !$is_delivery_order, },
|
||
{ id => 'bin', width => 10, value => $locale->text('Bin'), display => 0, },
|
||
... | ... | |
my $deliverydate = $locale->text('Required by');
|
||
|
||
# special alignings
|
||
my %align = map { $_ => 'right' } qw(qty ship right sellprice_pg discount linetotal stock_in_out weight);
|
||
my %align = map { $_ => 'right' } qw(qty ship right discount linetotal stock_in_out weight);
|
||
my %nowrap = map { $_ => 1 } qw(description unit);
|
||
|
||
$form->{marge_total} = 0;
|
||
... | ... | |
$form->{"sellprice_$i"} = $form->{"price_new_$i"};
|
||
}
|
||
|
||
my $record_item = _make_record_item($i);
|
||
|
||
# unit begin
|
||
$form->{"unit_old_$i"} ||= $form->{"unit_$i"};
|
||
$form->{"selected_unit_$i"} ||= $form->{"unit_$i"};
|
||
... | ... | |
|| !AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units)) { # (z.B. Dimensionseinheit war ausgewaehlt, es handelt sich aber
|
||
$form->{"unit_old_$i"} = $form->{"selected_unit_$i"} = $form->{"unit_$i"}; # um eine Dienstleistung). Dann keinerlei Umrechnung vornehmen.
|
||
}
|
||
# adjust prices by unit, ignore if pricegroup changed
|
||
if ((!$form->{"prices_$i"}) || ($form->{"new_pricegroup_$i"} == $form->{"old_pricegroup_$i"})) {
|
||
$form->{"sellprice_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1;
|
||
$form->{"lastcost_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1;
|
||
$form->{"unit_old_$i"} = $form->{"selected_unit_$i"};
|
||
}
|
||
|
||
$form->{"sellprice_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1;
|
||
$form->{"lastcost_$i"} *= AM->convert_unit($form->{"selected_unit_$i"}, $form->{"unit_old_$i"}, $all_units) || 1;
|
||
$form->{"unit_old_$i"} = $form->{"selected_unit_$i"};
|
||
|
||
my $this_unit = $form->{"unit_$i"};
|
||
$this_unit = $form->{"selected_unit_$i"} if AM->convert_unit($this_unit, $form->{"selected_unit_$i"}, $all_units);
|
||
|
||
... | ... | |
$column_data{ship} = $form->format_amount(\%myconfig, $form->round_amount($ship_qty, 2) * 1) . ' ' . $form->{"unit_$i"};
|
||
}
|
||
|
||
# build in drop down list for pricesgroups
|
||
# $sellprice_value setzt den Wert etwas unabhängiger von der Darstellung.
|
||
# Hintergrund: Preisgruppen werden hier überprüft und neu berechnet.
|
||
# Vorher wurde der ganze cgi->textfield Block zweimal identisch eingebaut, dass passiert
|
||
# jetzt nach der Abfrage.
|
||
my $sellprice_value;
|
||
if ($form->{"prices_$i"}) {
|
||
$column_data{sellprice_pg} = qq|<select name="sellprice_pg_$i" style="width: 8em">$form->{"prices_$i"}</select>|;
|
||
$sellprice_value =($form->{"new_pricegroup_$i"} != $form->{"old_pricegroup_$i"})
|
||
? $form->format_amount(\%myconfig, $form->{"price_new_$i"}, $decimalplaces)
|
||
: $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
|
||
} else {
|
||
# for last row and report
|
||
# set pricegroup drop down list from report menu
|
||
if ($form->{"sellprice_$i"} != 0) {
|
||
# remember the pricegroup_id in pricegroup_old
|
||
# but don't overwrite it
|
||
$form->{"pricegroup_old_$i"} = $form->{"pricegroup_id_$i"};
|
||
my $default_option = $form->{"sellprice_$i"}.'--'.$form->{"pricegroup_id_$i"};
|
||
$column_data{sellprice_pg} = NTI($cgi->popup_menu("sellprice_pg_$i", [ $default_option ], $default_option, { $default_option => $form->{"pricegroup_$i"} || '' }));
|
||
} else {
|
||
$column_data{sellprice_pg} = qq| |;
|
||
}
|
||
$sellprice_value = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
|
||
|
||
}
|
||
# Falls der Benutzer die Preise nicht anpassen sollte, wird das entsprechende
|
||
# Textfield auf readonly gesetzt. Anm. von Sven: Manipulation der Preise ist
|
||
# immer noch möglich, konsequenterweise sollten diese NUR aus der Datenbank
|
||
# geholt werden.
|
||
my $edit_prices = $main::auth->assert('edit_prices', 1);
|
||
$column_data{sellprice} = (!$edit_prices)
|
||
? $cgi->textfield(-readonly => "readonly",
|
||
-name => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value => $sellprice_value)
|
||
: $cgi->textfield(-name => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value => $sellprice_value);
|
||
my $sellprice_value = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
|
||
my $edit_prices = $main::auth->assert('edit_prices', 1) && !$::form->{"active_price_source_$i"};
|
||
$column_data{sellprice} = (!$edit_prices)
|
||
? $cgi->hidden( -name => "sellprice_$i", -id => "sellprice_$i", -value => $sellprice_value) . $sellprice_value
|
||
: $cgi->textfield(-name => "sellprice_$i", -id => "sellprice_$i", -size => 10, -onBlur => "check_right_number_format(this)", -value => $sellprice_value);
|
||
$column_data{discount} = (!$edit_prices)
|
||
? $cgi->textfield(-readonly => "readonly",
|
||
-name => "discount_$i", -size => 3, -value => $form->format_amount(\%myconfig, $form->{"discount_$i"}))
|
||
... | ... | |
|
||
$column_data{weight} = $form->format_amount(\%myconfig, $form->{"qty_$i"} * $form->{"weight_$i"}, 3) . ' ' . $defaults->{weightunit} if $defaults->{show_weight};
|
||
|
||
if ($form->{"id_${i}"}) {
|
||
my $price_source = SL::PriceSource->new(record_item => $record_item);
|
||
my $price = $price_source->price_from_source($::form->{"active_price_source_$i"});
|
||
$::form->{price_sources}[$i] = $price_source;
|
||
$column_data{price_source} .= $cgi->button(-value => $price->full_description, -onClick => "toggle_price_source($i)");
|
||
}
|
||
|
||
if ($is_delivery_order) {
|
||
$column_data{stock_in_out} = calculate_stock_in_out($i);
|
||
}
|
||
... | ... | |
|
||
if ($is_delivery_order) {
|
||
map { $form->{"${_}_${i}"} = $form->format_amount(\%myconfig, $form->{"${_}_${i}"}) } qw(sellprice discount lastcost);
|
||
$form->{"pricegroup_id_$i"} = $form->{"pricegroup_old_$i"} if $form->{"pricegroup_old_$i"};
|
||
$form->{"sellprice_pg_$i"} = $form->{"hidden_prices_$i"} if $form->{"hidden_prices_$i"};
|
||
push @hidden_vars, grep { defined $form->{"${_}_${i}"} } qw(sellprice discount not_discountable price_factor_id lastcost pricegroup_id sellprice_pg);
|
||
push @hidden_vars, grep { defined $form->{"${_}_${i}"} } qw(sellprice discount not_discountable price_factor_id lastcost);
|
||
push @hidden_vars, "stock_${stock_in_out}_sum_qty", "stock_${stock_in_out}";
|
||
}
|
||
|
||
... | ... | |
$cgi->hidden("-name" => "unit_old_$i", "-value" => $form->{"selected_unit_$i"}),
|
||
$cgi->hidden("-name" => "price_new_$i", "-value" => $form->format_amount(\%myconfig, $form->{"price_new_$i"})),
|
||
map { ($cgi->hidden("-name" => $_, "-id" => $_, "-value" => $form->{$_})); } map { $_."_$i" }
|
||
(qw(orderitems_id bo pricegroup_old price_old id inventory_accno bin partsgroup partnotes
|
||
(qw(orderitems_id bo price_old id inventory_accno bin partsgroup partnotes active_price_source
|
||
income_accno expense_accno listprice assembly taxaccounts ordnumber donumber transdate cusordnumber
|
||
longdescription basefactor marge_absolut marge_percent marge_price_factor weight), @hidden_vars)
|
||
);
|
||
... | ... | |
# Benutzerdefinierte Variablen für Waren/Dienstleistungen/Erzeugnisse
|
||
_render_custom_variables_inputs(ROW2 => \@ROW2, row => $i, part_id => $form->{"id_$i"});
|
||
|
||
push @ROWS, { ROW1 => \@ROW1, ROW2 => \@ROW2, HIDDENS => \@HIDDENS, colspan => $colspan, error => $form->{"row_error_$i"}, };
|
||
push @ROWS, { ROW1 => \@ROW1, ROW2 => \@ROW2, HIDDENS => \@HIDDENS, colspan => $colspan, error => $form->{"row_error_$i"}, obj => $record_item };
|
||
}
|
||
|
||
$form->{totalweight} = $totalweight;
|
||
... | ... | |
$main::lxdebug->leave_sub();
|
||
}
|
||
|
||
##################################################
|
||
# build html-code for pricegroups in variable $form->{prices_$j}
|
||
|
||
sub set_pricegroup {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
my $form = $main::form;
|
||
my $locale = $main::locale;
|
||
my $cgi = $::request->{cgi};
|
||
|
||
_check_io_auth();
|
||
|
||
my $rowcount = shift;
|
||
for my $j (1 .. $rowcount) {
|
||
next unless $form->{PRICES}{$j};
|
||
# build drop down list for pricegroups
|
||
my $option_tmpl = qq|<option value="%s--%s" %s>%s</option>|;
|
||
$form->{"prices_$j"} = join '', map { sprintf $option_tmpl, @$_{qw(price pricegroup_id selected pricegroup)} }
|
||
(+{ pricegroup => $locale->text("none (pricegroup)") }, @{ $form->{PRICES}{$j} });
|
||
|
||
foreach my $item (@{ $form->{PRICES}{$j} }) {
|
||
# set new selectedpricegroup_id and prices for "Preis"
|
||
$form->{"pricegroup_old_$j"} = $item->{pricegroup_id} if $item->{selected} && $item->{pricegroup_id};
|
||
$form->{"sellprice_$j"} = $item->{price} if $item->{selected} && $item->{pricegroup_id};
|
||
$form->{"price_new_$j"} = $form->{"sellprice_$j"} if $item->{selected} || !$item->{pricegroup_id};
|
||
}
|
||
|
||
# save hidden pricegroups for delivery_orders
|
||
next unless my @selected_prices = grep { $_->{selected} } @{ $form->{PRICES}{$j} };
|
||
$form->{"hidden_prices_$j"} = $selected_prices[-1]{price} . "--" . $selected_prices[-1]{pricegroup_id};
|
||
}
|
||
$main::lxdebug->leave_sub();
|
||
}
|
||
|
||
sub select_item {
|
||
$main::lxdebug->enter_sub();
|
||
|
||
... | ... | |
$form->format_amount(\%myconfig, $form->{"${_}_$i"}, $decimalplaces)
|
||
} qw(sellprice listprice lastcost) if $form->{item} ne 'assembly';
|
||
|
||
# get pricegroups for parts
|
||
IS->get_pricegroups_for_parts(\%myconfig, \%$form);
|
||
|
||
# build up html code for prices_$i
|
||
set_pricegroup($form->{rowcount});
|
||
|
||
&display_form;
|
||
|
||
$main::lxdebug->leave_sub();
|
||
... | ... | |
or (($form->{level} eq undef) and ($form->{type} =~ /invoice/))
|
||
or ($form->{type} =~ /sales_order/)) {
|
||
|
||
# get pricegroups for parts
|
||
IS->get_pricegroups_for_parts(\%myconfig, \%$form);
|
||
|
||
# build up html code for prices_$i
|
||
set_pricegroup($form->{rowcount});
|
||
|
||
}
|
||
|
||
&display_form;
|
||
... | ... | |
taxaccounts bin assembly weight projectnumber project_id
|
||
oldprojectnumber runningnumber serialnumber partsgroup payment_id
|
||
not_discountable shop ve gv buchungsgruppen_id language_values
|
||
sellprice_pg pricegroup_old price_old price_new unit_old ordnumber donumber
|
||
price_old price_new unit_old ordnumber donumber
|
||
transdate longdescription basefactor marge_total marge_percent
|
||
marge_price_factor lastcost price_factor_id partnotes
|
||
stock_out stock_in has_sernumber reqdate orderitems_id);
|
||
... | ... | |
# get details for customer/vendor
|
||
call_sub($::form->{vc} . "_details", qw(name department_1 department_2 street zipcode city country contact email phone fax), $::form->{vc} . "number");
|
||
|
||
# get pricegroups for parts
|
||
IS->get_pricegroups_for_parts(\%::myconfig, \%$::form);
|
||
|
||
# build up html code for prices_$i
|
||
set_pricegroup($::form->{rowcount});
|
||
|
||
$::form->{rowcount}--;
|
||
|
||
my @shipto_vars = qw(shiptoname shiptostreet shiptozipcode shiptocity shiptocountry
|
||
... | ... | |
$::form->redo_rows(\@fields, \@new_rows, scalar(@new_rows), $::form->{rowcount});
|
||
$::form->{rowcount} -= $removed_rows;
|
||
}
|
||
|
||
sub _make_record_item {
|
||
my ($row) = @_;
|
||
|
||
my $class = {
|
||
sales_order => 'OrderItem',
|
||
purchase_oder => 'OrderItem',
|
||
sales_quotation => 'OrderItem',
|
||
request_quotation => 'OrderItem',
|
||
invoice => 'InvoiceItem',
|
||
purchase_invoice => 'InvoiceItem',
|
||
purchase_delivery_order => 'DeliveryOrderItem',
|
||
sales_delivery_order => 'DeliveryOrderItem',
|
||
}->{$::form->{type}};
|
||
|
||
return unless $class;
|
||
|
||
$class = 'SL::DB::' . $class;
|
||
|
||
eval "require $class";
|
||
|
||
my $obj = $::form->{"orderitems_id_$row"}
|
||
? $class->meta->convention_manager->auto_manager_class_name->find_by(id => $::form->{"orderitems_id_$row"})
|
||
: $class->new;
|
||
|
||
for my $method (apply { s/_$row$// } grep { /_$row$/ } keys %$::form) {
|
||
next unless $obj->meta->column($method);
|
||
if ($obj->meta->column($method)->isa('Rose::DB::Object::Metadata::Column::Date')) {
|
||
$obj->${\"$method\_as_date"}($::form->{"$method\_$row"});
|
||
} else {
|
||
$obj->$method($::form->{"$method\_$row"});
|
||
}
|
||
}
|
||
|
||
if ($::form->{"id_$row"}) {
|
||
$obj->part(SL::DB::Part->load_cached($::form->{"id_$row"}));
|
||
}
|
||
|
||
return $obj;
|
||
}
|
bin/mozilla/is.pl | ||
---|---|---|
$form->{rowcount} = $i;
|
||
|
||
}
|
||
|
||
# get pricegroups for parts
|
||
IS->get_pricegroups_for_parts(\%myconfig, \%$form);
|
||
|
||
# Problem: set_pricegroup resets the sellprice of old invoices to the price
|
||
# currently defined in the pricegroup, which is a problem if the price has
|
||
# changed, as the old invoice gets the new price
|
||
# set_pricegroup must never be called, when an old invoice is initially loaded
|
||
|
||
# set_pricegroup($_) for 1 .. $form->{rowcount};
|
||
}
|
||
$main::lxdebug->leave_sub();
|
||
}
|
||
... | ... | |
map { $form->{"${_}_$i"} = $form->format_amount(\%myconfig, $form->{"${_}_$i"}, $decimalplaces) } qw(sellprice lastcost);
|
||
|
||
$form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"});
|
||
|
||
# get pricegroups for parts
|
||
IS->get_pricegroups_for_parts(\%myconfig, \%$form);
|
||
|
||
# build up html code for prices_$i
|
||
&set_pricegroup($i);
|
||
}
|
||
|
||
&display_form;
|
||
... | ... | |
$form->{forex} = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, 'buy');
|
||
$form->{exchangerate} = $form->{forex} if $form->{forex};
|
||
|
||
# remember pricegroups for "use as new"
|
||
IS->get_pricegroups_for_parts(\%myconfig, \%$form);
|
||
set_pricegroup($_) for 1 .. $form->{rowcount};
|
||
|
||
&display_form;
|
||
|
||
$main::lxdebug->leave_sub();
|
bin/mozilla/oe.pl | ||
---|---|---|
|
||
check_oe_access();
|
||
|
||
my $order = _make_record();
|
||
|
||
set_headings($form->{"id"} ? "edit" : "add");
|
||
|
||
$form->{update} = 1;
|
||
... | ... | |
$form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
|
||
$form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}, $decimalplaces);
|
||
$form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
|
||
|
||
# get pricegroups for parts
|
||
IS->get_pricegroups_for_parts(\%myconfig, \%$form);
|
||
|
||
# build up html code for prices_$i
|
||
&set_pricegroup($i);
|
||
}
|
||
|
||
display_form();
|
||
... | ... | |
$form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
|
||
}
|
||
|
||
# show pricegroup in newly loaded invoice when creating invoice from quotation/order
|
||
IS->get_pricegroups_for_parts(\%myconfig, \%$form);
|
||
set_pricegroup($_) for 1 .. $form->{rowcount};
|
||
|
||
&display_form;
|
||
|
||
$main::lxdebug->leave_sub();
|
||
... | ... | |
|
||
$::form->error($::locale->text('No action defined.'));
|
||
}
|
||
|
||
sub _make_record {
|
||
my $obj = SL::DB::Order->new;
|
||
|
||
for my $method (keys %$::form) {
|
||
next unless $obj->can($method);
|
||
next unless $obj->meta->column($method);
|
||
|
||
if ($obj->meta->column($method)->isa('Rose::DB::Object::Metadata::Column::Date')) {
|
||
$obj->${\"$method\_as_date"}($::form->{$method});
|
||
} elsif ((ref $obj->meta->column($method)) =~ /^Rose::DB::Object::Metadata::Column::(?:Integer|Numeric|Float|DoublePrecsion)$/) {
|
||
$obj->$method($::form->{$method});
|
||
}
|
||
}
|
||
|
||
my @items;
|
||
for my $i (1 .. $::form->{rowcount}) {
|
||
next unless $::form->{"id_$i"};
|
||
push @items, _make_record_item($i)
|
||
}
|
||
|
||
$obj->orderitems(@items);
|
||
|
||
return $obj;
|
||
}
|
||
|
locale/de/all | ||
---|---|---|
'No.' => 'Position',
|
||
'No/individual shipping address' => 'Keine/individuelle Lieferadresse',
|
||
'None' => 'Kein',
|
||
'None (PriceSource)' => 'Freier Preis',
|
||
'Normal users cannot log in.' => 'Normale Benutzer können sich nicht anmelden.',
|
||
'Normalize Customer / Vendor names' => 'Normalisierung Kunden- / Lieferantennamen',
|
||
'Normalize part description and part notes' => 'Normalisierung Artikelbeschreibung und Artikellangtext (Bemerkung)',
|
||
... | ... | |
'Price' => 'Preis',
|
||
'Price Factor' => 'Preisfaktor',
|
||
'Price Factors' => 'Preisfaktoren',
|
||
'Price Source' => 'Preisquelle',
|
||
'Price factor (database ID)' => 'Preisfaktor (Datenbank-ID)',
|
||
'Price factor (name)' => 'Preisfaktor (Name)',
|
||
'Price factor deleted!' => 'Preisfaktor gelöscht.',
|
||
... | ... | |
'no article assigned yet' => 'noch kein Artikel zugewiesen',
|
||
'no bestbefore' => 'keine Mindesthaltbarkeit',
|
||
'no chargenumber' => 'keine Chargennummer',
|
||
'none (pricegroup)' => 'keine',
|
||
'not configured' => 'nicht konfiguriert',
|
||
'not delivered' => 'nicht geliefert',
|
||
'not executed' => 'nicht ausgeführt',
|
sql/Pg-upgrade2/recorditem_active_price_source.sql | ||
---|---|---|
-- @tag: recorditem_active_price_source
|
||
-- @description: Preisquelle in Belegpositionen
|
||
-- @depends: release_2_6_2
|
||
-- @encoding: utf-8
|
||
|
||
ALTER TABLE orderitems ADD COLUMN active_price_source TEXT NOT NULL DEFAULT '';
|
||
ALTER TABLE delivery_order_items ADD COLUMN active_price_source TEXT NOT NULL DEFAULT '';
|
||
ALTER TABLE invoice ADD COLUMN active_price_source TEXT NOT NULL DEFAULT '';
|
templates/webpages/oe/_price_sources_row.html | ||
---|---|---|
[%- USE T8 %]
|
||
[%- USE HTML %]
|
||
[%- USE L %]
|
||
[%- USE LxERP %]
|
||
<tr class="listrow[% i % 2 %]" id="row[% i %]_3" style='display:none'>
|
||
<td colspan="[% row.colspan %]">
|
||
<span class="[% IF !row.obj.active_price_source %]bold[% END %]">
|
||
[% L.radio_button_tag('active_price_source_' _ i, label=LxERP.t8('None (PriceSource)'), checked=!row.obj.active_price_source, value='', onChange='update_price_source(' _ i _ ', \'\')') %]
|
||
</span>
|
||
[%- FOREACH price IN price_sources.$i.available_prices %]
|
||
<div class="[% IF price.source == row.obj.active_price_source %]bold[% END %]">
|
||
[% L.radio_button_tag('active_price_source_' _ i, value=price.source, checked=price.source == row.obj.active_price_source, label=LxERP.format_amount(price.price, 2) _ ' (' _ price.full_description _ ')', onChange='update_price_source(' _ i _ ', \'' _ price.source _ '\', \'' _ LxERP.format_amount(price.price, -2) _ '\')' ) %]
|
||
</div>
|
||
[%- END %]
|
||
</td>
|
||
</tr>
|
templates/webpages/oe/sales_order.html | ||
---|---|---|
[%- USE T8 %]
|
||
[%- USE HTML %]
|
||
|
||
[%- USE L %]
|
||
[%- USE LxERP %]
|
||
[%- PROCESS 'amcvar/render_inputs_block.html' %]
|
||
<tr>
|
||
<td>
|
||
... | ... | |
|
||
</td>
|
||
</tr>
|
Auch abrufbar als: Unified diff
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