use strict;
use warnings;
+use POSIX qw(strftime);
use parent qw(SL::Controller::Base);
use SL::DB::Warehouse;
use SL::DB::Unit;
use SL::WH;
+use SL::ReportGenerator;
use SL::Locale::String qw(t8);
use SL::Presenter;
use SL::DBUtils;
use SL::Helper::Flash;
+use SL::Controller::Helper::ReportGenerator;
use English qw(-no_match_vars);
__PACKAGE__->run_before('load_bin_from_form', only => [ qw(stock_in stock) ]);
__PACKAGE__->run_before('set_target_from_part', only => [ qw(part_changed) ]);
__PACKAGE__->run_before('mini_stock', only => [ qw(stock_in mini_stock) ]);
-__PACKAGE__->run_before('sanitize_target', only => [ qw(stock_in warehouse_changed part_changed) ]);
+__PACKAGE__->run_before('sanitize_target', only => [ qw(stock_usage stock_in warehouse_changed part_changed) ]);
__PACKAGE__->run_before('set_layout');
sub action_stock_in {
$self->render('inventory/warehouse_selection_stock', title => $::form->{title}, TRANSFER_TYPES => $transfer_types );
}
+sub action_stock_usage {
+ my ($self) = @_;
+
+ $::form->{title} = t8('UsageE');
+
+ $::form->get_lists('warehouses' => { 'key' => 'WAREHOUSES',
+ 'bins' => 'BINS', });
+ $::request->layout->use_javascript("${_}.js") for qw(kivi.PartsWarehouse);
+
+ $self->render('inventory/warehouse_usage',
+ title => $::form->{title},
+ year => DateTime->today->year,
+ # PARTSCLASSIFICATIONS => SL::DB:Manager::PartsClassification->get_all_classifications_by_name() ,
+ WAREHOUSES => $::form->{WAREHOUSES},
+ WAREHOUSE_FILTER => 1,
+ warehouse_id => 0,
+ bin_id => 0
+ );
+
+}
+
+sub getnumcolumns {
+ my ($self) = @_;
+ return qw(stock incorrection found insum back outcorrection disposed
+ missing shipped used outsum consumed averconsumed);
+}
+
+sub action_usage {
+ my ($self) = @_;
+
+ $main::lxdebug->enter_sub();
+
+ my $form = $main::form;
+ my %myconfig = %main::myconfig;
+ my $locale = $main::locale;
+
+ $form->{title} = t8('UsageE');
+ $form->{report_generator_output_format} = 'HTML' if !$form->{report_generator_output_format};
+
+ my $report = SL::ReportGenerator->new(\%myconfig, $form);
+
+ my @columns = qw(partnumber partdescription);
+
+ push @columns , qw(ptype unit) if $form->{report_generator_output_format} eq 'HTML';
+
+ my @numcolumns = qw(stock incorrection found insum back outcorrection disposed
+ missing shipped used outsum consumed averconsumed);
+
+ push @columns , $self->getnumcolumns();
+
+ my @hidden_variables = qw(reporttype year duetyp fromdate todate
+ warehouse_id bin_id partnumber description bestbefore chargenumber partstypes_id);
+ my %column_defs = (
+ 'partnumber' => { 'text' => $locale->text('Part Number'), },
+ # 'partclass' => { 'text' => $locale->text('Part Classification'), },
+ 'partdescription' => { 'text' => $locale->text('Part_br_Description'), },
+ 'unit' => { 'text' => $locale->text('Unit'), },
+ 'stock' => { 'text' => $locale->text('stock_br'), },
+ 'incorrection' => { 'text' => $locale->text('correction_br'), },
+ 'found' => { 'text' => $locale->text('found_br'), },
+ 'insum' => { 'text' => $locale->text('sum'), },
+ 'back' => { 'text' => $locale->text('back_br'), },
+ 'outcorrection' => { 'text' => $locale->text('correction_br'), },
+ 'disposed' => { 'text' => $locale->text('disposed_br'), },
+ 'missing' => { 'text' => $locale->text('missing_br'), },
+ 'shipped' => { 'text' => $locale->text('shipped_br'), },
+ 'used' => { 'text' => $locale->text('used_br'), },
+ 'outsum' => { 'text' => $locale->text('sum'), },
+ 'consumed' => { 'text' => $locale->text('consumed'), },
+ 'averconsumed' => { 'text' => $locale->text('averconsumed_br'), },
+ );
+
+
+ map { $column_defs{$_}->{visible} = 1 } @columns;
+ #map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
+ map { $column_defs{$_}->{align} = 'right' } @numcolumns;
+
+ my @custom_headers = ();
+ # Zeile 1:
+ push @custom_headers, [
+ { 'text' => $locale->text('Part'),
+ 'colspan' => ($form->{report_generator_output_format} eq 'HTML'?4:2), 'align' => 'center'},
+ { 'text' => $locale->text('Into bin'), 'colspan' => 4, 'align' => 'center'},
+ { 'text' => $locale->text('From bin'), 'colspan' => 7, 'align' => 'center'},
+ { 'text' => $locale->text('UsageWithout'), 'colspan' => 2, 'align' => 'center'},
+ ];
+
+ # Zeile 2:
+ my @line_2 = ();
+ map { push @line_2 , $column_defs{$_} } @columns;
+ push @custom_headers, [ @line_2 ];
+
+ $report->set_custom_headers(@custom_headers);
+ $report->set_columns( %column_defs );
+ $report->set_column_order(@columns);
+
+ $report->set_export_options('usage', @hidden_variables );
+
+ $report->set_sort_indicator($form->{sort}, $form->{order});
+ $report->set_options('output_format' => 'HTML',
+ 'controller_class' => 'Inventory',
+ 'title' => $form->{title},
+# 'html_template' => 'inventory/usage_report',
+ 'attachment_basename' => strftime($locale->text('warehouse_usage_list') . '_%Y%m%d', localtime time));
+ $report->set_options_from_form;
+
+ my %searchparams ;
+# form vars
+# reporttype = custom
+# year = 2014
+# duetyp = 7
+
+ my $start = DateTime->now_local;
+ my $end = DateTime->now_local;
+ my $actualepoch = $end->epoch();
+ my $days = 365;
+ my $mdays=30;
+ $searchparams{reporttype} = $form->{reporttype};
+ if ($form->{reporttype} eq "custom") {
+ my $smon = 1;
+ my $emon = 12;
+ my $sday = 1;
+ my $eday = 31;
+ #forgotten the year --> thisyear
+ if ($form->{year} !~ m/^\d\d\d\d$/) {
+ $locale->date(\%myconfig, $form->current_date(\%myconfig), 0) =~
+ /(\d\d\d\d)/;
+ $form->{year} = $1;
+ }
+ my $leapday = ($form->{year} % 4 == 0) ? 1:0;
+ #yearly report
+ if ($form->{duetyp} eq "13") {
+ $days += $leapday;
+ }
+
+ #Quater reports
+ if ($form->{duetyp} eq "A") {
+ $emon = 3;
+ $days = 90 + $leapday;
+ }
+ if ($form->{duetyp} eq "B") {
+ $smon = 4;
+ $emon = 6;
+ $eday = 30;
+ $days = 91;
+ }
+ if ($form->{duetyp} eq "C") {
+ $smon = 7;
+ $emon = 9;
+ $eday = 30;
+ $days = 92;
+ }
+ if ($form->{duetyp} eq "D") {
+ $smon = 10;
+ $days = 92;
+ }
+ #Monthly reports
+ if ($form->{duetyp} eq "1" || $form->{duetyp} eq "3" || $form->{duetyp} eq "5" ||
+ $form->{duetyp} eq "7" || $form->{duetyp} eq "8" || $form->{duetyp} eq "10" ||
+ $form->{duetyp} eq "12") {
+ $smon = $emon = $form->{duetyp}*1;
+ $mdays=$days = 31;
+ }
+ if ($form->{duetyp} eq "2" || $form->{duetyp} eq "4" || $form->{duetyp} eq "6" ||
+ $form->{duetyp} eq "9" || $form->{duetyp} eq "11" ) {
+ $smon = $emon = $form->{duetyp}*1;
+ $eday = 30;
+ if ($form->{duetyp} eq "2" ) {
+ #this works from 1901 to 2099, 1900 and 2100 fail.
+ $eday = ($form->{year} % 4 == 0) ? 29 : 28;
+ }
+ $mdays=$days = $eday;
+ }
+ $searchparams{year} = $form->{year};
+ $searchparams{duetyp} = $form->{duetyp};
+ $start->set_month($smon);
+ $start->set_day($sday);
+ $start->set_year($form->{year}*1);
+ $end->set_month($emon);
+ $end->set_day($eday);
+ $end->set_year($form->{year}*1);
+ } else {
+ $searchparams{fromdate} = $form->{fromdate};
+ $searchparams{todate} = $form->{todate};
+# reporttype = free
+# fromdate = 01.01.2014
+# todate = 31.05.2014
+ my ($yy, $mm, $dd) = $locale->parse_date(\%myconfig,$form->{fromdate});
+ $start->set_year($yy);
+ $start->set_month($mm);
+ $start->set_day($dd);
+ ($yy, $mm, $dd) = $locale->parse_date(\%myconfig,$form->{todate});
+ $end->set_year($yy);
+ $end->set_month($mm);
+ $end->set_day($dd);
+ my $dur = $start->delta_md($end);
+ $days = $dur->delta_months()*30 + $dur->delta_days() ;
+ }
+ $start->set_second(0);
+ $start->set_minute(0);
+ $start->set_hour(0);
+ $end->set_second(59);
+ $end->set_minute(59);
+ $end->set_hour(23);
+ if ( $end->epoch() > $actualepoch ) {
+ $end = DateTime->now_local;
+ my $dur = $start->delta_md($end);
+ $days = $dur->delta_months()*30 + $dur->delta_days() ;
+ }
+ if ( $start->epoch() > $end->epoch() ) { $start = $end;$days = 1;}
+ $days = $mdays if $days < $mdays;
+ #$main::lxdebug->message(LXDebug->DEBUG2(), "start=".$start->epoch());
+ #$main::lxdebug->message(LXDebug->DEBUG2(), " end=".$end->epoch());
+ #$main::lxdebug->message(LXDebug->DEBUG2(), " days=".$days);
+ my @andfilter = (shippingdate => { ge => $start }, shippingdate => { le => $end } );
+ if ( $form->{warehouse_id} ) {
+ push @andfilter , ( warehouse_id => $form->{warehouse_id});
+ $searchparams{warehouse_id} = $form->{warehouse_id};
+ if ( $form->{bin_id} ) {
+ push @andfilter , ( bin_id => $form->{bin_id});
+ $searchparams{bin_id} = $form->{bin_id};
+ }
+ }
+ # alias class t2 entspricht parts
+ if ( $form->{partnumber} ) {
+ push @andfilter , ( 't2.partnumber' => { ilike => '%'. $form->{partnumber} .'%' });
+ $searchparams{partnumber} = $form->{partnumber};
+ }
+ if ( $form->{description} ) {
+ push @andfilter , ( 't2.description' => { ilike => '%'. $form->{description} .'%' });
+ $searchparams{description} = $form->{description};
+ }
+ if ( $form->{bestbefore} ) {
+ push @andfilter , ( bestbefore => { eq => $form->{bestbefore} });
+ $searchparams{bestbefore} = $form->{bestbefore};
+ }
+ if ( $form->{chargenumber} ) {
+ push @andfilter , ( chargenumber => { ilike => '%'.$form->{chargenumber}.'%' });
+ $searchparams{chargenumber} = $form->{chargenumber};
+ }
+ if ( $form->{partstypes_id} ) {
+ push @andfilter , ( 't2.partstypes_id' => $form->{partstypes_id} );
+ $searchparams{partstypes_id} = $form->{partstypes_id};
+ }
+
+ my @filter = (and => [ @andfilter ] );
+
+ my $objs = SL::DB::Manager::Inventory->get_all(with_objects => ['parts'], where => [ @filter ] , sort_by => 'parts.partnumber ASC');
+ #my $objs = SL::DB::Inventory->_get_manager_class->get_all(...);
+
+ # manual paginating, yuck
+ my $page = $::form->{page} || 1;
+ my $pages = {};
+ $pages->{per_page} = $::form->{per_page} || 20;
+ my $first_nr = ($page - 1) * $pages->{per_page};
+ my $last_nr = $first_nr + $pages->{per_page};
+
+ my $last_partid = 0;
+ my $last_row = { };
+ my $row_ind = 0;
+ my $allrows = 0;
+ $allrows = 1 if $form->{report_generator_output_format} ne 'HTML' ;
+ #$main::lxdebug->message(LXDebug->DEBUG2(), "first_nr=".$first_nr." last_nr=".$last_nr);
+ foreach my $entry (@{ $objs } ) {
+ if ( $entry->parts_id != $last_partid ) {
+ if ( $last_partid > 0 ) {
+ if ( $allrows || ($row_ind >= $first_nr && $row_ind < $last_nr )) {
+ $self->make_row_result($last_row,$days,$last_partid);
+ $report->add_data($last_row);
+ }
+ $row_ind++ ;
+ }
+ $last_partid = $entry->parts_id;
+ $last_row = { };
+ $last_row->{partnumber}->{data} = $entry->part->partnumber;
+ $last_row->{partdescription}->{data} = $entry->part->description;
+ $last_row->{unit}->{data} = $entry->part->unit;
+ $last_row->{stock}->{data} = 0;
+ $last_row->{incorrection}->{data} = 0;
+ $last_row->{found}->{data} = 0;
+ $last_row->{back}->{data} = 0;
+ $last_row->{outcorrection}->{data} = 0;
+ $last_row->{disposed}->{data} = 0;
+ $last_row->{missing}->{data} = 0;
+ $last_row->{shipped}->{data} = 0;
+ $last_row->{used}->{data} = 0;
+ $last_row->{insum}->{data} = 0;
+ $last_row->{outsum}->{data} = 0;
+ $last_row->{consumed}->{data} = 0;
+ $last_row->{averconsumed}->{data} = 0;
+ }
+ if ( !$allrows && $row_ind >= $last_nr ) {
+ next;
+ }
+ my $prefix='';
+ if ( $entry->trans_type->description eq 'correction' ) {
+ $prefix = $entry->trans_type->direction;
+ }
+ $last_row->{$prefix.$entry->trans_type->description}->{data} +=
+ ( $entry->trans_type->direction eq 'out' ? -$entry->qty : $entry->qty );
+ }
+ if ( $last_partid > 0 && ( $allrows || ($row_ind >= $first_nr && $row_ind < $last_nr ))) {
+ $self->make_row_result($last_row,$days,$last_partid);
+ $report->add_data($last_row);
+ $row_ind++ ;
+ }
+ my $num_rows = @{ $report->{data} } ;
+ #$main::lxdebug->message(LXDebug->DEBUG2(), "count=".$row_ind." rows=".$num_rows);
+
+ if ( ! $allrows ) {
+ $pages->{max} = SL::DB::Helper::Paginated::ceil($row_ind, $pages->{per_page}) || 1;
+ $pages->{page} = $page < 1 ? 1: $page > $pages->{max} ? $pages->{max}: $page;
+ $pages->{common} = [ grep { $_->{visible} } @{ SL::DB::Helper::Paginated::make_common_pages($pages->{page}, $pages->{max}) } ];
+ $self->{pages} = $pages;
+ $searchparams{action} = "usage";
+ $self->{base_url} = $self->url_for(\%searchparams );
+ #$main::lxdebug->message(LXDebug->DEBUG2(), "page=".$pages->{page}." url=".$self->{base_url});
+
+ $report->set_options('raw_bottom_info_text' => $self->render('inventory/report_bottom', { output => 0 }) );
+ }
+ $report->generate_with_headers();
+
+ $main::lxdebug->leave_sub();
+
+}
+
+sub make_row_result {
+ my ($self,$row,$days,$partid) = @_;
+ my $form = $main::form;
+ my $myconfig = \%main::myconfig;
+
+ $row->{insum}->{data} = $row->{stock}->{data} + $row->{incorrection}->{data} + $row->{found}->{data};
+ $row->{outsum}->{data} = $row->{back}->{data} + $row->{outcorrection}->{data} + $row->{disposed}->{data} +
+ $row->{missing}->{data} + $row->{shipped}->{data} + $row->{used}->{data};
+ $row->{consumed}->{data} = $row->{outsum}->{data} -
+ $row->{outcorrection}->{data} - $row->{incorrection}->{data};
+ $row->{averconsumed}->{data} = $row->{consumed}->{data}*30/$days ;
+ map { $row->{$_}->{data} = $form->format_amount($myconfig,$row->{$_}->{data},2); } $self->getnumcolumns();
+# $row->{partclass}->{data} = '';
+ $row->{partnumber}->{link} = 'ic.pl?action=edit&id='.$partid;
+# $row->{partdescription}->{link} = 'ic.pl?action=edit&id='.$partid;
+}
+
sub action_stock {
my ($self) = @_;
SL::DB::Manager::Warehouse->get_all(query => [ or => [ invalid => 0, invalid => undef ]]);
}
+#sub init_bins {
+# SL::DB::Manager::Bin->get_all();
+#}
+
sub init_units {
SL::DB::Manager::Unit->get_all;
}
$self->warehouse($self->warehouses->[0]) if !$self->warehouse || !$self->warehouse->id;
$self->bin ($self->warehouse->bins->[0]) if !$self->bin || !$self->bin->id;
+# foreach my $warehouse ( $self->warehouses ) {
+# $warehouse->{BINS} = [];
+# foreach my $bin ( $self->bins ) {
+# if ( $bin->warehouse_id == $warehouse->id ) {
+# push @{ $warehouse->{BINS} }, $bin;
+# }
+# }
+# }
}
sub load_part_from_form {
$form->{title} = $locale->text("WHJournal");
$form->{sort} ||= 'date';
+ $form->{report_generator_output_format} = 'HTML' if !$form->{report_generator_output_format};
+
my %filter;
my @columns = qw(trans_id date warehouse_from bin_from warehouse_to bin_to partnumber partdescription chargenumber bestbefore trans_type comment qty employee oe_id projectnumber);
'chargenumber' => { 'text' => $locale->text('Charge Number'), },
'bestbefore' => { 'text' => $locale->text('Best Before'), },
'qty' => { 'text' => $locale->text('Qty'), },
+ 'unit' => { 'text' => $locale->text('Part Unit'), },
+ 'partunit' => { 'text' => $locale->text('Unit'), },
'employee' => { 'text' => $locale->text('Employee'), },
'projectnumber' => { 'text' => $locale->text('Project Number'), },
'oe_id' => { 'text' => $locale->text('Document'), },
);
my $href = build_std_url('action=generate_journal', grep { $form->{$_} } @hidden_variables);
- map { $column_defs{$_}->{link} = $href . "&sort=${_}&order=" . Q($_ eq $form->{sort} ? 1 - $form->{order} : $form->{order}) } @columns;
+ my $page = $::form->{page} || 1;
+ map { $column_defs{$_}->{link} = $href ."&page=".$page. "&sort=${_}&order=" . Q($_ eq $form->{sort} ? 1 - $form->{order} : $form->{order}) } @columns;
my %column_alignment = map { $_ => 'right' } qw(qty);
'purchase_invoice' => { script => 'ir', title => $locale->text('Purchase Invoice') },
);
+ my $allrows = 0;
+ $allrows = 1 if $form->{report_generator_output_format} ne 'HTML' ;
+
+ # manual paginating
+ my $pages = {};
+ $pages->{per_page} = $::form->{per_page} || 15;
+ my $first_nr = ($page - 1) * $pages->{per_page};
+ my $last_nr = $first_nr + $pages->{per_page};
+ my $idx = 0;
+
foreach my $entry (@contents) {
$entry->{qty} = $form->format_amount_units('amount' => $entry->{qty},
'part_unit' => $entry->{partunit},
}
}
- $report->add_data($row);
+ if ( $allrows || ($idx >= $first_nr && $idx < $last_nr )) {
+ $report->add_data($row);
+ }
+ $idx++;
}
+ if ( ! $allrows ) {
+ $pages->{max} = SL::DB::Helper::Paginated::ceil($idx, $pages->{per_page}) || 1;
+ $pages->{page} = $page < 1 ? 1: $page > $pages->{max} ? $pages->{max}: $page;
+ $pages->{common} = [ grep { $_->{visible} } @{ SL::DB::Helper::Paginated::make_common_pages($pages->{page}, $pages->{max}) } ];
+
+ $report->set_options('raw_bottom_info_text' => $form->parse_html_template('common/paginate',
+ { 'pages' => $pages , 'base_url' => $href}) );
+ }
$report->generate_with_headers();
$main::lxdebug->leave_sub();
$form->{title} = $locale->text("Report about warehouse contents");
$form->{sort} ||= 'partnumber';
+ $form->{sort} ||= 'partunit';
my $sort_col = $form->{sort};
my %filter;
my @columns = qw(warehousedescription bindescription partnumber partdescription chargenumber bestbefore qty stock_value);
# filter stuff
- map { $filter{$_} = $form->{$_} if ($form->{$_}) } qw(warehouse_id bin_id partnumber description chargenumber bestbefore date include_invalid_warehouses);
+ map { $filter{$_} = $form->{$_} if ($form->{$_}) } qw(warehouse_id bin_id partstypes_id partnumber description chargenumber bestbefore date include_invalid_warehouses);
# show filter stuff also in report
my @options;
$form->{subtotal} = '' if (!first { $_ eq $sort_col } qw(partnumber partdescription));
+ $form->{report_generator_output_format} = 'HTML' if !$form->{report_generator_output_format};
my $report = SL::ReportGenerator->new(\%myconfig, $form);
my @hidden_variables = map { "l_${_}" } @columns;
- push @hidden_variables, qw(warehouse_id bin_id partnumber description chargenumber bestbefore qty_op qty qty_unit l_warehousedescription l_bindescription);
+ push @hidden_variables, qw(warehouse_id bin_id partnumber partstypes_id description chargenumber bestbefore qty_op qty qty_unit partunit l_warehousedescription l_bindescription);
push @hidden_variables, qw(include_empty_bins subtotal include_invalid_warehouses date);
my %column_defs = (
'chargenumber' => { 'text' => $locale->text('Charge Number'), },
'bestbefore' => { 'text' => $locale->text('Best Before'), },
'qty' => { 'text' => $locale->text('Qty'), },
+ 'partunit' => { 'text' => $locale->text('Unit'), },
'stock_value' => { 'text' => $locale->text('Stock value'), },
);
my $href = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
- map { $column_defs{$_}->{link} = $href . "&sort=${_}&order=" . Q($_ eq $sort_col ? 1 - $form->{order} : $form->{order}) } @columns;
+ my $page = $::form->{page} || 1;
+ map { $column_defs{$_}->{link} = $href . "&page=".$page."&sort=${_}&order=" . Q($_ eq $sort_col ? 1 - $form->{order} : $form->{order}) } @columns;
my %column_alignment = map { $_ => 'right' } qw(qty stock_value);
my $total_stock_value = 0;
+ my $allrows = 0;
+ $allrows = 1 if $form->{report_generator_output_format} ne 'HTML' ;
+
+ # manual paginating
+ my $pages = {};
+ $pages->{per_page} = $::form->{per_page} || 20;
+ my $first_nr = ($page - 1) * $pages->{per_page};
+ my $last_nr = $first_nr + $pages->{per_page};
+
foreach my $entry (@contents) {
map { $subtotals{$_} += $entry->{$_} } @subtotals_columns;
$total_stock_value += $entry->{stock_value} * 1;
-
- $entry->{qty} = $form->format_amount_units('amount' => $entry->{qty},
- 'part_unit' => $entry->{partunit},
- 'conv_units' => 'convertible');
+ $entry->{qty} = $form->format_amount(\%myconfig, $entry->{qty});
+# $entry->{qty} = $form->format_amount_units('amount' => $entry->{qty},
+# 'part_unit' => $entry->{partunit},
+# 'conv_units' => 'convertible');
$entry->{stock_value} = $form->format_amount(\%myconfig, $entry->{stock_value} * 1, 2);
my $row_set = [ { map { $_ => { 'data' => $entry->{$_}, 'align' => $column_alignment{$_} } } @columns } ];
|| ($entry->{$sort_col} ne $contents[$idx + 1]->{$sort_col}))) {
my $row = { map { $_ => { 'data' => '', 'class' => 'listsubtotal', 'align' => $column_alignment{$_}, } } @columns };
- $row->{qty}->{data} = $form->format_amount_units('amount' => $subtotals{qty} * 1,
- 'part_unit' => $entry->{partunit},
- 'conv_units' => 'convertible');
+ $row->{qty}->{data} = $form->format_amount(\%myconfig, $subtotals{qty});
+# $row->{qty}->{data} = $form->format_amount_units('amount' => $subtotals{qty} * 1,
+# 'part_unit' => $entry->{partunit},
+# 'conv_units' => 'convertible');
$row->{stock_value}->{data} = $form->format_amount(\%myconfig, $subtotals{stock_value} * 1, 2);
%subtotals = map { $_ => 0 } @subtotals_columns;
push @{ $row_set }, $row;
}
- $report->add_data($row_set);
-
+ if ( $allrows || ($idx >= $first_nr && $idx < $last_nr )) {
+ $report->add_data($row_set);
+ }
$idx++;
}
kleinere neue Features und Detailverbesserungen:
+ - Weiterer Bericht in der Rubrik Lager: Lagerentnahme
+ Gibt eine Statistik über Lagerbewegungen, pro Monat/Quartal/Jahr.
+
- Für UStVA Voranmeldung über Elster gibt es die Anbindung über Geierlein (Installation/Config siehe Commit)
- CSV-Import von Artikel hat nun für existierende Artikel folgende Optionen:
'Fristsetzung' => 'Fristsetzung',
'From' => 'Von',
'From Date' => 'Von',
+ 'From bin' => 'Ausgelagert',
'From this version on a new feature is available.' => 'Ab dieser Version ist ein neues Feature verfügbar.',
'From this version on it is necessary to name a default value.' => 'Ab dieser Version benötigt kivitendo eine Standardwährung.',
'From this version on the partnumber of services, articles and assemblies have to be unique.' => 'Ab dieser Version müssen Artikelnummern eindeutig vergeben werden.',
'Internal Phone List' => 'Interne Telefonliste',
'Internal comment' => 'Interne Bemerkungen',
'Internet' => 'Internet',
+ 'Into bin' => 'Eingelagert',
'Intra-Community supply' => 'Gelangensbestätigung',
'Introduction of clients' => 'Einführung von Mandanten',
'Inv. Duedate' => 'Rg. Fälligkeit',
'Part' => 'Ware',
'Part "#1" has chargenumber or best before date set. So it cannot be transfered automatically.' => 'Bei Artikel "#1" ist eine Chargenummer oder ein Mindesthaltbarkeitsdatum vergeben. Deshalb kann dieser Artikel nicht automatisch ausgelagert werden.',
'Part (database ID)' => 'Artikel (Datenbank-ID)',
+ 'Part Classification' => '',
'Part Description' => 'Artikelbeschreibung',
'Part Description missing!' => 'Artikelbezeichnung fehlt!',
'Part Notes' => 'Bemerkungen',
'Part Number' => 'Artikelnummer',
'Part Number missing!' => 'Artikelnummer fehlt!',
+ 'Part Unit' => '',
'Part picker' => 'Artikelauswahl',
+ 'Part_br_Description' => 'Beschreibung',
'Partial invoices' => 'Teilrechnungen',
'Partnumber' => 'Artikelnummer',
'Partnumber must not be set to empty!' => 'Die Artikelnummer darf nicht auf leer geändert werden.',
'Updating the client fields in the database "#1" on host "#2:#3" failed.' => 'Die Aktualisierung der Mandantenfelder in der Datenbank "#1" auf Host "#2:#3" schlug fehl.',
'Uploaded at' => 'Hochgeladen um',
'Uploaded on #1, size #2 kB' => 'Am #1 hochgeladen, Größe #2 kB',
+ 'UsageE' => 'Lagerentnahme',
+ 'UsageWithout' => 'Entnommen (ohne Korr.)',
'Use As New' => 'Als neu verwenden',
'Use Balance Sheet' => 'Bilanz verwenden',
'Use Datevautomatik' => 'Datev-Automatik verwenden',
'View/edit all employees sales documents' => 'Bearbeiten/ansehen der Verkaufsdokumente aller Mitarbeiter',
'Von Konto: ' => 'von Konto: ',
'WHJournal' => 'Lagerbuchungen',
+ 'WHUsage' => 'Lagerentnahme',
'Warehouse' => 'Lager',
'Warehouse (database ID)' => 'Lager (Datenbank-ID)',
'Warehouse (name)' => 'Lager (Name)',
'assembly' => 'Erzeugnis',
'assembly_list' => 'erzeugnisliste',
'averaged values, in invoice mode only useful when filtered by a part' => 'gemittelte Werte, im Rechnungsmodus nur sinnvoll wenn nach Artikel gefiltert wird',
+ 'averconsumed_br' => 'Ø mtl.',
'back' => 'zurück',
+ 'back_br' => 'Zurk.',
'balance' => 'Betriebsvermögensvergleich/Bilanzierung',
'bank_collection_payment_list_#1' => 'bankeinzugszahlungsliste_#1',
'bank_transfer_payment_list_#1' => 'ueberweisungszahlungsliste_#1',
'config/kivitendo.conf: Key "authentication/ldap" is missing.' => 'config/kivitendo.conf: Der Schlüssel "authentication/ldap" fehlt.',
'config/kivitendo.conf: Missing parameters in "authentication/database". Required parameters are "host", "db" and "user".' => 'config/kivitendo.conf: Fehlende Parameter in "authentication/database". Benötigte Parameter sind "host", "db" und "user".',
'config/kivitendo.conf: Missing parameters in "authentication/ldap". Required parameters are "host", "attribute" and "base_dn".' => 'config/kivitendo.conf: Fehlende Parameter in "authentication/ldap". Benötigt werden "host", "attribute" und "base_dn".',
+ 'consumed' => 'Im Zeitraum',
'contact_list' => 'ansprechperson_liste',
'continue' => 'weiter',
'correction' => 'Korrektur',
+ 'correction_br' => 'Korr.',
'cp_greeting to cp_gender migration' => 'Datenumwandlung von Titel nach Geschlecht (cp_greeting to cp_gender)',
'customer' => 'Kunde',
'customer_list' => 'kundenliste',
'difference_as_skonto' => 'Differenz als Skonto',
'direct debit' => 'Lastschrifteinzug',
'disposed' => 'Entsorgung',
+ 'disposed_br' => 'Entsgt.',
'do not include' => 'Nicht aufnehmen',
'done' => 'erledigt',
'dunning_list' => 'mahnungsliste',
'for all' => 'für alle',
'for date' => 'zum Stichtag',
'found' => 'Gefunden',
+ 'found_br' => 'Gef.',
'from (time)' => 'von',
'general_ledger_list' => 'buchungsjournal',
'generate cb/ob transactions for selected charts' => 'Buchungen erstellen',
'male' => 'männlich',
'mark as paid' => 'als bezahlt markieren',
'missing' => 'Fehlbestand',
+ 'missing_br' => 'Fehl.',
'month' => 'Monatliche Abgabe',
'monthly' => 'monatlich',
'never' => 'niemals',
'service' => 'Dienstleistung',
'service_list' => 'dienstleistungsliste',
'shipped' => 'verschickt',
+ 'shipped_br' => 'Verschk.',
'singular first char' => 'S',
'sort items' => 'Positionen sortieren',
'stock' => 'Einlagerung',
+ 'stock_br' => 'Eingel.',
'submit' => 'abschicken',
'succeeded' => 'erfolgreich',
+ 'sum' => 'Summe',
'tax_chartaccno' => 'Automatikkonto',
'tax_percent' => 'Prozentsatz',
'tax_rate' => 'Prozent',
'use program settings' => 'benutze Programmeinstellungen',
'use user config' => 'Verwende Benutzereinstellung',
'used' => 'Verbraucht',
+ 'used_br' => 'Verbr.',
'valid from' => 'Gültig ab',
'vendor' => 'Lieferant',
'vendor_invoice_list' => 'kreditorenbuchungsliste',
'waiting for job to be started' => 'warte darauf, dass der Job gestartet wird',
'warehouse_journal_list' => 'lagerbuchungsliste',
'warehouse_report_list' => 'lagerbestandsliste',
+ 'warehouse_usage_list' => 'Lagerentnahmeliste',
'with amount' => 'mit Betrag',
'with skonto acc. to pt' => 'mit Skonto nach ZB',
'with_skonto_pt' => 'mit Skonto nach ZB',
--- /dev/null
+#
+# opendynamic feature
+#
+---
+#
+# Warenverbrauchsbericht
+#
+- parent: warehouse_reports
+ id: warehouse_reports_whusage
+ name: WHUsage
+ icon: warehouse_usage
+ order: 300
+ access: warehouse_contents | warehouse_management
+ params:
+ action: Inventory/stock_usage
--- /dev/null
+[%- PROCESS 'common/paginate.html' pages=SELF.pages, base_url = SELF.base_url %]
--- /dev/null
+[%- USE T8 %]
+[%- USE L %]
+[%- USE HTML %]
+[%- USE LxERP %]
+[%- WAREHOUSE_FILTER = 1 %]
+[%- PROCESS 'common/select_warehouse_bin.html' %]
+
+<h1>[% title | html %]</h1>
+
+[%- INCLUDE 'common/flash.html' %]
+
+<form name="Form" method="post" action="controller.pl">
+
+ <table border="0">
+ <tr>
+ <th class="listheading" align="left" valign="top" colspan="5" nowrap>[% 'Period:' | $T8 %]</th>
+ </tr>
+ <tr>
+ <th align=left><input name=reporttype class=radio type=radio value="custom" checked>[% 'Customized Report' | $T8 %]</th>
+ </tr>
+ <tr>
+ <th colspan=1>[% 'Year' | $T8 %]</th>
+ <td><input name=year size=11 title="[% 'YYYY' | $T8 %]" value="[% year %]" class="initial_focus"></td>
+ </tr>
+ <tr>
+ <td align=right> <b>[% 'Yearly' | $T8 %]</b> </td>
+ <th align=left>[% 'Quarterly' | $T8 %]</th>
+ <th align=left colspan=3>[% 'Monthly' | $T8 %]</th>
+ </tr>
+ <tr>
+ <td align=right> <input name=duetyp class=radio type=radio value="13" checked></td>
+ <td><input name=duetyp class=radio type=radio value="A"> 1. [% 'Quarter' | $T8 %]</td>
+ <td><input name=duetyp class=radio type=radio value="1"> [% 'January' | $T8 %]</td>
+ <td><input name=duetyp class=radio type=radio value="5"> [% 'May' | $T8 %]</td>
+ <td><input name=duetyp class=radio type=radio value="9"> [% 'September' | $T8 %]</td>
+ </tr>
+ <tr>
+ <td align= right> </td>
+ <td><input name=duetyp class=radio type=radio value="B"> 2. [% 'Quarter' | $T8 %]</td>
+ <td><input name=duetyp class=radio type=radio value="2"> [% 'February' | $T8 %]</td>
+ <td><input name=duetyp class=radio type=radio value="6"> [% 'June' | $T8 %]</td>
+ <td><input name=duetyp class=radio type=radio value="10"> [% 'October' | $T8 %]</td>
+ </tr>
+ <tr>
+ <td> </td>
+ <td><input name=duetyp class=radio type=radio value="C"> 3. [% 'Quarter' | $T8 %]</td>
+ <td><input name=duetyp class=radio type=radio value="3"> [% 'March' | $T8 %]</td>
+ <td><input name=duetyp class=radio type=radio value="7"> [% 'July' | $T8 %]</td>
+ <td><input name=duetyp class=radio type=radio value="11"> [% 'November' | $T8 %]</td>
+ </tr>
+ <tr>
+ <td> </td>
+ <td><input name=duetyp class=radio type=radio value="D"> 4. [% 'Quarter' | $T8 %]</td>
+ <td><input name=duetyp class=radio type=radio value="4"> [% 'April' | $T8 %]</td>
+ <td><input name=duetyp class=radio type=radio value="8"> [% 'August' | $T8 %]</td>
+ <td><input name=duetyp class=radio type=radio value="12"> [% 'December' | $T8 %]</td>
+ </tr>
+ <tr>
+ <td colspan="5"><hr size=3 noshade></td>
+ </tr>
+ <tr>
+ <th align=left><input name=reporttype class=radio type=radio value="free">[% 'Free report period' | $T8 %]</th>
+ <td align=left colspan=4>
+ [% 'From' | $T8 %] [% L.date_tag('fromdate', fromdate) %]
+ [% 'Bis' | $T8 %] [% L.date_tag('todate', todate) %]
+ </td>
+ </tr>
+ <tr>
+ <th class="listheading" align="left" valign="top" colspan="5" nowrap>[% 'Filter' | $T8 %]</th>
+ </tr>
+ <tr>
+ <td colspan="5">
+ <table>
+ <tr>
+ <th align="right" nowrap>[% 'Warehouse' | $T8 %]:</th>
+ <td>
+ <select name="warehouse_id" id="warehouse_id" onchange="kivi.PartsWarehouse.warehouseChanged(this.value, 0)">
+ <option value="">---</option>
+ [%- FOREACH warehouse = WAREHOUSES %]
+ <option value="[% HTML.escape(warehouse.id) %]">[% warehouse.description %]</option>
+ [%- END %]
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <th align="right" nowrap>[% 'Bin' | $T8 %]:</th>
+ <td><select name="bin_id" id="bin_id"></select></td>
+ </tr>
+ <tr>
+ <th align="right" nowrap>[% 'Part Number' | $T8 %]:</th>
+ <td><input name="partnumber" size=20></td>
+ </tr>
+ <tr>
+ <th align="right" nowrap>[% 'Part Description' | $T8 %]:</th>
+ <td><input name="description" size=40></td>
+ </tr>
+[% IF PARTSCLASSIFICATIONS %]
+ <tr>
+ <td>
+ [% L.select_tag('partsclassification',PARTSCLASSIFICATION,title_key="partsclassification") %]
+ </td>
+ </tr>
+[% END %]
+ <tr>
+ <th align="right" nowrap>[% 'Charge Number' | $T8 %]:</th>
+ <td><input name="chargenumber" size=40></td>
+ </tr>
+ [% IF INSTANCE_CONF.get_show_bestbefore %]
+ <tr>
+ <th align="right" nowrap>[% 'Best Before' | $T8 %]:</th>
+ <td>
+ [% L.date_tag('bestbefore') %]
+ </td>
+ </tr>
+ [% END %]
+ </table>
+ </td>
+ </tr>
+ </table>
+ </p>
+
+ <p>
+ <input type="hidden" name="action" value="Inventory/dispatch">
+ <input type="submit" id="action_usage" class="submit" name="action_usage" value="[% 'Continue' | $T8 %]">
+ </p>
+ </form>
[%- USE T8 %]
[%- USE HTML %][%- USE LxERP %]
- [%- SET default_margin = LxERP.format_amount(1.5) %]
+ [%- SET default_ymargin = LxERP.format_amount(1.5) %]
+ [%- SET default_xmargin = LxERP.format_amount(0.8) %]
<h1>[% HTML.escape(title) %]</h1>
<tr>
<td align="right">[% 'Top' | $T8 %]</td>
- <td><input name="report_generator_pdf_options_margin_top" size="4" value="[% HTML.escape(default_margin) %]"> cm</td>
+ <td><input name="report_generator_pdf_options_margin_top" size="4" value="[% HTML.escape(default_ymargin) %]"> cm</td>
</tr>
<tr>
<td align="right">[% 'Left' | $T8 %]</td>
- <td><input name="report_generator_pdf_options_margin_left" size="4" value="[% HTML.escape(default_margin) %]"> cm</td>
+ <td><input name="report_generator_pdf_options_margin_left" size="4" value="[% HTML.escape(default_xmargin) %]"> cm</td>
</tr>
<tr>
<td align="right">[% 'Bottom' | $T8 %]</td>
- <td><input name="report_generator_pdf_options_margin_bottom" size="4" value="[% HTML.escape(default_margin) %]"> cm</td>
+ <td><input name="report_generator_pdf_options_margin_bottom" size="4" value="[% HTML.escape(default_ymargin) %]"> cm</td>
</tr>
<tr>
<td align="right">[% 'Right' | $T8 %]</td>
- <td><input name="report_generator_pdf_options_margin_right" size="4" value="[% HTML.escape(default_margin) %]"> cm</td>
+ <td><input name="report_generator_pdf_options_margin_right" size="4" value="[% HTML.escape(default_xmargin) %]"> cm</td>
</tr>
<tr>