Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 60e0cecf

Von Bernd Bleßmann vor 8 Monaten hinzugefügt

  • ID 60e0cecfb4e12ed8a2e11f2582f693f204a31613
  • Vorgänger a0e9a20c
  • Nachfolger d2b3f1ee

Zwischeninventur: Controller für Abgleich

Unterschiede anzeigen:

SL/Controller/StockCountingReconciliation.pm
package SL::Controller::StockCountingReconciliation;
use strict;
use parent qw(SL::Controller::Base);
use English qw(-no_match_vars);
use List::Util qw(sum0);
use POSIX qw(strftime);
use SL::Controller::Helper::GetModels;
use SL::Controller::Helper::ReportGenerator;
use SL::DB::Employee;
use SL::DB::StockCounting;
use SL::DB::StockCountingItem;
use SL::Helper::Flash qw(flash_later);
use SL::Helper::Number qw(_format_total);
use SL::JSON;
use SL::Locale::String qw(t8);
use SL::Presenter::Tag qw(checkbox_tag);
use SL::ReportGenerator;
use SL::WH;
use Rose::Object::MakeMethods::Generic(
#scalar => [ qw() ],
'scalar --get_set_init' => [ qw(countings models) ],
);
# check permissions
__PACKAGE__->run_before(sub { $::auth->assert('warehouse_management'); });
my %sort_columns = (
counting => t8('Stock Counting'),
counted_at => t8('Counted At'),
qty => t8('Qty'),
part => t8('Article'),
bin => t8('Bin'),
employee => t8('Employee'),
);
sub action_list {
my ($self, %params) = @_;
$self->make_filter_summary;
$self->prepare_report;
my $objects = $self->models->get;
if ($::form->{group_counting_items}) {
my $grouped_objects_by;
my @grouped_objects;
foreach my $object (@$objects) {
my $group_object;
if (!$grouped_objects_by->{$object->counting_id}->{$object->part_id}->{$object->bin_id}) {
$group_object = SL::DB::StockCountingItem->new(
counting => $object->counting, part => $object->part, bin => $object->bin, qty => 0);
push @grouped_objects, $group_object;
$grouped_objects_by->{$object->counting_id}->{$object->part_id}->{$object->bin_id} = $group_object;
} else {
$group_object = $grouped_objects_by->{$object->counting_id}->{$object->part_id}->{$object->bin_id}
}
$group_object->id($group_object->id ? ($group_object->id . ',' . $object->id) : $object->id);
$group_object->qty($group_object->qty + $object->qty);
}
$objects = \@grouped_objects;
}
$self->get_stocked($objects);
$self->setup_list_action_bar;
$self->report_generator_list_objects(report => $self->{report}, objects => $objects);
}
sub action_reconcile {
my ($self) = @_;
my @transfer_errors;
foreach my $selection (@{$::form->{ids}}) {
my $ids = SL::JSON::from_json($selection);
my @counting_item_ids = split ',', $ids;
my $counting_items = SL::DB::Manager::StockCountingItem->get_all(query => [id => \@counting_item_ids]);
my $counted_qty = sum0 map { $_->qty } @$counting_items;
my $stocked_qty = $counting_items->[0]->part->get_stock(bin_id => $counting_items->[0]->bin_id);
my $comment = t8('correction from stock counting (counting "#1")', $counting_items->[0]->counting->name);
my $transfer_qty = $counted_qty - $stocked_qty;
my $src_or_dst = $transfer_qty < 0? 'src' : 'dst';
$transfer_qty = abs($transfer_qty);
my $transfer_error;
# do stock
$::form->throw_on_error(sub {
eval {
WH->transfer({
parts => $counting_items->[0]->part,
$src_or_dst.'_bin' => $counting_items->[0]->bin,
$src_or_dst.'_wh' => $counting_items->[0]->bin->warehouse,
qty => $transfer_qty,
unit => $counting_items->[0]->part->unit,
transfer_type => 'correction',
comment => $comment,
});
1;
} or do { $transfer_error = ref($EVAL_ERROR) eq 'SL::X::FormError' ? $EVAL_ERROR->error : $EVAL_ERROR; }
});
push @transfer_errors, $transfer_error if $transfer_error;
}
if (@transfer_errors) {
flash_later('error', @transfer_errors);
} else {
flash_later('info', t8('successfully reconciled'));
}
return $self->redirect_to($::form->{callback}) if $::form->{callback};
}
sub init_models {
my ($self) = @_;
SL::Controller::Helper::GetModels->new(
controller => $_[0],
model => 'StockCountingItem',
sorted => \%sort_columns,
disable_plugin => 'paginated',
with_objects => [ 'counting', 'employee', 'part' ],
);
}
sub init_countings {
SL::DB::Manager::StockCounting->get_all_sorted;
}
sub prepare_report {
my ($self) = @_;
my $report = SL::ReportGenerator->new(\%::myconfig, $::form);
$self->{report} = $report;
my @columns = $::form->{group_counting_items} ? qw(ids counting part bin qty stocked)
: qw(ids counting counted_at part bin qty stocked employee);
my %column_defs = (
ids => { raw_header_data => checkbox_tag("", id => "check_all", checkall => "[data-checkall=1]"),
align => 'center',
raw_data => sub { $_[0]->correction_inventory_id ? '' : checkbox_tag("ids[]", value => SL::JSON::to_json($_[0]->id), "data-checkall" => 1) } },
counting => { text => t8('Stock Counting'), sub => sub { $_[0]->counting->name }, },
counted_at => { text => t8('Counted At'), sub => sub { $_[0]->counted_at_as_timestamp }, },
qty => { text => t8('Qty'), sub => sub { $_[0]->qty_as_number }, align => 'right' },
part => { text => t8('Article'), sub => sub { $_[0]->part && $_[0]->part->displayable_name } },
bin => { text => t8('Bin'), sub => sub { $_[0]->bin->full_description } },
employee => { text => t8('Employee'), sub => sub { $_[0]->employee ? $_[0]->employee->safe_name : '---'} },
stocked => { text => t8('Stocked Qty'), sub => sub { _format_total($_[0]->{stocked}) }, align => 'right'},
);
# remove columns from defs which are not in @columns
foreach my $column (keys %column_defs) {
delete $column_defs{$column} if !grep { $column eq $_ } @columns;
}
my $title = t8('Stock Countings');
$report->{title} = $title; # for browser titlebar (title-tag)
$report->set_options(
controller_class => 'StockCountingReconciliation',
std_column_visibility => 1,
output_format => 'HTML',
title => $title, # for heading
allow_pdf_export => 1,
allow_csv_export => 1,
);
$report->set_columns(%column_defs);
$report->set_column_order(@columns);
$report->set_export_options(qw(list filter group_counting_items));
$report->set_options_from_form;
$self->models->disable_plugin('paginated') if $report->{options}{output_format} =~ /^(pdf|csv)$/i;
$self->models->add_additional_url_params(filter => $::form->{filter}, group_counting_items => $::form->{group_counting_items});
$self->models->finalize;
$self->models->set_report_generator_sort_options(report => $report, sortable_columns => [keys %sort_columns]);
$report->set_options(
raw_top_info_text => $self->render('stock_counting_reconciliation/report_top', { output => 0 }),
raw_bottom_info_text => $self->render('stock_counting_reconciliation/report_bottom', { output => 0 }, models => $self->models),
attachment_basename => t8('stock_countings') . strftime('_%Y%m%d', localtime time),
);
}
sub make_filter_summary {
my ($self) = @_;
my @filter_strings;
push @filter_strings, t8('Group Counting Items') if $::form->{group_counting_items};
my $filter = $::form->{filter} || {};
my $counting = $filter->{counting_id} ? SL::DB::StockCounting->new(id => $filter->{counting_id})->load->name : '';
my @filters = (
[ $counting, t8('Stock Counting') ],
);
for (@filters) {
push @filter_strings, "$_->[1]: $_->[0]" if $_->[0];
}
$self->{filter_summary} = join ', ', @filter_strings;
}
sub get_stocked {
my ($self, $objects) = @_;
$_->{stocked} = $_->part->get_stock(bin_id => $_->bin_id) for @$objects;
}
sub setup_list_action_bar {
my ($self) = @_;
for my $bar ($::request->layout->get('actionbar')) {
$bar->add(
action => [
t8('Update'),
submit => [ '#filter_form', { action => 'StockCountingReconciliation/list' } ],
accesskey => 'enter',
],
combobox => [
action => [
t8('Actions'),
],
action => [
t8('Reconcile'),
submit => [ '#form', { action => 'StockCountingReconciliation/reconcile', callback => $self->models->get_callback } ],
checks => [ [ 'kivi.check_if_entries_selected', '[name="ids[]"]' ] ],
confirm => t8('Do you really want the selected entries to be reconciled?'),
],
],
);
}
}
1;
menus/user/00-erp.yaml
access: warehouse_management
params:
action: Inventory/stocktaking
- parent: warehouse
id: warehouse_stock_counting_reconciliation
name: Stock Counting Reconciliations
order: 480
access: warehouse_management
params:
action: StockCountingReconciliation/list
- parent: warehouse
id: warehouse_reports
name: Reports
templates/design40_webpages/stock_counting_reconciliation/_filter.html
[%- USE T8 %]
[%- USE L %]
[%- USE LxERP %]
<form action='controller.pl' method='post' id='filter_form'>
<div class="wrapper">
[% BLOCK filter_toggle_panel %]
<table id='filter_table' class='tbl-horizontal'>
<tbody>
<tr>
<th align="right">[% 'Stock Counting' | $T8 %]</th>
<td>
[% L.select_tag('filter.counting_id', SELF.countings,
default => filter.counting_id,
title_key => 'name',
value_key => 'id',
with_empty => 1,
class => 'wi-lightwide') %]
</td>
</tr>
<tr>
<th align="right">[% 'Group Counting Items' | $T8 %]</th>
<td>
[% L.checkbox_tag('group_counting_items', checked = FORM.group_counting_items, for_submit = 1) %]
</td>
</tr>
</body>
</table>
[% L.hidden_tag('sort_by', FORM.sort_by) %]
[% L.hidden_tag('sort_dir', FORM.sort_dir) %]
[% L.hidden_tag('page', FORM.page) %]
<div class="buttons">
[% L.button_tag('$("#filter_form").clearForm()', LxERP.t8('Reset')) %]
</div>
[% END # /BLOCK filter_toggle_panel %]
[% INCLUDE 'common/toggle_panel.html' %]
</div>
</form>
templates/design40_webpages/stock_counting_reconciliation/report_bottom.html
[%- USE L %]
[% L.paginate_controls(models=SELF.models) %]
</form>
</div>
templates/design40_webpages/stock_counting_reconciliation/report_top.html
[%- PROCESS 'stock_counting_reconciliation/_filter.html' filter=SELF.models.filtered.laundered %]
<div class="wrapper">
<form method="post" action="controller.pl" id="form">
templates/webpages/stock_counting_reconciliation/_filter.html
[%- USE T8 %]
[%- USE L %]
[%- USE LxERP %]
[%- USE HTML %]
<form action='controller.pl' method='post' id='filter_form'>
<div class='filter_toggle'>
<a href='#' onClick='javascript:$(".filter_toggle").toggle()'>[% 'Show Filter' | $T8 %]</a>
[% SELF.filter_summary | html %]
</div>
<div class='filter_toggle' style='display:none'>
<a href='#' onClick='javascript:$(".filter_toggle").toggle()'>[% 'Hide Filter' | $T8 %]</a>
<table id='filter_table'>
<tr>
<th align="right">[% 'Stock Counting' | $T8 %]</th>
<td>
[% L.select_tag('filter.counting_id', SELF.countings,
default => filter.counting_id,
title_key => 'name',
value_key => 'id',
with_empty => 1,
style => 'width: 200px') %]
</td>
</tr>
<tr>
<th align="right">[% 'Group Counting Items' | $T8 %]</th>
<td>
[% L.checkbox_tag('group_counting_items', checked = FORM.group_counting_items, for_submit = 1) %]
</td>
</tr>
</table>
[% L.hidden_tag('sort_by', FORM.sort_by) %]
[% L.hidden_tag('sort_dir', FORM.sort_dir) %]
[% L.hidden_tag('page', FORM.page) %]
[% L.button_tag('$("#filter_form").clearForm()', LxERP.t8('Reset')) %]
</div>
</form>
templates/webpages/stock_counting_reconciliation/report_bottom.html
[%- USE L %]
[% L.paginate_controls(models=SELF.models) %]
</form>
templates/webpages/stock_counting_reconciliation/report_top.html
[%- PROCESS 'stock_counting_reconciliation/_filter.html' filter=SELF.models.filtered.laundered %]
<hr>
<form method="post" action="controller.pl" id="form">

Auch abrufbar als: Unified diff