X-Git-Url: http://wagnertech.de/git?a=blobdiff_plain;f=bin%2Fmozilla%2Fdo.pl;h=ac71eca9a49d08810703ec1b30589e588a282946;hb=refs%2Fheads%2Forder_controller;hp=7924b05a21c5d52c433423c447503d6aaef2ed88;hpb=5494f687372570c9d1c5eb5c6aad73767e50820a;p=kivitendo-erp.git diff --git a/bin/mozilla/do.pl b/bin/mozilla/do.pl index 7924b05a2..ac71eca9a 100644 --- a/bin/mozilla/do.pl +++ b/bin/mozilla/do.pl @@ -30,6 +30,8 @@ # Delivery orders #====================================================================== +use Carp; +use List::MoreUtils qw(uniq); use List::Util qw(max sum); use POSIX qw(strftime); use YAML; @@ -38,9 +40,10 @@ use SL::DB::DeliveryOrder; use SL::DO; use SL::IR; use SL::IS; +use SL::MoreCommon qw(ary_diff); use SL::ReportGenerator; use SL::WH; - +use Sort::Naturally (); require "bin/mozilla/arap.pl"; require "bin/mozilla/common.pl"; require "bin/mozilla/invoice_io.pl"; @@ -85,6 +88,10 @@ sub add { check_do_access(); + if (($::form->{type} =~ /purchase/) && !$::instance_conf->get_allow_new_purchase_invoice) { + $::form->show_generic_error($::locale->text("You do not have the permissions to access this function.")); + } + my $form = $main::form; set_headings("add"); @@ -166,16 +173,14 @@ sub order_links { $form->all_vc(\%myconfig, $form->{vc}, ($form->{vc} eq 'customer') ? "AR" : "AP"); # retrieve order/quotation - $form->{webdav} = $::lx_office_conf{features}->{webdav}; - $form->{jsscript} = 1; + $form->{webdav} = $::instance_conf->get_webdav; my $editing = $form->{id}; DO->retrieve('vc' => $form->{vc}, 'ids' => $form->{id}); - $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes)); - $form->{shipto} = 1 if $form->{id}; + $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes delivery_term_id currency)); # get customer / vendor if ($form->{vc} eq 'vendor') { @@ -186,7 +191,8 @@ sub order_links { $form->{discount} = $form->{customer_discount}; } - $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id)); + $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id delivery_term_id)); + $form->restore_vars(qw(currency)) if ($form->{id} || $form->{convert_from_oe_ids}); $form->restore_vars(qw(taxincluded)) if $form->{id}; $form->restore_vars(qw(salesman_id)) if $editing; @@ -255,32 +261,46 @@ sub form_header { $form->{employee_id} = $form->{old_employee_id} if $form->{old_employee_id}; $form->{salesman_id} = $form->{old_salesman_id} if $form->{old_salesman_id}; - # use JavaScript Calendar or not - $form->{jsscript} = 1; - - #write Trigger - my $jsscript = Form->write_trigger(\%myconfig, "2", "transdate", "BL", "trigger1", "reqdate", "BL", "trigger2"); - - my @old_project_ids = ($form->{"globalproject_id"}); - map({ push(@old_project_ids, $form->{"project_id_$_"}) - if ($form->{"project_id_$_"}); } (1..$form->{"rowcount"})); - my $vc = $form->{vc} eq "customer" ? "customers" : "vendors"; - $form->get_lists("contacts" => "ALL_CONTACTS", - "shipto" => "ALL_SHIPTO", - "projects" => { - "key" => "ALL_PROJECTS", - "all" => 0, - "old_id" => \@old_project_ids - }, - "employees" => "ALL_EMPLOYEES", - "salesmen" => "ALL_SALESMEN", - $vc => "ALL_VC", + $form->get_lists($vc => "ALL_VC", "price_factors" => "ALL_PRICE_FACTORS", "departments" => "ALL_DEPARTMENTS", "business_types" => "ALL_BUSINESS_TYPES", ); + # Projects + my @old_project_ids = uniq grep { $_ } map { $_ * 1 } ($form->{"globalproject_id"}, map { $form->{"project_id_$_"} } 1..$form->{"rowcount"}); + my @old_ids_cond = @old_project_ids ? (id => \@old_project_ids) : (); + my @customer_cond; + if (($vc eq 'customers') && $::instance_conf->get_customer_projects_only_in_sales) { + @customer_cond = ( + or => [ + customer_id => $::form->{customer_id}, + billable_customer_id => $::form->{customer_id}, + ]); + } + my @conditions = ( + or => [ + and => [ active => 1, @customer_cond ], + @old_ids_cond, + ]); + + $::form->{ALL_PROJECTS} = SL::DB::Manager::Project->get_all_sorted(query => \@conditions); + $::form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{employee_id}, deleted => 0 ] ]); + $::form->{ALL_SALESMEN} = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{salesman_id}, deleted => 0 ] ]); + $::form->{ALL_SHIPTO} = SL::DB::Manager::Shipto->get_all_sorted(query => [ + or => [ trans_id => $::form->{"$::form->{vc}_id"} * 1, and => [ shipto_id => $::form->{shipto_id} * 1, trans_id => undef ] ] + ]); + $::form->{ALL_CONTACTS} = SL::DB::Manager::Contact->get_all_sorted(query => [ + or => [ + cp_cv_id => $::form->{"$::form->{vc}_id"} * 1, + and => [ + cp_cv_id => undef, + cp_id => $::form->{cp_id} * 1 + ] + ] + ]); + map { $_->{value} = "$_->{description}--$_->{id}" } @{ $form->{ALL_DEPARTMENTS} }; map { $_->{value} = "$_->{name}--$_->{id}" } @{ $form->{ALL_VC} }; @@ -289,20 +309,37 @@ sub form_header { $form->{oldvcname} = $form->{"old$form->{vc}"}; $form->{oldvcname} =~ s/--.*//; - $form->{onload} = ""; - if ($form->{resubmit}) { - if ($form->{format} eq "html") { - $form->{onload} = "window.open('about:blank','Beleg'); document.do.target = 'Beleg';"; - } + my $dispatch_to_popup = ''; + if ($form->{resubmit} && ($form->{format} eq "html")) { + $dispatch_to_popup = "window.open('about:blank','Beleg'); document.do.target = 'Beleg';"; + $dispatch_to_popup .= "document.do.submit();"; + } elsif ($form->{resubmit}) { # emulate click for resubmitting actions - $form->{onload} .= "document.do.${_}.click(); " for grep { /^action_/ } keys %$form; - $form->{onload} .= "document.do.submit();" + $dispatch_to_popup = "document.do.${_}.click(); " for grep { /^action_/ } keys %$form; } + $::request->{layout}->add_javascripts_inline("\$(function(){$dispatch_to_popup});"); + + + my $follow_up_vc = $form->{ $form->{vc} eq 'customer' ? 'customer' : 'vendor' }; + $follow_up_vc =~ s/--\d*\s*$//; + + $form->{follow_up_trans_info} = $form->{donumber} .'('. $follow_up_vc .')'; + + $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.SalesPurchase ckeditor/ckeditor ckeditor/adapters/jquery kivi.io autocomplete_customer autocomplete_part)); $form->header(); # Fix für Bug 1082 Erwartet wird: 'abteilungsNAME--abteilungsID' - $form->{department} .= '--' . $form->{department_id}; - + # und Erweiterung für Bug 1760: + # Das war leider nur ein Teil-Fix, da das Verhalten den 'Erneuern'-Knopf + # nicht überlebt. Konsequent jetzt auf L umgestellt + # $ perldoc SL::Template::Plugin::L + # Daher entsprechend nur die Anpassung in form_header + # und in DO.pm gemacht. 4 Testfälle: + # department_id speichern | i.O. + # department_id lesen | i.O. + # department leer überlebt erneuern | i.O. + # department nicht leer überlebt erneuern | i.O. + # $main::lxdebug->message(0, 'ABTEILUNGS ID in form?' . $form->{department_id}); print $form->parse_html_template('do/form_header'); $main::lxdebug->leave_sub(); @@ -316,8 +353,10 @@ sub form_footer { my $form = $main::form; $form->{PRINT_OPTIONS} = print_options('inline' => 1); + $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted(); - print $form->parse_html_template('do/form_footer'); + print $form->parse_html_template('do/form_footer', + {transfer_default => ($::instance_conf->get_transfer_default)}); $main::lxdebug->leave_sub(); } @@ -332,6 +371,8 @@ sub update_delivery_order { set_headings($form->{"id"} ? "edit" : "add"); + $form->{insertdate} = SL::DB::DeliveryOrder->new(id => $form->{id})->load->itime_as_date if $form->{id}; + $form->{update} = 1; my $payment_id; @@ -353,7 +394,6 @@ sub update_delivery_order { # Kunde mit Rabatt 20 -> Rabatt 5,5 i.O. $form->{payment_id} = $payment_id if $form->{payment_id} eq ""; - # for pricegroups my $i = $form->{rowcount}; if ( ($form->{"partnumber_$i"} eq "") @@ -376,21 +416,46 @@ sub update_delivery_order { my $rows = scalar @{ $form->{item_list} }; if ($rows) { - $form->{"qty_$i"} = 1 unless $form->parse_amount(\%myconfig, $form->{"qty_$i"}); + $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"}); + if( !$form->{"qty_$i"} ) { + $form->{"qty_$i"} = 1; + } if ($rows > 1) { - select_item(mode => $mode); + select_item(mode => $mode, pre_entered_qty => $form->{"qty_$i"}); ::end_of_request(); } else { + my $sellprice = $form->parse_amount(\%myconfig, $form->{"sellprice_$i"}); + map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} } keys %{ $form->{item_list}[0] }; $form->{"marge_price_factor_$i"} = $form->{item_list}->[0]->{price_factor}; + + 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; + } + } + $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}); - $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}); + $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}); $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"}); + $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0); } display_form(); @@ -429,14 +494,14 @@ sub search { $form->{vc} = $form->{type} eq 'purchase_delivery_order' ? 'vendor' : 'customer'; - $form->get_lists("projects" => { "key" => "ALL_PROJECTS", - "all" => 1 }, - "employees" => "ALL_EMPLOYEES", - "salesmen" => "ALL_SALESMEN", - "$form->{vc}s" => "ALL_VC"); + $form->get_lists("projects" => { "key" => "ALL_PROJECTS", + "all" => 1 }, + "departments" => "ALL_DEPARTMENTS", + "$form->{vc}s" => "ALL_VC", + "business_types" => "ALL_BUSINESS_TYPES"); + $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]); $form->{SHOW_VC_DROP_DOWN} = $myconfig{vclimit} > scalar @{ $form->{ALL_VC} }; - $form->{jsscript} = 1; $form->{title} = $locale->text('Delivery Orders'); $form->header(); @@ -456,6 +521,7 @@ sub orders { my $locale = $main::locale; my $cgi = $::request->{cgi}; + $form->{department_id} = (split /--/, $form->{department})[-1]; ($form->{ $form->{vc} }, $form->{"$form->{vc}_id"}) = split(/--/, $form->{ $form->{vc} }); report_generator_set_default_sort('transdate', 1); @@ -465,13 +531,14 @@ sub orders { $form->{rowcount} = scalar @{ $form->{DO} }; my @columns = qw( - ids transdate + ids transdate reqdate id donumber - ordnumber - name employee + ordnumber customernumber cusordnumber + name employee salesman shipvia globalprojectnumber - transaction_description + transaction_description department open delivered + insertdate ); $form->{l_open} = $form->{l_closed} = "Y" if ($form->{open} && $form->{closed}); @@ -484,27 +551,35 @@ sub orders { my $report = SL::ReportGenerator->new(\%myconfig, $form); my @hidden_variables = map { "l_${_}" } @columns; - push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber - transaction_description transdatefrom transdateto type vc employee_id salesman_id project_id); + push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber serialnumber cusordnumber + transaction_description transdatefrom transdateto reqdatefrom reqdateto + type vc employee_id salesman_id project_id + insertdatefrom insertdateto business_id); my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables); my %column_defs = ( 'ids' => { 'text' => '', }, - 'transdate' => { 'text' => $locale->text('Date'), }, + 'transdate' => { 'text' => $locale->text('Delivery Order Date'), }, + 'reqdate' => { 'text' => $locale->text('Reqdate'), }, 'id' => { 'text' => $locale->text('ID'), }, 'donumber' => { 'text' => $locale->text('Delivery Order'), }, 'ordnumber' => { 'text' => $locale->text('Order'), }, + 'customernumber' => { 'text' => $locale->text('Customer Number'), }, + 'cusordnumber' => { 'text' => $locale->text('Customer Order Number'), }, 'name' => { 'text' => $form->{vc} eq 'customer' ? $locale->text('Customer') : $locale->text('Vendor'), }, 'employee' => { 'text' => $locale->text('Employee'), }, + 'salesman' => { 'text' => $locale->text('Salesman'), }, 'shipvia' => { 'text' => $locale->text('Ship via'), }, 'globalprojectnumber' => { 'text' => $locale->text('Project Number'), }, 'transaction_description' => { 'text' => $locale->text('Transaction description'), }, 'open' => { 'text' => $locale->text('Open'), }, 'delivered' => { 'text' => $locale->text('Delivered'), }, + 'department' => { 'text' => $locale->text('Department'), }, + 'insertdate' => { 'text' => $locale->text('Insert Date'), }, ); - foreach my $name (qw(id transdate donumber ordnumber name employee shipvia transaction_description)) { + foreach my $name (qw(id transdate reqdate donumber ordnumber name employee salesman shipvia transaction_description department insertdate)) { my $sortdir = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir}; $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir"; } @@ -528,6 +603,9 @@ sub orders { if ($form->{vendor}) { push @options, $locale->text('Vendor') . " : $form->{vendor}"; } + if ($form->{cp_name}) { + push @options, $locale->text('Contact Person') . " : $form->{cp_name}"; + } if ($form->{department}) { my ($department) = split /--/, $form->{department}; push @options, $locale->text('Department') . " : $department"; @@ -538,15 +616,29 @@ sub orders { if ($form->{ordnumber}) { push @options, $locale->text('Order Number') . " : $form->{ordnumber}"; } + push @options, $locale->text('Serial Number') . " : $form->{serialnumber}" if $form->{serialnumber}; + if ($form->{business_id}) { + my $vc_type_label = $form->{vc} eq 'customer' ? $locale->text('Customer type') : $locale->text('Vendor type'); + push @options, $vc_type_label . " : " . SL::DB::Business->new(id => $form->{business_id})->load->description; + } if ($form->{transaction_description}) { push @options, $locale->text('Transaction description') . " : $form->{transaction_description}"; } - if ($form->{transdatefrom}) { - push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1); - } - if ($form->{transdateto}) { - push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1); - } + if ( $form->{transdatefrom} or $form->{transdateto} ) { + push @options, $locale->text('Delivery Order Date'); + push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1) if $form->{transdatefrom}; + push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1) if $form->{transdateto}; + }; + if ( $form->{reqdatefrom} or $form->{reqdateto} ) { + push @options, $locale->text('Reqdate'); + push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1) if $form->{reqdatefrom}; + push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{reqdateto}, 1) if $form->{reqdateto}; + }; + if ( $form->{insertdatefrom} or $form->{insertdateto} ) { + push @options, $locale->text('Insert Date'); + push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1) if $form->{insertdatefrom}; + push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{insertdateto}, 1) if $form->{insertdateto}; + }; if ($form->{open}) { push @options, $locale->text('Open'); } @@ -595,8 +687,7 @@ sub orders { }; $row->{donumber}->{link} = $edit_url . "&id=" . E($dord->{id}) . "&callback=${callback}"; - $row->{ordnumber}->{link} = $edit_order_url . "&id=" . E($dord->{oe_id}) . "&callback=${callback}"; - + $row->{ordnumber}->{link} = $edit_order_url . "&id=" . E($dord->{oe_id}) . "&callback=${callback}" if $dord->{oe_id}; $report->add_data($row); $idx++; @@ -618,6 +709,8 @@ sub save { my %myconfig = %main::myconfig; my $locale = $main::locale; + $form->mtime_ischanged('delivery_orders'); + $form->{defaultcurrency} = $form->get_default_currency(\%myconfig); $form->isblank("transdate", $locale->text('Delivery Order Date missing!')); @@ -641,17 +734,7 @@ sub save { } $form->{id} = 0 if $form->{saveasnew}; - # best case fix für bug 1079. Einkaufsrabatt wird nicht richtig - # aus Lieferantenauftrag -> Lieferschein -> Rechnung übernommen - # Tritt nur auf, wenn man direkt über Lieferschein -> speichern -> - # Workflow Rechnung geht (beim Aufruf über edit() i.O.) - # Gut. DO-save() speichert den Discount im DB-Format 0.12 für - # 12%, die Konvertierung wird leider in $form gemacht und daher - # wird die Maske mit dem falschen Rabatt wieder aufgebaut. - # Wie immer: backup_vars verwenden um nichts anderes kaputt zu - # machen. jan 03.03.2010 - # nicht mehr notwendig da für bug 1284 der backend aufruf entsprechend - # geändert wurde + DO->save(); # saving the history if(!exists $form->{addition}) { @@ -663,8 +746,8 @@ sub save { $form->{simple_save} = 1; if (!$params{no_redirect} && !$form->{print_and_save}) { - set_headings("edit"); - update(); + delete @{$form}{ary_diff([keys %{ $form }], [qw(login id script type cursor_fokus)])}; + edit(); ::end_of_request(); } $main::lxdebug->leave_sub(); @@ -675,25 +758,6 @@ sub delete { check_do_access(); - my $form = $main::form; - my $locale = $main::locale; - - map { delete $form->{$_} } qw(action header login password); - my @variables = map { { 'key' => $_, 'value' => $form->{$_} } } grep { '' eq ref $form->{$_} } keys %{ $form }; - - $form->{title} = $locale->text('Delete delivery order'); - $form->header(); - - print $form->parse_html_template('do/delete', { 'VARIABLES' => \@variables }); - - $main::lxdebug->leave_sub(); -} - -sub delete_delivery_order { - $main::lxdebug->enter_sub(); - - check_do_access(); - my $form = $main::form; my %myconfig = %main::myconfig; my $locale = $main::locale; @@ -724,6 +788,8 @@ sub invoice { my $locale = $main::locale; check_do_access(); + $form->mtime_ischanged('delivery_orders'); + $main::auth->assert($form->{type} eq 'purchase_delivery_order' ? 'vendor_invoice_edit' : 'invoice_edit'); $form->{convert_from_do_ids} = $form->{id}; @@ -760,13 +826,15 @@ sub invoice { } } } - map { $form->{"${_}_${i}"} = $form->parse_amount(\%myconfig, $form->{"${_}_${i}"}) if $form->{"${_}_${i}"} } qw(ship qty sellprice listprice lastcost basefactor); + map { $form->{"${_}_${i}"} = $form->parse_amount(\%myconfig, $form->{"${_}_${i}"}) if $form->{"${_}_${i}"} } qw(ship qty sellprice lastcost basefactor); + $form->{"donumber_$i"} = $form->{donumber}; + $form->{"converted_from_delivery_order_items_id_$i"} = delete $form->{"delivery_order_items_id_$i"}; } $form->{type} = "invoice"; # locale messages - $main::locale = new Locale "$myconfig{countrycode}", "$script"; + $main::locale = Locale->new("$myconfig{countrycode}", "$script"); $locale = $main::locale; require "bin/mozilla/$form->{script}"; @@ -855,6 +923,8 @@ sub invoice_multi { # Hinweis: delete gibt den wert zurueck und loescht danach das element (nett und einfach) # $shell: perldoc perlunc; /delete EXPR $form->{donumber} = delete $form->{donumber_array}; + $form->{ordnumber} = delete $form->{ordnumber_array}; + $form->{cusordnumber} = delete $form->{cusordnumber_array}; $form->{deliverydate} = $form->{transdate}; $form->{transdate} = $form->current_date(\%myconfig); $form->{duedate} = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1); @@ -896,6 +966,7 @@ sub invoice_multi { $ref->{reqdate} ||= $ref->{dord_transdate}; # copy transdates into each invoice row map { $form->{"${_}_$form->{rowcount}"} = $ref->{$_} } keys %{ $ref }; map { $form->{"${_}_$form->{rowcount}"} = $form->format_amount(\%myconfig, $ref->{$_}) } qw(qty sellprice lastcost); + $form->{"converted_from_delivery_order_items_id_$form->{rowcount}"} = delete $form->{"delivery_order_items_id_$form->{rowcount}"}; if ($vc_discount){ # falls wir einen Lieferanten/Kundenrabatt haben # und keinen anderen discount wert an $i ... @@ -910,12 +981,13 @@ sub invoice_multi { } delete $form->{form_details}; - $locale = new Locale "$myconfig{countrycode}", "$script"; + $locale = Locale->new("$myconfig{countrycode}", "$script"); require "bin/mozilla/$form->{script}"; invoice_links(); prepare_invoice(); + display_form(); $main::lxdebug->leave_sub(); @@ -933,8 +1005,8 @@ sub save_as_new { $form->{delivered} = 0; map { delete $form->{$_} } qw(printed emailed queued); delete @{ $form }{ grep { m/^stock_(?:in|out)_\d+/ } keys %{ $form } }; - - # Let Lx-Office assign a new order number if the user hasn't changed the + $form->{"converted_from_delivery_order_items_id_$_"} = delete $form->{"delivery_order_items_id_$_"} for 1 .. $form->{"rowcount"}; + # Let kivitendo assign a new order number if the user hasn't changed the # previous one. If it has been changed manually then use it as-is. $form->{donumber} =~ s/^\s*//g; $form->{donumber} =~ s/\s*$//g; @@ -952,9 +1024,9 @@ sub e_mail { check_do_access(); - my $form = $main::form; + $::form->mtime_ischanged('delivery_orders','mail'); - $form->{print_and_save} = 1; + $::form->{print_and_save} = 1; my $saved_form = save_form(); @@ -984,14 +1056,16 @@ sub calculate_stock_in_out { my $in_out = $form->{type} =~ /^sales/ ? 'out' : 'in'; my $sinfo = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_${i}"}); + my $do_qty = AM->sum_with_unit($::form->{"qty_$i"}, $::form->{"unit_$i"}); my $sum = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $sinfo }); + my $matches = $do_qty == $sum; my $content = $form->format_amount_units('amount' => $sum * 1, 'part_unit' => $form->{"partunit_$i"}, 'amount_unit' => $all_units->{$form->{"partunit_$i"}}->{base_unit}, 'conv_units' => 'convertible_not_smaller', 'max_places' => 2); - $content = qq|${content} |; + $content = qq|${content} |; $main::lxdebug->leave_sub(); @@ -1060,7 +1134,8 @@ sub update_stock_in { foreach my $i (1..$form->{rowcount}) { $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"}); - push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(warehouse_id bin_id chargenumber bestbefore qty unit) }; + push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(warehouse_id bin_id chargenumber + bestbefore qty unit delivery_order_items_stock_id) }; } display_stock_in_form($stock_info); @@ -1089,12 +1164,16 @@ sub display_stock_in_form { my %myconfig = %main::myconfig; my $locale = $main::locale; - $form->{jsscript} = 1; - $form->{title} = $locale->text('Stock'); my $part_info = IC->get_basic_part_info('id' => $form->{parts_id}); + # Standardlagerplatz für Standard-Auslagern verwenden, falls keiner für die Ware explizit definiert wurde + if ($::instance_conf->get_transfer_default_use_master_default_bin) { + $part_info->{warehouse_id} ||= $::instance_conf->get_warehouse_id; + $part_info->{bin_id} ||= $::instance_conf->get_bin_id; + } + my $units = AM->retrieve_units(\%myconfig, $form); # der zweite Parameter von unit_select_data gibt den default-Namen (selected) vor my $units_data = AM->unit_select_data($units, $form->{do_unit}, undef, $part_info->{unit}); @@ -1106,7 +1185,7 @@ sub display_stock_in_form { get_basic_bin_wh_info($stock_info); - $form->header(); + $form->header(no_layout => 1); print $form->parse_html_template('do/stock_in_form', { 'UNITS' => $units_data, 'STOCK_INFO' => $stock_info, 'PART_INFO' => $part_info, }); @@ -1139,15 +1218,20 @@ sub set_stock_in { next if ($form->{"qty_$i"} <= 0); - push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(warehouse_id bin_id chargenumber bestbefore qty unit) }; + push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(delivery_order_items_stock_id warehouse_id bin_id chargenumber bestbefore qty unit) }; } $form->{stock} = YAML::Dump($stock_info); _stock_in_out_set_qty_display($stock_info); + my $do_qty = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit}); + my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info }); + $form->header(); - print $form->parse_html_template('do/set_stock_in_out'); + print $form->parse_html_template('do/set_stock_in_out', { + qty_matches => $do_qty == $transfer_qty, + }); $main::lxdebug->leave_sub(); } @@ -1183,7 +1267,7 @@ sub stock_out_form { ($row->{chargenumber} ne $sinfo->{chargenumber}) || ($row->{bestbefore} ne $sinfo->{bestbefore})); - map { $row->{"stock_$_"} = $sinfo->{$_} } qw(qty unit error); + map { $row->{"stock_$_"} = $sinfo->{$_} } qw(qty unit error delivery_order_items_stock_id); } } @@ -1195,7 +1279,7 @@ sub stock_out_form { } } - $form->header(); + $form->header(no_layout => 1); print $form->parse_html_template('do/stock_out_form', { 'UNITS' => $units_data, 'WHCONTENTS' => $form->{delivered} ? $stock_info : \@contents, 'PART_INFO' => $part_info, }); @@ -1225,6 +1309,7 @@ sub set_stock_out { 'qty' => $form->{"qty_$i"}, 'unit' => $form->{"unit_$i"}, 'row' => $i, + 'delivery_order_items_stock_id' => $form->{"delivery_order_items_stock_id_$i"}, }; } @@ -1241,8 +1326,13 @@ sub set_stock_out { } else { _stock_in_out_set_qty_display($stock_info); + my $do_qty = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit}); + my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info }); + $form->header(); - print $form->parse_html_template('do/set_stock_in_out'); + print $form->parse_html_template('do/set_stock_in_out', { + qty_matches => $do_qty == $transfer_qty, + }); } $main::lxdebug->leave_sub(); @@ -1281,6 +1371,8 @@ sub transfer_in { $request->{parts_id} = $form->{"id_$i"}; $row_sum_base_qty += $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor; + $request->{project_id} = $form->{"project_id_$i"} || $form->{globalproject_id}; + push @all_requests, $request; } @@ -1390,7 +1482,7 @@ sub transfer_out { my $pinfo = $part_info_map{$request->{parts_id}}; my $binfo = $bin_info_map{$request->{bin_id}}; - if ($::lx_office_conf{features}->{show_best_before}) { + if ($::instance_conf->get_show_bestbefore) { push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, #5, for the transfer of #6.", $pinfo->{description}, $binfo->{warehouse_description}, @@ -1465,7 +1557,8 @@ sub dispatcher { my $form = $main::form; my $locale = $main::locale; - foreach my $action (qw(update ship_to print e_mail save transfer_out transfer_in mark_closed save_as_new invoice delete)) { + foreach my $action (qw(update ship_to print e_mail save transfer_out transfer_out_default sort + transfer_in transfer_in_default mark_closed save_as_new invoice delete)) { if ($form->{"action_${action}"}) { call_sub($action); return; @@ -1474,3 +1567,231 @@ sub dispatcher { $form->error($locale->text('No action defined.')); } + +sub transfer_out_default { + $main::lxdebug->enter_sub(); + + my $form = $main::form; + + transfer_in_out_default('direction' => 'out'); + + $main::lxdebug->leave_sub(); +} + +sub transfer_in_default { + $main::lxdebug->enter_sub(); + + my $form = $main::form; + + transfer_in_out_default('direction' => 'in'); + + $main::lxdebug->leave_sub(); +} + +# Falls das Standardlagerverfahren aktiv ist, wird +# geprüft, ob alle Standardlagerplätze für die Auslager- +# artikel vorhanden sind UND ob die Warenmenge ausreicht zum +# Auslagern. Falls nicht wird entsprechend eine Fehlermeldung +# generiert. Offen Chargennummer / bestbefore wird nicht berücksichtigt +sub transfer_in_out_default { + $main::lxdebug->enter_sub(); + + my $form = $main::form; + my %myconfig = %main::myconfig; + my $locale = $main::locale; + my %params = @_; + + my (%missing_default_bins, %qty_parts, @all_requests, %part_info_map, $default_warehouse_id, $default_bin_id); + + Common::check_params(\%params, qw(direction)); + + # entsprechende defaults holen, falls standardlagerplatz verwendet werden soll + if ($::instance_conf->get_transfer_default_use_master_default_bin) { + $default_warehouse_id = $::instance_conf->get_warehouse_id; + $default_bin_id = $::instance_conf->get_bin_id; + } + + + my @part_ids = map { $form->{"id_${_}"} } (1 .. $form->{rowcount}); + if (@part_ids) { + my $units = AM->retrieve_units(\%myconfig, $form); + %part_info_map = IC->get_basic_part_info('id' => \@part_ids); + foreach my $i (1 .. $form->{rowcount}) { + next unless ($form->{"id_$i"}); + my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1; + my $qty = $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor; + + $form->show_generic_error($locale->text("Cannot transfer negative entries." ), 'back_button' => 1) if ($qty < 0); + # if we do not want to transfer services and this part is a service, set qty to zero + # ... and do not create a hash entry in %qty_parts below (will skip check for bins for the transfer == out case) + # ... and push only a empty (undef) element to @all_requests (will skip check for bin_id and warehouse_id and will not alter the row) + + $qty = 0 if (!$::instance_conf->get_transfer_default_services && !defined($part_info_map{$form->{"id_$i"}}->{inventory_accno_id}) && !$part_info_map{$form->{"id_$i"}}->{assembly}); + $qty_parts{$form->{"id_$i"}} += $qty; + if ($qty == 0) { + delete $qty_parts{$form->{"id_$i"}} unless $qty_parts{$form->{"id_$i"}}; + undef $form->{"stock_in_$i"}; + } + + $part_info_map{$form->{"id_$i"}}{bin_id} ||= $default_bin_id; + $part_info_map{$form->{"id_$i"}}{warehouse_id} ||= $default_warehouse_id; + + push @all_requests, ($qty == 0) ? { } : { + 'chargenumber' => '', #?? die müsste entsprechend geholt werden + #'bestbefore' => undef, # TODO wird nicht berücksichtigt + 'bin_id' => $part_info_map{$form->{"id_$i"}}{bin_id}, + 'qty' => $qty, + 'parts_id' => $form->{"id_$i"}, + 'comment' => $locale->text("Default transfer delivery order"), + 'unit' => $part_info_map{$form->{"id_$i"}}{unit}, + 'warehouse_id' => $part_info_map{$form->{"id_$i"}}{warehouse_id}, + 'oe_id' => $form->{id}, + 'project_id' => $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id} + }; + } + + # jetzt wird erst überprüft, ob die Stückzahl entsprechend stimmt. + # check if bin (transfer in and transfer out and qty (transfer out) is correct + foreach my $key (keys %qty_parts) { + + $missing_default_bins{$key}{missing_bin} = 1 unless ($part_info_map{$key}{bin_id}); + next unless ($part_info_map{$key}{bin_id}); # abbruch + + if ($params{direction} eq 'out') { # wird nur für ausgehende Mengen benötigt + my ($max_qty, $error) = WH->get_max_qty_parts_bin(parts_id => $key, bin_id => $part_info_map{$key}{bin_id}); + if ($error == 1) { + # wir können nicht entscheiden, welche charge oder mhd (bestbefore) ausgewählt sein soll + # deshalb rückmeldung nach oben geben, manuell auszulagern + # TODO Bei nur einem Treffer mit Charge oder bestbefore wäre das noch möglich + $missing_default_bins{$key}{chargenumber} = 1; + } + if ($max_qty < $qty_parts{$key}){ + $missing_default_bins{$key}{missing_qty} = $max_qty - $qty_parts{$key}; + } + } + } + } # if @parts_id + + # Abfrage für Fehlerbehandlung (nur bei direction == out) + if (scalar (keys %missing_default_bins)) { + my $fehlertext; + foreach my $fehler (keys %missing_default_bins) { + + my $ware = WH->get_part_description(parts_id => $fehler); + if ($missing_default_bins{$fehler}{missing_bin}){ + $fehlertext .= "Kein Standardlagerplatz definiert bei $ware
"; + } + if ($missing_default_bins{$fehler}{missing_qty}) { # missing_qty + $fehlertext .= "Es fehlen " . $missing_default_bins{$fehler}{missing_qty}*-1 . + " von $ware auf dem Standard-Lagerplatz " . $part_info_map{$fehler}{bin} . " zum Auslagern
"; + } + if ($missing_default_bins{$fehler}{chargenumber}){ + $fehlertext .= "Die Ware hat eine Chargennummer oder eine Mindesthaltbarkeit definiert. + Hier kann man nicht automatisch entscheiden. + Bitte diesen Lieferschein manuell auslagern. + Bei: $ware"; + } + # auslagern soll immer gehen, auch wenn nicht genügend auf lager ist. + # der lagerplatz ist hier extra konfigurierbar, bspw. Lager-Korrektur mit + # Lagerplatz Lagerplatz-Korrektur + my $default_warehouse_id_ignore_onhand = $::instance_conf->get_warehouse_id_ignore_onhand; + my $default_bin_id_ignore_onhand = $::instance_conf->get_bin_id_ignore_onhand; + if ($::instance_conf->get_transfer_default_ignore_onhand && $default_bin_id_ignore_onhand) { + # entsprechende defaults holen + # falls chargenumber, bestbefore oder anzahl nicht stimmt, auf automatischen + # lagerplatz wegbuchen! + foreach (@all_requests) { + if ($_->{parts_id} eq $fehler){ + $_->{bin_id} = $default_bin_id_ignore_onhand; + $_->{warehouse_id} = $default_warehouse_id_ignore_onhand; + } + } + } else { + #$main::lxdebug->message(0, 'Fehlertext: ' . $fehlertext); + $form->show_generic_error($locale->text("Cannot transfer.
Reason:
#1", $fehlertext ), 'back_button' => 1); + } + } + } + + + # hier der eigentliche fallunterschied für in oder out + my $prefix = $params{direction} eq 'in' ? 'in' : 'out'; + + # dieser array_ref ist für DO->save da: + # einmal die all_requests in YAML verwandeln, damit delivery_order_items_stock + # gefüllt werden kann. + my $i = 0; + foreach (@all_requests){ + $i++; + next unless scalar(%{ $_ }); + $form->{"stock_${prefix}_$i"} = YAML::Dump([$_]); + } + + save(no_redirect => 1); # Wir können auslagern, deshalb beleg speichern + # und in delivery_order_items_stock speichern + DO->transfer_in_out('direction' => $prefix, + 'requests' => \@all_requests); + + SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1); + + $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'out'; + $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'in'; + $form->redirect; + +} + +sub sort { + $main::lxdebug->enter_sub(); + + check_do_access(); + + my $form = $main::form; + my %temp_hash; + + croak ("Delivery Order needs to be saved") unless $form->{id}; + + # hashify partnumbers, positions. key is delivery_order_items_id + for my $i (1 .. ($form->{rowcount}) ) { + $temp_hash{$form->{"delivery_order_items_id_$i"}} = { runningnumber => $form->{"runningnumber_$i"}, partnumber => $form->{"partnumber_$i"} }; + } + # naturally sort partnumbers and get a sorted array of doi_ids + my @sorted_doi_ids = sort { Sort::Naturally::ncmp($temp_hash{$a}->{"partnumber"}, $temp_hash{$b}->{"partnumber"}) } keys %temp_hash; + + + my $new_number = 1; + + for (@sorted_doi_ids) { + $form->{"runningnumber_$temp_hash{$_}->{runningnumber}"} = $new_number; + $new_number++; + } + $main::lxdebug->leave_sub(); + save(); +} + +__END__ + +=pod + +=encoding utf8 + +=head1 NAME + +do.pl - Script for all calls to delivery order + + +=head1 FUNCTIONS + +=over 2 + +=item C + +Sorts all position with Natural Sort. Can be activated in form_footer.html like this +Cinput class="submit" type="submit" name="action_sort" id="sort_button" value="[% 'Sort and Save' | $T8 %]"E> + +=back + +=head1 TODO + +Sort and Save can be implemented as an optional button if configuration ca be set by client config. +Example coding for database scripts and templates in (git show af2f24b8), check also +autogeneration for rose (scripts/rose_auto_create_model.pl --h)