use parent qw(SL::Controller::Base);
use SL::Helper::Flash qw(flash_later);
-use SL::Helper::Number qw(_format_number_units);
+use SL::Helper::Number qw(_format_number_units _parse_number);
use SL::Presenter::Tag qw(select_tag hidden_tag div_tag);
use SL::Locale::String qw(t8);
use SL::SessionFile::Random;
use SL::DB::Order;
use SL::DB::Default;
use SL::DB::Unit;
+use SL::DB::Order;
use SL::DB::Part;
use SL::DB::PartClassification;
use SL::DB::PartsGroup;
use SL::Helper::UserPreferences::UpdatePositions;
use SL::Controller::Helper::GetModels;
-use SL::Controller::DeliveryOrder::TypeData;
+use SL::Controller::DeliveryOrder::TypeData qw(:types);
use List::Util qw(first sum0);
use List::UtilsBy qw(sort_by uniq_by);
# safety
-__PACKAGE__->run_before('check_auth');
+__PACKAGE__->run_before('check_auth',
+ except => [ qw(pack_stock_information) ]);
__PACKAGE__->run_before('get_unalterable_data',
only => [ qw(save save_as_new save_and_delivery_order save_and_invoice save_and_ap_transaction
# this interfers with init_order
$self->{converted_from_oe_id} = delete $::form->{id};
- # TODO copy data and remember to link them on save
+ $self->type_data->validate($::form->{type});
+
+ my $order = SL::DB::Order->new(id => $self->{converted_from_oe_id})->load;
+
+ $self->order(SL::DB::DeliveryOrder->new_from($order, type => $::form->{type}));
$self->action_add;
}
email_form => $email_form,
show_bcc => $::auth->assert('email_bcc', 'may fail'),
FILES => \%files,
- is_customer => $self->cv eq 'customer',
+ is_customer => $self->type_data->is_customer,
ALL_EMPLOYEES => $self->{all_employees},
);
ITEM => $item,
ID => $item_id,
SELF => $self,
+ in_out => $self->type_data->transfer,
);
if ($::form->{insert_before_item_id}) {
ITEM => $item,
ID => $item_id,
SELF => $self,
+ in_out => $self->type_data->transfer,
);
if ($::form->{insert_before_item_id}) {
}
+sub action_stock_in_out_dialog {
+ my ($self) = @_;
+
+ my $part = SL::DB::Part->load_cached($::form->{parts_id}) or die "need parts_id";
+ my $stock = $::form->{stock};
+ my $unit = $::form->{unit};
+ my $row = $::form->{row};
+ my $item_id = $::form->{item_id};
+ my $qty = _parse_number($::form->{qty_as_number});
+
+ my $inout = $self->type_data->transfer;
+
+ my @contents = DO->get_item_availability(parts_id => $part->id);
+ my $stock_info = DO->unpack_stock_information(packed => $stock);
+
+ $self->merge_stock_data($stock_info, \@contents, $part);
+
+ $self->render("delivery_order/stock_dialog", { layout => 0 },
+ WHCONTENTS => $self->order->delivered ? $stock_info : \@contents,
+ part => $part,
+ do_qty => $qty,
+ do_unit => $unit,
+ delivered => $self->order->delivered,
+ row => $row,
+ item_id => $item_id,
+ );
+}
+
+# we're using the old YAML based stock packing, but don't want to do this in
+# the frontend so we're doing a tiny roundtrip to the backend, back the info in
+# perl, serve it back to the frontend and store it in the DOM there
+sub action_pack_stock_information {
+ my ($self) = @_;
+
+ my $stock_info = $::form->{stock_info};
+ my $yaml = SL::YAML::Dump($stock_info);
+
+ $self->render(\$yaml, { layout => 0, process => 0 });
+}
+
+sub merge_stock_data {
+ my ($self, $stock_info, $contents, $part) = @_;
+ # TODO rewrite to mapping
+
+ if (!$self->order->delivered) {
+ for my $row (@$contents) {
+ $row->{available_qty} = _format_number_units($row->{qty}, $row->{unit}, $part->unit);
+
+ for my $sinfo (@{ $stock_info }) {
+ next if $row->{bin_id} != $sinfo->{bin_id} ||
+ $row->{warehouse_id} != $sinfo->{warehouse_id} ||
+ $row->{chargenumber} ne $sinfo->{chargenumber} ||
+ $row->{bestbefore} ne $sinfo->{bestbefore};
+
+ $row->{"stock_$_"} = $sinfo->{$_}
+ for qw(qty unit error delivery_order_items_stock_id);
+ }
+ }
+
+ } else {
+ for my $sinfo (@{ $stock_info }) {
+ my $bin = SL::DB::Bin->load_cached($sinfo->{bin_id});
+ $sinfo->{warehouse_description} = $bin->warehouse->description;
+ $sinfo->{bin_description} = $bin->escription;
+ map { $sinfo->{"stock_$_"} = $sinfo->{$_} } qw(qty unit);
+ }
+ }
+}
+
# load the second row for one or more items
#
# This action gets the html code for all items second rows by rendering a template for
# TODO move to type data
my $trans_type = $self->type_data->properties('transfer') eq 'in'
? SL::DB::Manager::TransferType->find_by(direction => "id", description => "stock")
- : SL::DB::Manager::TransferType->find_by(direction => "out", deescription => "shipped");
+ : SL::DB::Manager::TransferType->find_by(direction => "out", description => "shipped");
my @transfer_requests;
$self->js->flash("error", t8("No stock to transfer"))->render;
}
- SL::DB->with_transaction(sub {
+ SL::DB->client->with_transaction(sub {
$_->save for @transfer_requests;
- $self->order->update_attributes(deliverd => 1);
+ $self->order->update_attributes(delivered => 1);
});
$self->js->flash("info", t8("Stock transfered"))->render;
# You need a custom shipto object to call cvars_by_config to get the cvars.
$self->order->custom_shipto(SL::DB::Shipto->new(module => 'OE', custom_variables => [])) if !$self->order->custom_shipto;
+ $self->prepare_stock_info($_) for $self->order->items;
+
return $self->order;
}
push @items, $item;
$pos++;
}
+
+ $self->prepare_stock_info($_) for $order->items, @items;
+
$order->add_items(grep {!$_->id} @items);
return $order;
# saved. Adding empty custom_variables to new orderitem here solves this problem.
$item ||= SL::DB::DeliveryOrderItem->new(custom_variables => []);
+ # handle stock info
+ if (my $stock_info = delete $attr->{stock_info}) {
+ my %existing = map { $_->id => $_ } $item->delivery_order_stock_entries;
+ my @save;
+
+ for my $line (@{ DO->unpack_stock_information(packed => $stock_info) }) {
+ # lookup existing or make new
+ my $obj = delete $existing{$line->{delivery_order_items_stock_id}}
+ // SL::DB::DeliveryOrderItemsStock->new;
+
+ # assign attributes
+ $obj->$_($line->{$_}) for qw(bin_id warehouse_id chargenumber qty unit);
+ $obj->bestbefore_as_date($line->{bestfbefore})
+ if $line->{bestbefore} && $::instance_conf->get_show_bestbefore;
+ push @save, $obj;
+ }
+
+ $item->delivery_order_stock_entries(@save);
+ }
+
$item->assign_attributes(%$attr);
if ($is_new) {
return $item;
}
+sub prepare_stock_info {
+ my ($self, $item) = @_;
+
+ $item->{stock_info} = SL::YAML::Dump([
+ map +{
+ delivery_order_items_stock_id => $_->id,
+ qty => $_->qty,
+ warehouse_id => $_->warehouse_id,
+ bin_id => $_->bin_id,
+ chargenumber => $_->chargenumber,
+ unit => $_->unit,
+ }, $item->delivery_order_stock_entries
+ ]);
+}
+
sub setup_order_from_cv {
my ($order) = @_;
if ($::form->{converted_from_oe_id}) {
my @converted_from_oe_ids = split ' ', $::form->{converted_from_oe_id};
foreach my $converted_from_oe_id (@converted_from_oe_ids) {
- my $src = SL::DB::DeliveryOrder->new(id => $converted_from_oe_id)->load;
- $src->update_attributes(closed => 1) if $src->type =~ /_quotation$/;
+ my $src = SL::DB::Order->new(id => $converted_from_oe_id)->load;
+ $src->update_attributes(closed => 1) if $src->type =~ /_quotation$/ && $self->order->is_type(PURCHASE_DELIVERY_ORDER_TYPE);
$src->link_to_record($self->order);
}
if (scalar @{ $::form->{converted_from_orderitems_ids} || [] }) {
} } @all_objects;
}
- $self->{template_args}{inout} = $self->type_data->properties('transfer');
+ $self->{template_args}{in_out} = $self->type_data->transfer;
$self->get_item_cvpartnumber($_) for @{$self->order->items_sorted};
sub calculate_stock_in_out {
my ($self, $item) = @_;
- return "" if !$item->part || !$item->part->unit;
+ return "" if !$item->part || !$item->part->unit || !$item->unit;
- my $in_out = $self->type_data->properties("transfer");
+ my $in_out = $self->type_data->transfer;
my $do_qty = $item->qty;
- my $sum = sum0 map { $_->unit_obj->convert_to($_->qty, $item->unit_obj) } @{ $item->delivery_order_stock_entries };
+ my $sum = sum0 map {
+ $_->unit_obj->convert_to($_->qty, $item->unit_obj)
+ } $item->delivery_order_stock_entries;
my $matches = $do_qty == $sum;
my $content = _format_number_units($sum, 2, $item->unit_obj, $item->part->unit_obj);