use Data::Dumper;
use SL::DBUtils;
-use SL::MoreCommon qw(listify);
+use SL::MoreCommon qw(any listify);
sub get_configs {
$main::lxdebug->enter_sub();
: $cvar->{type} eq 'timestamp' ? $act_var->{timestamp_value}
: $cvar->{type} eq 'number' ? $act_var->{number_value}
: $cvar->{type} eq 'customer' ? $act_var->{number_value}
+ : $cvar->{type} eq 'vendor' ? $act_var->{number_value}
+ : $cvar->{type} eq 'part' ? $act_var->{number_value}
: $cvar->{type} eq 'bool' ? $act_var->{bool_value}
: $act_var->{text_value};
$cvar->{valid} = $valid;
} elsif ($cvar->{type} eq 'customer') {
require SL::DB::Customer;
$cvar->{value} = SL::DB::Manager::Customer->find_by(id => $cvar->{value} * 1);
+ } elsif ($cvar->{type} eq 'vendor') {
+ require SL::DB::Vendor;
+ $cvar->{value} = SL::DB::Manager::Vendor->find_by(id => $cvar->{value} * 1);
+ } elsif ($cvar->{type} eq 'part') {
+ require SL::DB::Part;
+ $cvar->{value} = SL::DB::Manager::Part->find_by(id => $cvar->{value} * 1);
}
}
} elsif ($config->{type} eq 'bool') {
push @values, $value ? 't' : 'f', undef, undef, undef;
- } elsif ($config->{type} eq 'customer') {
+ } elsif (any { $config->{type} eq $_ } qw(customer vendor part)) {
push @values, undef, undef, undef, $value * 1;
}
$not = 'NOT' if ($params{filter}->{$name} eq 'no');
push @sub_where, qq|COALESCE(cvar.bool_value, false) = TRUE|;
- } elsif ($config->{type} eq 'customer') {
+ } elsif (any { $config->{type} eq $_ } qw(customer vendor part)) {
next unless $params{filter}->{$name};
- push @sub_where, qq|cvar.number_value * 1 IN (SELECT id FROM customer WHERE name ILIKE ?)|;
+ my $table = $config->{type};
+ push @sub_where, qq|cvar.number_value * 1 IN (SELECT id FROM $table WHERE name ILIKE ?)|;
+ push @sub_values, "%$params{filter}->{$name}%";
+ } elsif ($config->{type} eq 'part') {
+ next unless $params{filter}->{$name};
+
+ push @sub_where, qq|cvar.number_value * 1 IN (SELECT id FROM parts WHERE partnumber ILIKE ?)|;
push @sub_values, "%$params{filter}->{$name}%";
}
: $cfg->{type} eq 'timestamp' ? $ref->{timestamp_value}
: $cfg->{type} eq 'number' ? $form->format_amount($myconfig, $ref->{number_value} * 1, $cfg->{precision})
: $cfg->{type} eq 'customer' ? (SL::DB::Manager::Customer->find_by(id => 1*$ref->{number_value}) || SL::DB::Customer->new)->name
+ : $cfg->{type} eq 'vendor' ? (SL::DB::Manager::Vendor->find_by(id => 1*$ref->{number_value}) || SL::DB::Vendor->new)->name
+ : $cfg->{type} eq 'part' ? (SL::DB::Manager::Part->find_by(id => 1*$ref->{number_value}) || SL::DB::Part->new)->partnumber
: $cfg->{type} eq 'bool' ? ($ref->{bool_value} ? $locale->text('Yes') : $locale->text('No'))
: $ref->{text_value};
}
--- /dev/null
+package SL::Controller::DeliveryPlan;
+
+use strict;
+use parent qw(SL::Controller::Base);
+
+use Clone qw(clone);
+use SL::DB::OrderItem;
+use SL::Controller::Helper::ParseFilter;
+use SL::Controller::Helper::ReportGenerator;
+
+__PACKAGE__->run_before(sub { $::auth->assert('sales_order_edit'); });
+
+sub action_list {
+ my ($self) = @_;
+ my %list_params = (
+ sort_by => $::form->{sort_by} || 'reqdate',
+ sort_dir => $::form->{sort_dir},
+ filter => $::form->{filter},
+ page => $::form->{page},
+ );
+
+ my $db_args = $self->setup_for_list(%list_params);
+ $self->{pages} = SL::DB::Manager::OrderItem->paginate(%list_params, args => $db_args);
+ $self->{flat_filter} = { map { $_->{key} => $_->{value} } $::form->flatten_variables('filter') };
+ $self->make_filter_summary;
+
+ my $top = $::form->parse_html_template('delivery_plan/report_top', { FORM => $::form, SELF => $self });
+ my $bottom = $::form->parse_html_template('delivery_plan/report_bottom', { SELF => $self });
+
+ $self->prepare_report(
+ report_generator_options => {
+ raw_top_info_text => $top,
+ raw_bottom_info_text => $bottom,
+ controller_class => 'DeliveryPlan',
+ },
+ report_generator_export_options => [
+ 'list', qw(filter sort_by sort_dir),
+ ],
+ db_args => $db_args,
+ );
+
+ $self->{orderitems} = SL::DB::Manager::OrderItem->get_all(%$db_args);
+
+ $self->list_objects;
+}
+
+# private functions
+
+sub setup_for_list {
+ my ($self, %params) = @_;
+ $self->{filter} = {};
+ my %args = (
+ parse_filter(
+ $self->_pre_parse_filter($::form->{filter}, $self->{filter}),
+ with_objects => [ 'order', 'order.customer', 'part' ],
+ launder_to => $self->{filter},
+ ),
+ sort_by => $self->set_sort_params(%params),
+ page => $params{page},
+ );
+
+ $args{query} = [ @{ $args{query} || [] },
+ (
+ 'order.customer_id' => { gt => 0 },
+ 'order.closed' => 0,
+ or => [ 'order.quotation' => 0, 'order.quotation' => undef ],
+
+ # filter by shipped_qty < qty, read from innermost to outermost
+ 'id' => [ \"
+ -- 3. resolve the desired information about those
+ SELECT oi.id FROM (
+ -- 2. slice only part, orderitem and both quantities from it
+ SELECT parts_id, trans_id, qty, SUM(doi_qty) AS doi_qty FROM (
+ -- 1. join orderitems and deliverorder items via record_links.
+ -- also add customer data to filter for sales_orders
+ SELECT oi.parts_id, oi.trans_id, oi.id, oi.qty, doi.qty AS doi_qty
+ FROM orderitems oi, oe, record_links rl, delivery_order_items doi
+ WHERE
+ oe.id = oi.trans_id AND
+ oe.customer_id IS NOT NULL AND
+ (oe.quotation = 'f' OR oe.quotation IS NULL) AND
+ NOT oe.closed AND
+ rl.from_id = oe.id AND
+ rl.from_id = oi.trans_id AND
+ oe.id = oi.trans_id AND
+ rl.from_table = 'oe' AND
+ rl.to_table = 'delivery_orders' AND
+ rl.to_id = doi.delivery_order_id AND
+ oi.parts_id = doi.parts_id
+ ) tuples GROUP BY parts_id, trans_id, qty
+ ) partials
+ LEFT JOIN orderitems oi ON partials.parts_id = oi.parts_id AND partials.trans_id = oi.trans_id
+ WHERE oi.qty > doi_qty
+
+ UNION ALL
+
+ -- 4. since the join over record_links fails for sales_orders wihtout any delivery order
+ -- retrieve those without record_links at all
+ SELECT oi.id FROM orderitems oi, oe
+ WHERE
+ oe.id = oi.trans_id AND
+ oe.customer_id IS NOT NULL AND
+ (oe.quotation = 'f' OR oe.quotation IS NULL) AND
+ NOT oe.closed AND
+ oi.trans_id NOT IN (
+ SELECT from_id
+ FROM record_links rl
+ WHERE
+ rl.from_table ='oe' AND
+ rl.to_table = 'delivery_orders'
+ )
+ " ],
+ )
+ ];
+
+ return \%args;
+}
+
+sub set_sort_params {
+ my ($self, %params) = @_;
+ my $sort_str;
+ ($self->{sort_by}, $self->{sort_dir}, $sort_str) =
+ SL::DB::Manager::OrderItem->make_sort_string(%params);
+ return $sort_str;
+}
+
+sub prepare_report {
+ my ($self, %params) = @_;
+
+ my $objects = $params{objects} || [];
+ my $report = SL::ReportGenerator->new(\%::myconfig, $::form);
+ $self->{report} = $report;
+
+ my @columns = qw(reqdate customer ordnumber partnumber description qty shipped_qty);
+ my @visible = qw(reqdate partnumber description qty shipped_qty ordnumber customer);
+ my @sortable = qw(reqdate partnumber description ordnumber customer);
+
+ my %column_defs = (
+ reqdate => { text => $::locale->text('Reqdate'),
+ sub => sub { $_[0]->reqdate_as_date || $_[0]->order->reqdate_as_date }},
+ description => { text => $::locale->text('Description'),
+ sub => sub { $_[0]->description },
+ obj_link => sub { $self->link_to($_[0]->part) }},
+ partnumber => { text => $::locale->text('Part Number'),
+ sub => sub { $_[0]->part->partnumber },
+ obj_link => sub { $self->link_to($_[0]->part) }},
+ qty => { text => $::locale->text('Qty'),
+ sub => sub { $_[0]->qty_as_number . ' ' . $_[0]->unit }},
+ missing => { text => $::locale->text('Missing qty'),
+ sub => sub { $::form->format_amount(\%::myconfig, $_[0]->qty - $_[0]->shipped_qty, 2) . ' ' . $_[0]->unit }},
+ shipped_qty => { text => $::locale->text('shipped'),
+ sub => sub { $::form->format_amount(\%::myconfig, $_[0]->shipped_qty, 2) . ' ' . $_[0]->unit }},
+ ordnumber => { text => $::locale->text('Order'),
+ sub => sub { $_[0]->order->ordnumber },
+ obj_link => sub { $self->link_to($_[0]->order) }},
+ customer => { text => $::locale->text('Customer'),
+ sub => sub { $_[0]->order->customer->name },
+ obj_link => sub { $self->link_to($_[0]->order->customer) }},
+ );
+
+
+ for my $col (@sortable) {
+ $column_defs{$col}{link} = $self->url_for(
+ action => 'list',
+ sort_by => $col,
+ sort_dir => ($self->{sort_by} eq $col ? 1 - $self->{sort_dir} : $self->{sort_dir}),
+ page => $self->{pages}{cur},
+ %{ $self->{flat_filter} },
+ );
+ }
+
+ map { $column_defs{$_}->{visible} = 1 } @visible;
+
+ $report->set_columns(%column_defs);
+ $report->set_column_order(@columns);
+ $report->set_options(allow_pdf_export => 1, allow_csv_export => 1);
+ $report->set_sort_indicator(%params);
+ $report->set_export_options(@{ $params{report_generator_export_options} || [] });
+ $report->set_options(
+ %{ $params{report_generator_options} || {} },
+ output_format => 'HTML',
+ top_info_text => $::locale->text('Delivery Plan for currently outstanding sales orders'),
+ title => $::locale->text('Delivery Plan'),
+ );
+ $report->set_options_from_form;
+
+ SL::DB::Manager::OrderItem->disable_paginating(args => $params{db_args}) if $report->{options}{output_format} =~ /^(pdf|csv)$/i;
+
+ $self->{report_data} = {
+ column_defs => \%column_defs,
+ columns => \@columns,
+ visible => \@visible,
+ sortable => \@sortable,
+ };
+}
+
+sub list_objects {
+ my ($self) = @_;
+ my $column_defs = $self->{report_data}{column_defs};
+ for my $obj (@{ $self->{orderitems} || [] }) {
+ $self->{report}->add_data({
+ map {
+ $_ => {
+ data => $column_defs->{$_}{sub} ? $column_defs->{$_}{sub}->($obj)
+ : $obj->can($_) ? $obj->$_
+ : $obj->{$_},
+ link => $column_defs->{$_}{obj_link} ? $column_defs->{$_}{obj_link}->($obj) : '',
+ },
+ } @{ $self->{report_data}{columns} || {} }
+ });
+ }
+
+ return $self->{report}->generate_with_headers;
+}
+
+sub make_filter_summary {
+ my ($self) = @_;
+
+ my $filter = $::form->{filter} || {};
+ my @filter_strings;
+
+ my @filters = (
+ [ $filter->{order}{"ordnumber:substr::ilike"}, $::locale->text('Number') ],
+ [ $filter->{part}{"partnumber:substr::ilike"}, $::locale->text('Part Number') ],
+ [ $filter->{"description:substr::ilike"}, $::locale->text('Part Description') ],
+ [ $filter->{"reqdate:date::ge"}, $::locale->text('Delivery Date') . " " . $::locale->text('From Date') ],
+ [ $filter->{"reqdate:date::le"}, $::locale->text('Delivery Date') . " " . $::locale->text('To Date') ],
+ [ $filter->{"qty:number"}, $::locale->text('Quantity') ],
+ [ $filter->{order}{customer}{"name:substr::ilike"}, $::locale->text('Customer') ],
+ [ $filter->{order}{customer}{"customernumber:substr::ilike"}, $::locale->text('Customer Number') ],
+ );
+
+ my @flags = (
+ [ $filter->{part}{type}{part}, $::locale->text('Parts') ],
+ [ $filter->{part}{type}{service}, $::locale->text('Services') ],
+ [ $filter->{part}{type}{assembly}, $::locale->text('Assemblies') ],
+ );
+
+ for (@flags) {
+ push @filter_strings, "$_->[1]" if $_->[0];
+ }
+ for (@filters) {
+ push @filter_strings, "$_->[1]: $_->[0]" if $_->[0];
+ }
+
+ $self->{filter_summary} = join ', ', @filter_strings;
+}
+
+sub link_to {
+ my ($self, $object, %params) = @_;
+
+ return unless $object;
+ my $action = $params{action} || 'edit';
+
+ if ($object->isa('SL::DB::Order')) {
+ my $type = $object->type;
+ my $vc = $object->is_sales ? 'customer' : 'vendor';
+ my $id = $object->id;
+
+ return "oe.pl?action=$action&type=$type&vc=$vc&id=$id";
+ }
+ if ($object->isa('SL::DB::Part')) {
+ my $id = $object->id;
+ return "ic.pl?action=$action&id=$id";
+ }
+ if ($object->isa('SL::DB::Customer')) {
+ my $id = $object->id;
+ return "ct.pl?action=$action&id=$id&db=customer";
+ }
+}
+
+# unfortunately ParseFilter can't handle compount filters.
+# so we clone the original filter (still need that for serializing)
+# rip out the options we know an replace them with the compound options.
+# ParseFilter will take care of the prefixing then.
+sub _pre_parse_filter {
+ my ($self, $orig_filter, $launder_to) = @_;
+
+ return undef unless $orig_filter;
+
+ my $filter = clone($orig_filter);
+ if ($filter->{part} && $filter->{part}{type}) {
+ $launder_to->{part}{type} = delete $filter->{part}{type};
+ my @part_filters = grep $_, map {
+ $launder_to->{part}{type}{$_} ? SL::DB::Manager::Part->type_filter($_) : ()
+ } qw(part service assembly);
+
+ push @{ $filter->{and} }, or => [ @part_filters ] if @part_filters;
+ }
+
+ for my $op (qw(le ge)) {
+ if ($filter->{"reqdate:date::$op"}) {
+ $launder_to->{"reqdate_date__$op"} = delete $filter->{"reqdate:date::$op"};
+ my $parsed_date = DateTime->from_lxoffice($launder_to->{"reqdate_date__$op"});
+ push @{ $filter->{and} }, or => [
+ 'reqdate' => { $op => $parsed_date },
+ and => [
+ 'reqdate' => undef,
+ 'order.reqdate' => { $op => $parsed_date },
+ ]
+ ] if $parsed_date;
+ }
+ }
+
+ if (my $style = delete $filter->{searchstyle}) {
+ $self->{searchstyle} = $style;
+ $launder_to->{searchstyle} = $style;
+ }
+
+ return $filter;
+}
+
+1;
);
my %methods = (
- lt => sub { +{ lt => $_[0] } },
- gt => sub { +{ gt => $_[0] } },
- ilike => sub { +{ ilike => $_[0] } },
- like => sub { +{ like => $_[0] } },
enable => sub { ;;;; },
+ map {
+ # since $_ is an alias it can't be used in a closure. even "".$_ or "$_"
+ # does not work, we need a real copy.
+ my $_copy = "$_";
+ $_ => sub { +{ $_copy => $_[0] } },
+ } qw(similar match imatch regex regexp like ilike rlike is is_not ne eq lt gt le ge),
);
sub parse_filter {
qty => [ 'qty' ],
ordnumber => [ 'order.ordnumber' ],
customer => [ 'lower(customer.name)', ],
- position => [ 'trans_id' ],
- reqdate => [ 'COALESCE(orderitems.reqdate, order.transdate)' ],
+ position => [ 'trans_id', 'runningnumber' ],
+ reqdate => [ 'COALESCE(orderitems.reqdate, order.reqdate)' ],
orddate => [ 'order.orddate' ],
sellprice => [ 'sellprice' ],
discount => [ 'discount' ],
+ transdate => [ 'transdate::date', 'order.reqdate' ],
},
default => [ 'position', 1 ],
nulls => { }
@required_modules = (
{ name => "parent", url => "http://search.cpan.org/~corion/", debian => 'libparent-perl' },
{ name => "Archive::Zip", version => '1.16', url => "http://search.cpan.org/~adamk/", debian => 'libarchive-zip-perl' },
+ { name => "Clone", url => "http://search.cpan.org/~rdf/", debian => 'libclone-perl' },
{ name => "Config::Std", url => "http://search.cpan.org/~dconway/", debian => 'libconfig-std-perl' },
{ name => "DateTime", url => "http://search.cpan.org/~drolsky/", debian => 'libdatetime-perl' },
{ name => "DBI", version => '1.50', url => "http://search.cpan.org/~timb/", debian => 'libdbi-perl' },
@optional_modules = (
{ name => "Digest::SHA", url => "http://search.cpan.org/~mshelor/", debian => 'libdigest-sha-perl' },
+ { name => "IO::Socket::SSL", url => "http://search.cpan.org/~sullr/", debian => 'libio-socket-ssl-perl' },
+ { name => "Net::LDAP", url => "http://search.cpan.org/~gbarr/", debian => 'libnet-ldap-perl' },
);
@developer_modules = (
sub check_for_conditional_dependencies {
return if $conditional_dependencies{net_ldap}++;
- push @required_modules, { 'name' => 'Net::LDAP', 'url' => 'http://search.cpan.org/~gbarr/' }
+ push @required_modules, grep { $_->{name} eq 'Net::LDAP' } @optional_modules
if $::lx_office_conf{authentication} && ($::lx_office_conf{authentication}->{module} eq 'LDAP');
}
use strict;
-use Config::Std;
-use Encode;
-
my $environment_initialized;
+sub safe_require {
+ my ($class, $may_fail);
+ my $failed;
+ $failed = !eval {
+ require Config::Std;
+ require Encode;
+ };
+
+ if ($failed) {
+ if ($may_fail) {
+ warn $@;
+ return 0;
+ } else {
+ die $@;
+ }
+ }
+
+ Config::Std->import;
+ Encode->import;
+
+ return 1;
+}
+
sub read {
- my ($class, $file_name) = @_;
+ my ($class, $file_name, $may_fail) = @_;
- read_config 'config/lx_office.conf.default' => %::lx_office_conf;
+ return unless $class->safe_require($may_fail);
+
+ read_config('config/lx_office.conf.default' => \%::lx_office_conf);
_decode_recursively(\%::lx_office_conf);
$file_name ||= 'config/lx_office.conf';
if (-f $file_name) {
- read_config $file_name => my %local_conf;
+ read_config($file_name => \ my %local_conf);
_decode_recursively(\%local_conf);
_flat_merge(\%::lx_office_conf, \%local_conf);
}
_init_environment();
_determine_application_paths();
+
+ return 1;
}
sub _decode_recursively {
JS
}
+# simple version with select_tag
+sub vendor_selector {
+ my ($self, $name, $value, %params) = @_;
+
+ my $actual_vendor_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"}) ? $::form->{"$name"}->id : $::form->{"$name"}) :
+ (ref $value && $value->can('id')) ? $value->id : '';
+ my $options_str = $self->options_for_select(SL::DB::Manager::Vendor->get_all(),
+ default => $actual_vendor_id,
+ title_sub => sub { $_[0]->vendornumber . " : " . $_[0]->name },
+ 'with_empty' => 1);
+
+ return $self->select_tag($name, $options_str, %params);
+}
+
+
+# simple version with select_tag
+sub part_selector {
+ my ($self, $name, $value, %params) = @_;
+
+ my $actual_part_id = (defined $::form->{"$name"})? ((ref $::form->{"$name"})? $::form->{"$name"}->id : $::form->{"$name"}) :
+ (ref $value && $value->can('id')) ? $value->id : '';
+ my $options_str = $self->options_for_select(SL::DB::Manager::Part->get_all(),
+ default => $actual_part_id,
+ title_sub => sub { $_[0]->partnumber . " : " . $_[0]->description },
+ 'with_empty' => 1);
+
+ return $self->select_tag($name, $options_str, %params);
+}
+
+
sub javascript_tag {
my $self = shift;
my $code = '';
'bool' => $locale->text('Yes/No (Checkbox)'),
'select' => $locale->text('Selection'),
'customer' => $locale->text('Customer'),
+ 'vendor' => $locale->text('Vendor'),
+ 'part' => $locale->text('Part'),
);
-our @types = qw(text textfield number date bool select customer); # timestamp
+our @types = qw(text textfield number date bool select customer vendor part); # timestamp
our @modules = ({ module => 'CT', description => $locale->text('Customers and vendors') },
{ module => 'Contacts', description => $locale->text('Contact persons') },
$::lxdebug->enter_sub;
$::auth->assert('customer_vendor_edit');
-
$::form->{CUSTOM_VARIABLES} = CVar->get_configs('module' => 'Contacts');
($::form->{CUSTOM_VARIABLES_FILTER_CODE},
$::form->{CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables' => $::form->{CUSTOM_VARIABLES},
'filter_prefix' => 'filter.',
'include_value' => 'Y');
+ $::form->{title} = $::locale->text('Search contacts');
$::form->header;
print $::form->parse_html_template('ct/search_contact');
'cp_street' => { 'text' => $::locale->text('Street'), },
'cp_phone1' => { 'text' => $::locale->text('Phone1'), },
'cp_phone2' => { 'text' => $::locale->text('Phone2'), },
- 'cp_mobile1' => { 'text' => $::locale->text('Mobile 1'), },
- 'cp_mobile2' => { 'text' => $::locale->text('Mobile 2'), },
+ 'cp_mobile1' => { 'text' => $::locale->text('Mobile1'), },
+ 'cp_mobile2' => { 'text' => $::locale->text('Mobile2'), },
'cp_email' => { 'text' => $::locale->text('E-mail'), },
'cp_abteilung' => { 'text' => $::locale->text('Department'), },
'cp_birthday' => { 'text' => $::locale->text('Birthday'), },
# set module stuff
if ($ref->{module} eq 'oe') {
- my $edit_oe_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'purchase_order' : 'sales_order'), 'id=' . E($ref->{trans_id}), 'callback');
- $row->{ordnumber}{link} = $edit_oe_link;
- $row->{quonumber}{link} = $edit_oe_link if (!$ref->{ordnumber});
+ # für oe gibt es vier fälle, jeweils nach kunde oder lieferant unterschiedlich:
+ #
+ # | ist bestellt | Vom Kunde bestellt | -> edit_oe_ord_link
+ # | Anfrage | Angebot | -> edit_oe_quo_link
+
+ my $edit_oe_ord_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'purchase_order' : 'sales_order'), 'id=' . E($ref->{trans_id}), 'callback');
+ my $edit_oe_quo_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'request_quotation' : 'sales_quotation'), 'id=' . E($ref->{trans_id}), 'callback');
+
+ $row->{ordnumber}{link} = $edit_oe_ord_link;
+ $row->{quonumber}{link} = $edit_oe_quo_link if (!$ref->{ordnumber});
} else {
$row->{invnumber}{link} = build_std_url("script=$ref->{module}.pl", 'action=edit', 'type=invoice', 'id=' . E($ref->{trans_id}), 'callback');
'Article type (see below)' => 'Artikeltyp (siehe unten)',
'As a result, the saved onhand values of the present goods can be stored into a warehouse designated by you, or will be reset for a proper warehouse tracking' => 'Als Konsequenz können die gespeicherten Mengen entweder in ein Lager überführt werden, oder für eine frische Lagerverwaltung resettet werden.',
'Assemblies' => 'Erzeugnisse',
+ 'Assembly' => 'Erzeugnis',
'Assembly Description' => 'Erzeugnis-Beschreibung',
'Assembly Number' => 'Erzeugnis-Nummer',
'Assembly Number missing!' => 'Erzeugnisnummer fehlt!',
'Delivery Order created' => 'Lieferschein erstellt',
'Delivery Order deleted!' => 'Lieferschein gelöscht!',
'Delivery Orders' => 'Lieferscheine',
+ 'Delivery Orders for this document' => 'Lieferscheine für dieses Dokument',
+ 'Delivery Plan' => 'Lieferplan',
+ 'Delivery Plan for currently outstanding sales orders' => 'Lieferplan für offene Verkaufsaufträge',
+ 'Delivery information deleted.' => 'Lieferinformation gelöscht.',
+ 'Delivery information saved.' => 'Lieferinformation gespeichert.',
'Department' => 'Abteilung',
'Department 1' => 'Abteilung (1)',
'Department 2' => 'Abteilung (2)',
'Help Template Variables' => 'Hilfe zu Dokumenten-Variablen',
'Help on column names' => 'Hilfe zu Spaltennamen',
'Here\'s an example command line:' => 'Hier ist eine Kommandozeile, die als Beispiel dient:',
+ 'Hide Filter' => 'Filter verbergen',
'Hide by default' => 'Standardmäßig verstecken',
'Hide help text' => 'Hilfetext verbergen',
'History' => 'Historie',
'Mitarbeiter' => 'Mitarbeiter',
'Mixed (requires column "type")' => 'Gemischt (erfordert Spalte "type")',
'Mobile' => 'Mobiltelefon',
- 'Mobile 1' => '',
- 'Mobile 2' => '',
- 'Mobile1' => 'Mobile 1',
- 'Mobile2' => 'Mobile 2',
+ 'Mobile1' => 'Mobil 1',
+ 'Mobile2' => 'Mobil 2',
'Model' => 'Lieferanten-Art-Nr.',
'Model (with X being a number)' => 'Lieferanten-Art-Nr. (X ist eine fortlaufende Zahl)',
'Module' => 'Modul',
'Requested execution date from' => 'Gewünschtes Ausführungsdatum von',
'Requested execution date to' => 'Gewünschtes Ausführungsdatum bis',
'Required by' => 'Lieferdatum',
+ 'Reset' => 'Zurücksetzen',
'Restore Dataset' => 'Datenbank wiederherstellen',
'Revenue' => 'Erlöskonto',
'Revenue Account' => 'Erlöskonto',
'Shopartikel' => 'Shopartikel',
'Short' => 'Knapp',
'Show' => 'Zeigen',
+ 'Show Filter' => 'Filter zeigen',
'Show Salesman' => 'Verkäufer anzeigen',
'Show TODO list' => 'Aufgabenliste anzeigen',
'Show by default' => 'Standardmäßig anzeigen',
'Missing user id!' => 'Benutzer ID fehlt!',
'Mitarbeiter' => 'Mitarbeiter',
'Mixed (requires column "type")' => 'Gemischt (erfordert Spalte "type")',
- 'Mobile1' => 'Mobile 1',
- 'Mobile2' => 'Mobile 2',
+ 'Mobile' => 'Mobiltelefon',
+ 'Mobile1' => 'Mobil 1',
+ 'Mobile2' => 'Mobil 2',
'Model' => 'Lieferanten-Art-Nr.',
'Model (with X being a number)' => 'Lieferanten-Art-Nr. (X ist eine fortlaufende Zahl)',
'Module' => 'Modul',
module=dn.pl
action=search
+[AR--Reports--Delivery Plan]
+ACCESS=sales_order_edit
+module=controller.pl
+action=DeliveryPlan/list
[AP]
}
use SL::InstallationCheck;
+use SL::LxOfficeConf;
my %check;
Getopt::Long::Configure ("bundling");
$| = 1;
+if (!SL::LxOfficeConf->read(undef, 'may fail')) {
+ print_header('Could not load the config file. If you have dependancies from any features enabled in the configuration these will still show up as optional because of this. Please rerun this script after installing the dependancies needed to load the cofiguration.')
+} else {
+ SL::InstallationCheck::check_for_conditional_dependencies();
+}
+
if ($check{r}) {
print_header('Checking Required Modules');
check_module($_, required => 1) for @SL::InstallationCheck::required_modules;
[%- ELSIF var.type == 'customer' %]
[% L.customer_picker(var_name, var.value) %]
+[%- ELSIF var.type == 'vendor' %]
+[% L.vendor_selector(var_name, var.value) %]
+
+[%- ELSIF var.type == 'part' %]
+[% L.part_selector(var_name, var.value) %]
+
[%- ELSIF var.type == 'select' %]
<select name="[% var_name %]">
[%- ELSIF cvar.var.type == 'customer' %]
[%- L.customer_picker(render_input_blocks__cvar_name, cvar.value) %]
+[%- ELSIF cvar.var.type == 'vendor' %]
+[% L.vendor_selector(render_input_blocks__cvar_name, cvar.value) %]
+
+[%- ELSIF cvar.var.type == 'part' %]
+[% L.part_selector(render_input_blocks__cvar_name, cvar.value) %]
+
[%- ELSIF cvar.var.type == 'number' %]
[%- L.input_tag(render_input_blocks__cvar_name, LxERP.format_amount(cvar.value, -2)) %]
--- /dev/null
+[%- USE T8 %]
+[%- USE L %]
+[%- USE LxERP %]
+[%- USE HTML %]
+<form action='controller.pl' method='post'>
+<div class='filter_toggle'>
+<a href='#' onClick='javascript:$(".filter_toggle").toggle()'>[% 'Show Filter' | $T8 %]</a>
+ [% SELF.filter_summary %]
+</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">[% 'Number' | $T8 %]</th>
+ <td>[% L.input_tag('filter.order.ordnumber:substr::ilike', filter.order.ordnumber_substr__ilike, size = 20) %]</td>
+ </tr>
+ <tr>
+ <th align="right">[% 'Part Number' | $T8 %]</th>
+ <td>[% L.input_tag('filter.part.partnumber:substr::ilike', filter.part.partnumber_substr__ilike, size = 20) %]</td>
+ </tr>
+ <tr>
+ <th align="right">[% 'Part Description' | $T8 %]</th>
+ <td>[% L.input_tag('filter.description:substr::ilike', filter.description_substr__ilike, size = 20) %]</td>
+ </tr>
+ <tr>
+ <th align="right">[% 'Delivery Date' | $T8 %] [% 'From Date' | $T8 %]</th>
+ <td>[% L.date_tag('filter.reqdate:date::ge', filter.reqdate_date__ge, cal_align = 'BR') %]</td>
+ </tr>
+ <tr>
+ <th align="right">[% 'Delivery Date' | $T8 %] [% 'To Date' | $T8 %]</th>
+ <td>[% L.date_tag('filter.reqdate:date::le', filter.reqdate_date__le, cal_align = 'BR') %]</td>
+ </tr>
+ <tr>
+ <th align="right">[% 'Quantity' | $T8 %]</th>
+ <td>[% L.input_tag('filter.qty:number', filter.qty_number, size = 20) %]</td>
+ </tr>
+ <tr>
+ <th align="right">[% 'Customer' | $T8 %]</th>
+ <td>[% L.input_tag('filter.order.customer.name:substr::ilike', filter.order.customer.name_substr__ilike, size = 20) %]</td>
+ </tr>
+ <tr>
+ <th align="right">[% 'Customer Number' | $T8 %]</th>
+ <td>[% L.input_tag('filter.order.customer.customernumber:substr::ilike', filter.order.customer.customernumber_substr__ilike, size = 20) %]</td>
+ </tr>
+ <tr>
+ <th align="right">[% 'Type' | $T8 %]</th>
+ <td>
+ [% L.checkbox_tag('filter.part.type.part', checked=filter.part.type.part, label=LxERP.t8('Part')) %]
+ [% L.checkbox_tag('filter.part.type.service', checked=filter.part.type.service, label=LxERP.t8('Service')) %]
+ [% L.checkbox_tag('filter.part.type.assembly', checked=filter.part.type.assembly, label=LxERP.t8('Assembly')) %]
+ </td>
+ </tr>
+ </table>
+
+[% L.hidden_tag('action', 'DeliveryPlan/dispatch') %]
+[% L.hidden_tag('sort_by', FORM.sort_by) %]
+[% L.hidden_tag('sort_dir', FORM.sort_dir) %]
+[% L.hidden_tag('page', FORM.page) %]
+[% L.input_tag('action_list', LxERP.t8('Continue'), type = 'submit', class='submit')%]
+
+
+<a href='#' onClick='javascript:$("#filter_table input").attr("value","");$("#filter_table option").attr("selected","")'>[% 'Reset' | $T8 %]</a>
+
+</div>
+
+</form>
--- /dev/null
+[% USE HTML %][% USE T8 %][% USE L %][% USE LxERP %]
+
+[% BLOCK header %]
+ [% SET new_sort_dir = SELF.sort_by == sort_by ? 1 - SELF.sort_dir : SELF.sort_dir %]
+ <th width="[% size %]%">
+ <a href="[% SELF.url_for(action => 'list') %]&sort_by=[% sort_by %]&sort_dir=[% new_sort_dir %]&page=[% FORM.page %]">
+ [%- title %]
+ [%- IF SELF.sort_by == sort_by %]
+ <img src="image/[% IF SELF.sort_dir %]down[% ELSE %]up[% END %].png" border="0">
+ [%- END %]
+ </a>
+ </th>
+[% END %]
+
+<div id="orders">
+[%- IF !SELF.orderitems.size %]
+ <p>[%- 'There are no outstanding deliveries at the moment.' | $T8 %]</p>
+[%- ELSE %]
+
+ <table width=100%>
+ <tr class="listheading">
+ [% PROCESS header title=LxERP.t8('Date') sort_by='transdate', size=15 %]
+ [% PROCESS header title=LxERP.t8('Description') sort_by='description', size=15 %]
+ [% PROCESS header title=LxERP.t8('Part Number') sort_by='partnumber', size=15 %]
+ [% PROCESS header title=LxERP.t8('Qty') sort_by='qty', size=10 %]
+ [% PROCESS header title=LxERP.t8('Order') sort_by='ordnumber', size=10 %]
+ [% PROCESS header title=LxERP.t8('Customer') sort_by='customer', size=10 %]
+ </tr>
+
+ [%- FOREACH row = SELF.orderitems %]
+ <tr class="listrow[% loop.count % 2 %]">
+ <td>[% row.transdate ? row.transdate : row.order.reqdate_as_date %]</td>
+ <td>[% row.part.partnumber | html %]</td>
+ <td>[% row.description | html %]</td>
+ <td class='numeric'>[% LxERP.format_amount(row.qty, 2) | html %]</td>
+ <td>[% row.order.ordnumber | html %]</td>
+ <td>[% row.order.customer.name | html %]</td>
+ </tr>
+ [%- END %]
+ </table>
+ <p align=right>[% PROCESS 'common/paginate.html' pages=SELF.pages, base_url=SELF.url_for(action='list', sort_dir=SELF.sort_dir, sort_by=SELF.sort_by) %]</p>
+
+[%- END %]
+</div>
--- /dev/null
+[%- USE T8 %]
+
+<h1>[% 'Delivery Plan' | $T8 %]</h1>
+
+[%- PROCESS 'delivery_plan/_filter.html' filter=FORM.filter %]
+ <hr>
+[%- PROCESS 'delivery_plan/_list.html' %]
--- /dev/null
+[% SET report_bottom_url_args = {} %]
+[% report_bottom_url_args.import(SELF.flat_filter) %]
+[% report_bottom_url_args.import({action='list', sort_dir=SELF.sort_dir, sort_by=SELF.sort_by}) %]
+<p align=right>[% PROCESS 'common/paginate.html' pages=SELF.pages, base_url=SELF.url_for(report_bottom_url_args) %]</p>
--- /dev/null
+[%- USE L %]
+[%- PROCESS 'delivery_plan/_filter.html' filter=SELF.filter %]
+ <hr>