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