1 #=====================================================================
 
   4 # Based on SQL-Ledger Version 2.1.9
 
   5 # Web http://www.lx-office.org
 
   7 #=====================================================================
 
   8 # SQL-Ledger, Accounting
 
   9 # Copyright (c) 1998-2003
 
  11 #  Author: Dieter Simader
 
  12 #   Email: dsimader@sql-ledger.org
 
  13 #     Web: http://www.sql-ledger.org
 
  16 # This program is free software; you can redistribute it and/or modify
 
  17 # it under the terms of the GNU General Public License as published by
 
  18 # the Free Software Foundation; either version 2 of the License, or
 
  19 # (at your option) any later version.
 
  21 # This program is distributed in the hope that it will be useful,
 
  22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  24 # GNU General Public License for more details.
 
  25 # You should have received a copy of the GNU General Public License
 
  26 # along with this program; if not, write to the Free Software
 
  27 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
  29 #======================================================================
 
  32 #======================================================================
 
  35 use List::MoreUtils qw(uniq);
 
  36 use List::Util qw(max sum);
 
  37 use POSIX qw(strftime);
 
  40 use SL::DB::DeliveryOrder;
 
  44 use SL::MoreCommon qw(ary_diff restore_form save_form);
 
  45 use SL::ReportGenerator;
 
  47 use Sort::Naturally ();
 
  48 require "bin/mozilla/common.pl";
 
  49 require "bin/mozilla/io.pl";
 
  50 require "bin/mozilla/reportgenerator.pl";
 
  59   $main::auth->assert($main::form->{type} . '_edit');
 
  63   $main::lxdebug->enter_sub();
 
  69   my $form     = $main::form;
 
  70   my $locale   = $main::locale;
 
  72   if ($form->{type} eq 'purchase_delivery_order') {
 
  73     $form->{vc}    = 'vendor';
 
  74     $form->{title} = $action eq "edit" ? $locale->text('Edit Purchase Delivery Order') : $locale->text('Add Purchase Delivery Order');
 
  76     $form->{vc}    = 'customer';
 
  77     $form->{title} = $action eq "edit" ? $locale->text('Edit Sales Delivery Order') : $locale->text('Add Sales Delivery Order');
 
  80   $form->{heading} = $locale->text('Delivery Order');
 
  82   $main::lxdebug->leave_sub();
 
  86   $main::lxdebug->enter_sub();
 
  90   if (($::form->{type} =~ /purchase/) && !$::instance_conf->get_allow_new_purchase_invoice) {
 
  91     $::form->show_generic_error($::locale->text("You do not have the permissions to access this function."));
 
  94   my $form     = $main::form;
 
  98   $form->{show_details} = $::myconfig{show_form_details};
 
  99   $form->{callback} = build_std_url('action=add', 'type', 'vc') unless ($form->{callback});
 
 105   $main::lxdebug->leave_sub();
 
 109   $main::lxdebug->enter_sub();
 
 113   my $form     = $main::form;
 
 115   $form->{show_details} = $::myconfig{show_form_details};
 
 117   # show history button
 
 118   $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
 
 119   #/show hhistory button
 
 121   $form->{simple_save} = 0;
 
 123   set_headings("edit");
 
 125   # editing without stuff to edit? try adding it first
 
 126   if ($form->{rowcount} && !$form->{print_and_save}) {
 
 127 #     map { $id++ if $form->{"multi_id_$_"} } (1 .. $form->{rowcount});
 
 131       undef $form->{rowcount};
 
 133       $main::lxdebug->leave_sub();
 
 136   } elsif (!$form->{id}) {
 
 138     $main::lxdebug->leave_sub();
 
 142   my ($language_id, $printer_id);
 
 143   if ($form->{print_and_save}) {
 
 144     $form->{action}   = "dispatcher";
 
 145     $form->{action_print} = "1";
 
 146     $form->{resubmit} = 1;
 
 147     $language_id      = $form->{language_id};
 
 148     $printer_id       = $form->{printer_id};
 
 151   set_headings("edit");
 
 156   if ($form->{print_and_save}) {
 
 157     $form->{language_id} = $language_id;
 
 158     $form->{printer_id}  = $printer_id;
 
 163   $main::lxdebug->leave_sub();
 
 167   $main::lxdebug->enter_sub();
 
 171   my $form     = $main::form;
 
 172   my %myconfig = %main::myconfig;
 
 174   # retrieve order/quotation
 
 175   my $editing = $form->{id};
 
 177   DO->retrieve('vc'  => $form->{vc},
 
 178                'ids' => $form->{id});
 
 180   $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes delivery_term_id currency));
 
 182   # get customer / vendor
 
 183   if ($form->{vc} eq 'vendor') {
 
 184     IR->get_vendor(\%myconfig, \%$form);
 
 185     $form->{discount} = $form->{vendor_discount};
 
 187     IS->get_customer(\%myconfig, \%$form);
 
 188     $form->{discount} = $form->{customer_discount};
 
 191   $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id delivery_term_id));
 
 192   $form->restore_vars(qw(currency)) if ($form->{id} || $form->{convert_from_oe_ids});
 
 193   $form->restore_vars(qw(taxincluded)) if $form->{id};
 
 194   $form->restore_vars(qw(salesman_id)) if $editing;
 
 196   $main::lxdebug->leave_sub();
 
 200   $main::lxdebug->enter_sub();
 
 204   my $form     = $main::form;
 
 205   my %myconfig = %main::myconfig;
 
 207   $form->{formname} = $form->{type} unless $form->{formname};
 
 210   foreach my $ref (@{ $form->{form_details} }) {
 
 211     $form->{rowcount} = ++$i;
 
 213     map { $form->{"${_}_$i"} = $ref->{$_} } keys %{$ref};
 
 215   for my $i (1 .. $form->{rowcount}) {
 
 217       $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100);
 
 219       $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
 
 221     my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
 
 223     my $decimalplaces = ($dec > 2) ? $dec : 2;
 
 225     # copy reqdate from deliverydate for invoice -> order conversion
 
 226     $form->{"reqdate_$i"} = $form->{"deliverydate_$i"} unless $form->{"reqdate_$i"};
 
 228     $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
 
 229     $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}, $decimalplaces);
 
 231     (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
 
 232     $dec_qty = length $dec_qty;
 
 233     $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
 
 236   $main::lxdebug->leave_sub();
 
 239 sub setup_do_action_bar {
 
 240   my @transfer_qty   = qw(kivi.SalesPurchase.delivery_order_check_transfer_qty);
 
 241   my @req_trans_desc = qw(kivi.SalesPurchase.check_transaction_description) x!!$::instance_conf->get_require_transaction_description_ps;
 
 242   my $is_customer    = $::form->{vc} eq 'customer';
 
 244   for my $bar ($::request->layout->get('actionbar')) {
 
 248           submit    => [ '#form', { action => "update" } ],
 
 249           id        => 'update_button',
 
 250           accesskey => 'enter',
 
 256           submit   => [ '#form', { action => "save" } ],
 
 257           checks   => [ @req_trans_desc ],
 
 258           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 262           submit   => [ '#form', { action => "save_as_new" } ],
 
 263           checks   => [ @req_trans_desc ],
 
 264           disabled => !$::form->{id},
 
 267           t8('Mark as closed'),
 
 268           submit   => [ '#form', { action => "mark_closed" } ],
 
 269           checks   => [ @req_trans_desc ],
 
 270           confirm  => t8('This will remove the delivery order from showing as open even if contents are not delivered. Proceed?'),
 
 271           disabled => !$::form->{id}    ? t8('This record has not been saved yet.')
 
 272                     : $::form->{closed} ? t8('This record has already been closed.')
 
 275       ], # end of combobox "Save"
 
 279         submit   => [ '#form', { action => "delete" } ],
 
 280         confirm  => t8('Do you really want to delete this object?'),
 
 281         disabled => !$::form->{id}                                                                              ? t8('This record has not been saved yet.')
 
 282                   : $::form->{delivered}                                                                        ? t8('This record has already been delivered.')
 
 283                   : ($::form->{vc} eq 'customer' && !$::instance_conf->get_sales_delivery_order_show_delete)    ? t8('Deleting this type of record has been disabled in the configuration.')
 
 284                   : ($::form->{vc} eq 'vendor'   && !$::instance_conf->get_purchase_delivery_order_show_delete) ? t8('Deleting this type of record has been disabled in the configuration.')
 
 291           submit   => [ '#form', { action => "transfer_out" } ],
 
 292           checks   => [ @req_trans_desc, @transfer_qty ],
 
 293           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 294           only_if  => $is_customer,
 
 297           t8('Transfer out via default'),
 
 298           submit   => [ '#form', { action => "transfer_out_default" } ],
 
 299           checks   => [ @req_trans_desc ],
 
 300           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 301           only_if  => $is_customer && $::instance_conf->get_transfer_default,
 
 305           submit   => [ '#form', { action => "transfer_in" } ],
 
 306           checks   => [ @req_trans_desc, @transfer_qty ],
 
 307           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 308           only_if  => !$is_customer,
 
 311           t8('Transfer in via default'),
 
 312           submit   => [ '#form', { action => "transfer_in_default" } ],
 
 313           checks   => [ @req_trans_desc ],
 
 314           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 315           only_if  => !$is_customer && $::instance_conf->get_transfer_default,
 
 317       ], # end of combobox "Transfer out"
 
 324         submit => [ '#form', { action => "invoice" } ],
 
 325         disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
 
 329         action => [ t8('Export') ],
 
 332           call   => [ 'kivi.SalesPurchase.show_print_dialog' ],
 
 333           checks => [ @req_trans_desc ],
 
 337           call   => [ 'kivi.SalesPurchase.show_email_dialog' ],
 
 338           checks => [ @req_trans_desc ],
 
 340       ], # end of combobox "Export"
 
 343         action => [ t8('more') ],
 
 346           call     => [ 'set_history_window', $::form->{id} * 1, 'id' ],
 
 347           disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
 
 351           call     => [ 'follow_up_window' ],
 
 352           disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
 
 354       ], # end if combobox "more"
 
 359 sub setup_do_search_action_bar {
 
 362   for my $bar ($::request->layout->get('actionbar')) {
 
 366         submit    => [ '#form' ],
 
 367         accesskey => 'enter',
 
 373 sub setup_do_orders_action_bar {
 
 376   for my $bar ($::request->layout->get('actionbar')) {
 
 380         submit    => [ '#form', { action => 'invoice_multi' } ],
 
 381         checks    => [ [ 'kivi.check_if_entries_selected', '#form tbody input[type=checkbox]' ] ],
 
 382         accesskey => 'enter',
 
 386         call   => [ 'kivi.SalesPurchase.show_print_dialog', 'js:kivi.MassDeliveryOrderPrint.submitMultiOrders' ],
 
 387         checks => [ [ 'kivi.check_if_entries_selected', '#form tbody input[type=checkbox]' ] ],
 
 394   $main::lxdebug->enter_sub();
 
 398   my $form     = $main::form;
 
 399   my %myconfig = %main::myconfig;
 
 401   my $class       = "SL::DB::" . ($form->{vc} eq 'customer' ? 'Customer' : 'Vendor');
 
 402   $form->{VC_OBJ} = $class->load_cached($form->{ $form->{vc} . '_id' });
 
 404   $form->{CONTACT_OBJ}   = $form->{cp_id} ? SL::DB::Contact->load_cached($form->{cp_id}) : undef;
 
 405   my $current_employee   = SL::DB::Manager::Employee->current;
 
 406   $form->{employee_id}   = $form->{old_employee_id} if $form->{old_employee_id};
 
 407   $form->{salesman_id}   = $form->{old_salesman_id} if $form->{old_salesman_id};
 
 408   $form->{employee_id} ||= $current_employee->id;
 
 409   $form->{salesman_id} ||= $current_employee->id;
 
 411   my $vc = $form->{vc} eq "customer" ? "customers" : "vendors";
 
 412   $form->get_lists("price_factors"  => "ALL_PRICE_FACTORS",
 
 413                    "business_types" => "ALL_BUSINESS_TYPES",
 
 415   $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all;
 
 418   my @old_project_ids = uniq grep { $_ } map { $_ * 1 } ($form->{"globalproject_id"}, map { $form->{"project_id_$_"} } 1..$form->{"rowcount"});
 
 419   my @old_ids_cond    = @old_project_ids ? (id => \@old_project_ids) : ();
 
 421   if (($vc eq 'customers') && $::instance_conf->get_customer_projects_only_in_sales) {
 
 424         customer_id          => $::form->{customer_id},
 
 425         billable_customer_id => $::form->{customer_id},
 
 430       and => [ active => 1, @customer_cond ],
 
 434   $::form->{ALL_PROJECTS}          = SL::DB::Manager::Project->get_all_sorted(query => \@conditions);
 
 435   $::form->{ALL_EMPLOYEES}         = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{employee_id},  deleted => 0 ] ]);
 
 436   $::form->{ALL_SALESMEN}          = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{salesman_id},  deleted => 0 ] ]);
 
 437   $::form->{ALL_SHIPTO}            = SL::DB::Manager::Shipto->get_all_sorted(query => [
 
 438     or => [ trans_id  => $::form->{"$::form->{vc}_id"} * 1, and => [ shipto_id => $::form->{shipto_id} * 1, trans_id => undef ] ]
 
 440   $::form->{ALL_CONTACTS}          = SL::DB::Manager::Contact->get_all_sorted(query => [
 
 442       cp_cv_id => $::form->{"$::form->{vc}_id"} * 1,
 
 445         cp_id    => $::form->{cp_id} * 1
 
 450   my $dispatch_to_popup = '';
 
 451   if ($form->{resubmit} && ($form->{format} eq "html")) {
 
 452     $dispatch_to_popup  = "window.open('about:blank','Beleg'); document.do.target = 'Beleg';";
 
 453     $dispatch_to_popup .= "document.do.submit();";
 
 454   } elsif ($form->{resubmit}) {
 
 455     # emulate click for resubmitting actions
 
 456     $dispatch_to_popup  = "document.do.${_}.click(); " for grep { /^action_/ } keys %$form;
 
 458   $::request->{layout}->add_javascripts_inline("\$(function(){$dispatch_to_popup});");
 
 461   $form->{follow_up_trans_info} = $form->{donumber} .'('. $form->{VC_OBJ}->name .')' if $form->{VC_OBJ};
 
 463   $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.File kivi.MassDeliveryOrderPrint kivi.SalesPurchase kivi.Part kivi.CustomerVendor ckeditor/ckeditor ckeditor/adapters/jquery kivi.io));
 
 466   push @custom_hidden, map { "shiptocvar_" . $_->name } @{ SL::DB::Manager::CustomVariableConfig->get_all(where => [ module => 'ShipTo' ]) };
 
 468   $::form->{HIDDENS} = [ map { +{ name => $_, value => $::form->{$_} } } (@custom_hidden) ];
 
 470   setup_do_action_bar();
 
 473   # Fix für Bug 1082 Erwartet wird: 'abteilungsNAME--abteilungsID'
 
 474   # und Erweiterung für Bug 1760:
 
 475   # Das war leider nur ein Teil-Fix, da das Verhalten den 'Erneuern'-Knopf
 
 476   # nicht überlebt. Konsequent jetzt auf L umgestellt
 
 477   #   $ perldoc SL::Template::Plugin::L
 
 478   # Daher entsprechend nur die Anpassung in form_header
 
 479   # und in DO.pm gemacht. 4 Testfälle:
 
 480   # department_id speichern                 | i.O.
 
 481   # department_id lesen                     | i.O.
 
 482   # department leer überlebt erneuern       | i.O.
 
 483   # department nicht leer überlebt erneuern | i.O.
 
 484   # $main::lxdebug->message(0, 'ABTEILUNGS ID in form?' . $form->{department_id});
 
 485   print $form->parse_html_template('do/form_header');
 
 487   $main::lxdebug->leave_sub();
 
 491   $main::lxdebug->enter_sub();
 
 495   my $form     = $main::form;
 
 497   $form->{PRINT_OPTIONS}      = setup_sales_purchase_print_options();
 
 498   $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
 
 500   print $form->parse_html_template('do/form_footer',
 
 501     {transfer_default         => ($::instance_conf->get_transfer_default)});
 
 503   $main::lxdebug->leave_sub();
 
 506 sub update_delivery_order {
 
 507   $main::lxdebug->enter_sub();
 
 511   my $form     = $main::form;
 
 512   my %myconfig = %main::myconfig;
 
 514   set_headings($form->{"id"} ? "edit" : "add");
 
 516   $form->{insertdate} = SL::DB::DeliveryOrder->new(id => $form->{id})->load->itime_as_date if $form->{id};
 
 521   $payment_id = $form->{payment_id} if $form->{payment_id};
 
 523   my $vc = $form->{vc};
 
 524   if (($form->{"previous_${vc}_id"} || $form->{"${vc}_id"}) != $form->{"${vc}_id"}) {
 
 525     $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
 
 527     IS->get_customer(\%myconfig, $form) if $vc eq 'customer';
 
 528     IR->get_vendor(\%myconfig, $form)   if $vc eq 'vendor';
 
 531   $form->{discount} =  $form->{"$form->{vc}_discount"} if defined $form->{"$form->{vc}_discount"};
 
 532   # Problem: Wenn man ohne Erneuern einen Kunden/Lieferanten
 
 533   # wechselt, wird der entsprechende Kunden/ Lieferantenrabatt
 
 534   # nicht übernommen. Grundproblem: In Commit 82574e78
 
 535   # hab ich aus discount customer_discount und vendor_discount
 
 536   # gemacht und entsprechend an den Oberflächen richtig hin-
 
 537   # geschoben. Die damals bessere Lösung wäre gewesen:
 
 538   # In den Templates nur die hidden für form-discount wieder ein-
 
 539   # setzen dann wäre die Verrenkung jetzt nicht notwendig.
 
 540   # TODO: Ggf. Bugfix 1284, 1575 und 817 wieder zusammenführen
 
 541   # Testfälle: Kunden mit Rabatt 0 -> Rabatt 20 i.O.
 
 542   #            Kunde mit Rabatt 20 -> Rabatt 0  i.O.
 
 543   #            Kunde mit Rabatt 20 -> Rabatt 5,5 i.O.
 
 544   $form->{payment_id} = $payment_id if $form->{payment_id} eq "";
 
 546   my $i = $form->{rowcount};
 
 548   if (   ($form->{"partnumber_$i"} eq "")
 
 549       && ($form->{"description_$i"} eq "")
 
 550       && ($form->{"partsgroup_$i"}  eq "")) {
 
 557     if ($form->{type} eq 'purchase_delivery_order') {
 
 558       IR->retrieve_item(\%myconfig, $form);
 
 561       IS->retrieve_item(\%myconfig, $form);
 
 565     my $rows = scalar @{ $form->{item_list} };
 
 568       $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
 569       if( !$form->{"qty_$i"} ) {
 
 570         $form->{"qty_$i"} = 1;
 
 575         select_item(mode => $mode, pre_entered_qty => $form->{"qty_$i"});
 
 576         $::dispatcher->end_request;
 
 580         my $sellprice = $form->parse_amount(\%myconfig, $form->{"sellprice_$i"});
 
 582         map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} } keys %{ $form->{item_list}[0] };
 
 584         $form->{"marge_price_factor_$i"} = $form->{item_list}->[0]->{price_factor};
 
 587           $form->{"sellprice_$i"} = $sellprice;
 
 589           my $record        = _make_record();
 
 590           my $price_source  = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
 
 591           my $best_price    = $price_source->best_price;
 
 592           my $best_discount = $price_source->best_discount;
 
 595             $::form->{"sellprice_$i"}           = $best_price->price;
 
 596             $::form->{"active_price_source_$i"} = $best_price->source;
 
 598           if ($best_discount) {
 
 599             $::form->{"discount_$i"}               = $best_discount->discount;
 
 600             $::form->{"active_discount_source_$i"} = $best_discount->source;
 
 604         $form->{"sellprice_$i"}          = $form->format_amount(\%myconfig, $form->{"sellprice_$i"});
 
 605         $form->{"lastcost_$i"}           = $form->format_amount(\%myconfig, $form->{"lastcost_$i"});
 
 606         $form->{"qty_$i"}                = $form->format_amount(\%myconfig, $form->{"qty_$i"});
 
 607         $form->{"discount_$i"}           = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0);
 
 614       # ok, so this is a new part
 
 615       # ask if it is a part or service item
 
 617       if (   $form->{"partsgroup_$i"}
 
 618           && ($form->{"partsnumber_$i"} eq "")
 
 619           && ($form->{"description_$i"} eq "")) {
 
 621         $form->{"discount_$i"} = "";
 
 622         $form->{"not_discountable_$i"} = "";
 
 626         $form->{"id_$i"}   = 0;
 
 632   $main::lxdebug->leave_sub();
 
 636   $main::lxdebug->enter_sub();
 
 640   my $form     = $main::form;
 
 641   my %myconfig = %main::myconfig;
 
 642   my $locale   = $main::locale;
 
 644   $form->{vc} = $form->{type} eq 'purchase_delivery_order' ? 'vendor' : 'customer';
 
 646   $form->get_lists("projects"       => { "key" => "ALL_PROJECTS",
 
 648                    "business_types" => "ALL_BUSINESS_TYPES");
 
 649   $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]);
 
 650   $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all;
 
 651   $form->{title}             = $locale->text('Delivery Orders');
 
 653   setup_do_search_action_bar();
 
 657   print $form->parse_html_template('do/search');
 
 659   $main::lxdebug->leave_sub();
 
 663   $main::lxdebug->enter_sub();
 
 667   my $form     = $main::form;
 
 668   my %myconfig = %main::myconfig;
 
 669   my $locale   = $main::locale;
 
 670   my $cgi      = $::request->{cgi};
 
 672   $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.MassDeliveryOrderPrint kivi.SalesPurchase));
 
 673   ($form->{ $form->{vc} }, $form->{"$form->{vc}_id"}) = split(/--/, $form->{ $form->{vc} });
 
 675   report_generator_set_default_sort('transdate', 1);
 
 679   $form->{rowcount} = scalar @{ $form->{DO} };
 
 682     ids                     transdate               reqdate
 
 684     ordnumber               customernumber          cusordnumber
 
 685     name                    employee  salesman
 
 686     shipvia                 globalprojectnumber
 
 687     transaction_description department
 
 692   $form->{l_open}      = $form->{l_closed} = "Y" if ($form->{open}      && $form->{closed});
 
 693   $form->{l_delivered} = "Y"                     if ($form->{delivered} && $form->{notdelivered});
 
 695   $form->{title}       = $locale->text('Delivery Orders');
 
 697   my $attachment_basename = $form->{vc} eq 'vendor' ? $locale->text('purchase_delivery_order_list') : $locale->text('sales_delivery_order_list');
 
 699   my $report = SL::ReportGenerator->new(\%myconfig, $form);
 
 701   my @hidden_variables = map { "l_${_}" } @columns;
 
 702   push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber serialnumber cusordnumber
 
 703                                           transaction_description transdatefrom transdateto reqdatefrom reqdateto
 
 704                                           type vc employee_id salesman_id project_id parts_partnumber parts_description
 
 705                                           insertdatefrom insertdateto business_id);
 
 707   my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
 
 710     'ids'                     => { 'text' => '<input type="checkbox" id="multi_all" value="1">', 'align' => 'center' },
 
 711     'transdate'               => { 'text' => $locale->text('Delivery Order Date'), },
 
 712     'reqdate'                 => { 'text' => $locale->text('Reqdate'), },
 
 713     'id'                      => { 'text' => $locale->text('ID'), },
 
 714     'donumber'                => { 'text' => $locale->text('Delivery Order'), },
 
 715     'ordnumber'               => { 'text' => $locale->text('Order'), },
 
 716     'customernumber'          => { 'text' => $locale->text('Customer Number'), },
 
 717     'cusordnumber'            => { 'text' => $locale->text('Customer Order Number'), },
 
 718     'name'                    => { 'text' => $form->{vc} eq 'customer' ? $locale->text('Customer') : $locale->text('Vendor'), },
 
 719     'employee'                => { 'text' => $locale->text('Employee'), },
 
 720     'salesman'                => { 'text' => $locale->text('Salesman'), },
 
 721     'shipvia'                 => { 'text' => $locale->text('Ship via'), },
 
 722     'globalprojectnumber'     => { 'text' => $locale->text('Project Number'), },
 
 723     'transaction_description' => { 'text' => $locale->text('Transaction description'), },
 
 724     'open'                    => { 'text' => $locale->text('Open'), },
 
 725     'delivered'               => { 'text' => $locale->text('Delivered'), },
 
 726     'department'              => { 'text' => $locale->text('Department'), },
 
 727     'insertdate'              => { 'text' => $locale->text('Insert Date'), },
 
 730   foreach my $name (qw(id transdate reqdate donumber ordnumber name employee salesman shipvia transaction_description department insertdate)) {
 
 731     my $sortdir                 = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
 
 732     $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
 
 735   $form->{"l_type"} = "Y";
 
 736   map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
 
 738   $column_defs{ids}->{visible} = 'HTML';
 
 740   $report->set_columns(%column_defs);
 
 741   $report->set_column_order(@columns);
 
 743   $report->set_export_options('orders', @hidden_variables, qw(sort sortdir));
 
 745   $report->set_sort_indicator($form->{sort}, $form->{sortdir});
 
 748   if ($form->{customer}) {
 
 749     push @options, $locale->text('Customer') . " : $form->{customer}";
 
 751   if ($form->{vendor}) {
 
 752     push @options, $locale->text('Vendor') . " : $form->{vendor}";
 
 754   if ($form->{cp_name}) {
 
 755     push @options, $locale->text('Contact Person') . " : $form->{cp_name}";
 
 757   if ($form->{department_id}) {
 
 758     push @options, $locale->text('Department') . " : " . SL::DB::Department->new(id => $form->{department_id})->load->description;
 
 760   if ($form->{donumber}) {
 
 761     push @options, $locale->text('Delivery Order Number') . " : $form->{donumber}";
 
 763   if ($form->{ordnumber}) {
 
 764     push @options, $locale->text('Order Number') . " : $form->{ordnumber}";
 
 766   push @options, $locale->text('Serial Number') . " : $form->{serialnumber}" if $form->{serialnumber};
 
 767   if ($form->{business_id}) {
 
 768     my $vc_type_label = $form->{vc} eq 'customer' ? $locale->text('Customer type') : $locale->text('Vendor type');
 
 769     push @options, $vc_type_label . " : " . SL::DB::Business->new(id => $form->{business_id})->load->description;
 
 771   if ($form->{transaction_description}) {
 
 772     push @options, $locale->text('Transaction description') . " : $form->{transaction_description}";
 
 774   if ($form->{parts_description}) {
 
 775     push @options, $locale->text('Part Description') . " : $form->{parts_description}";
 
 777   if ($form->{parts_partnumber}) {
 
 778     push @options, $locale->text('Part Number') . " : $form->{parts_partnumber}";
 
 780   if ( $form->{transdatefrom} or $form->{transdateto} ) {
 
 781     push @options, $locale->text('Delivery Order Date');
 
 782     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1)     if $form->{transdatefrom};
 
 783     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{transdateto},   1)     if $form->{transdateto};
 
 785   if ( $form->{reqdatefrom} or $form->{reqdateto} ) {
 
 786     push @options, $locale->text('Reqdate');
 
 787     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1)       if $form->{reqdatefrom};
 
 788     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{reqdateto},   1)       if $form->{reqdateto};
 
 790   if ( $form->{insertdatefrom} or $form->{insertdateto} ) {
 
 791     push @options, $locale->text('Insert Date');
 
 792     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1)    if $form->{insertdatefrom};
 
 793     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{insertdateto},   1)    if $form->{insertdateto};
 
 796     push @options, $locale->text('Open');
 
 798   if ($form->{closed}) {
 
 799     push @options, $locale->text('Closed');
 
 801   if ($form->{delivered}) {
 
 802     push @options, $locale->text('Delivered');
 
 804   if ($form->{notdelivered}) {
 
 805     push @options, $locale->text('Not delivered');
 
 808   my $pr = SL::DB::Manager::Printer->find_by(
 
 809       printer_description => $::locale->text("sales_delivery_order_printer"));
 
 811       $form->{printer_id} = $pr->id;
 
 814   my $print_options = SL::Helper::PrintOptions->get_print_options(
 
 816       hide_language_id => 1,
 
 822   $report->set_options('top_info_text'        => join("\n", @options),
 
 823                        'raw_top_info_text'    => $form->parse_html_template('do/orders_top'),
 
 824                        'raw_bottom_info_text' => $form->parse_html_template('do/orders_bottom', { print_options => $print_options }),
 
 825                        'output_format'        => 'HTML',
 
 826                        'title'                => $form->{title},
 
 827                        'attachment_basename'  => $attachment_basename . strftime('_%Y%m%d', localtime time),
 
 829   $report->set_options_from_form();
 
 830   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
 
 832   # add sort and escape callback, this one we use for the add sub
 
 833   $form->{callback} = $href .= "&sort=$form->{sort}";
 
 835   # escape callback for href
 
 836   my $callback = $form->escape($href);
 
 838   my $edit_url       = build_std_url('action=edit', 'type', 'vc');
 
 839   my $edit_order_url = build_std_url('script=oe.pl', 'type=' . ($form->{type} eq 'sales_delivery_order' ? 'sales_order' : 'purchase_order'), 'action=edit');
 
 843   foreach my $dord (@{ $form->{DO} }) {
 
 844     $dord->{open}      = $dord->{closed}    ? $locale->text('No')  : $locale->text('Yes');
 
 845     $dord->{delivered} = $dord->{delivered} ? $locale->text('Yes') : $locale->text('No');
 
 847     my $row = { map { $_ => { 'data' => $dord->{$_} } } @columns };
 
 849     my $ord_id = $dord->{id};
 
 851       'raw_data' =>   $cgi->hidden('-name' => "trans_id_${idx}", '-value' => $ord_id)
 
 852                     . $cgi->checkbox('-name' => "multi_id_${idx}",' id' => "multi_id_id_".$ord_id, '-value' => 1, '-label' => ''),
 
 853       'valign'   => 'center',
 
 857     $row->{donumber}->{link}  = $edit_url       . "&id=" . E($dord->{id})      . "&callback=${callback}";
 
 858     $row->{ordnumber}->{link} = $edit_order_url . "&id=" . E($dord->{oe_id})   . "&callback=${callback}" if $dord->{oe_id};
 
 859     $report->add_data($row);
 
 864   setup_do_orders_action_bar();
 
 866   $report->generate_with_headers();
 
 868   $main::lxdebug->leave_sub();
 
 872   $main::lxdebug->enter_sub();
 
 878   my $form     = $main::form;
 
 879   my %myconfig = %main::myconfig;
 
 880   my $locale   = $main::locale;
 
 882   $form->mtime_ischanged('delivery_orders');
 
 884   $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
 
 886   $form->isblank("transdate", $locale->text('Delivery Order Date missing!'));
 
 888   $form->{donumber} =~ s/^\s*//g;
 
 889   $form->{donumber} =~ s/\s*$//g;
 
 891   my $msg = ucfirst $form->{vc};
 
 892   $form->isblank($form->{vc} . "_id", $locale->text($msg . " missing!"));
 
 894   # $locale->text('Customer missing!');
 
 895   # $locale->text('Vendor missing!');
 
 897   remove_emptied_rows();
 
 900   # if the name changed get new values
 
 901   my $vc = $form->{vc};
 
 902   if (($form->{"previous_${vc}_id"} || $form->{"${vc}_id"}) != $form->{"${vc}_id"}) {
 
 903     $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
 
 905     IS->get_customer(\%myconfig, $form) if $vc eq 'customer';
 
 906     IR->get_vendor(\%myconfig, $form)   if $vc eq 'vendor';
 
 909     $::dispatcher->end_request;
 
 912   $form->{id} = 0 if $form->{saveasnew};
 
 916   if(!exists $form->{addition}) {
 
 917     $form->{snumbers} = qq|donumber_| . $form->{donumber};
 
 918     $form->{addition} = "SAVED";
 
 921   # /saving the history
 
 923   $form->{simple_save} = 1;
 
 924   if (!$params{no_redirect} && !$form->{print_and_save}) {
 
 925     delete @{$form}{ary_diff([keys %{ $form }], [qw(login id script type cursor_fokus)])};
 
 927     $::dispatcher->end_request;
 
 929   $main::lxdebug->leave_sub();
 
 933   $main::lxdebug->enter_sub();
 
 937   my $form     = $main::form;
 
 938   my %myconfig = %main::myconfig;
 
 939   my $locale   = $main::locale;
 
 943     if(!exists $form->{addition}) {
 
 944       $form->{snumbers} = qq|donumber_| . $form->{donumber};
 
 945       $form->{addition} = "DELETED";
 
 948     # /saving the history
 
 950     $form->info($locale->text('Delivery Order deleted!'));
 
 951     $::dispatcher->end_request;
 
 954   $form->error($locale->text('Cannot delete delivery order!'));
 
 956   $main::lxdebug->leave_sub();
 
 960   $main::lxdebug->enter_sub();
 
 962   my $form     = $main::form;
 
 963   my %myconfig = %main::myconfig;
 
 964   my $locale   = $main::locale;
 
 967   $form->mtime_ischanged('delivery_orders');
 
 969   $main::auth->assert($form->{type} eq 'purchase_delivery_order' ? 'vendor_invoice_edit' : 'invoice_edit');
 
 971   $form->{convert_from_do_ids} = $form->{id};
 
 972   # if we have a reqdate (Liefertermin), this is definetely the preferred
 
 973   # deliverydate for invoices
 
 974   $form->{deliverydate}        = $form->{reqdate} || $form->{transdate};
 
 975   $form->{transdate}           = $form->{invdate} = $form->current_date(\%myconfig);
 
 976   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
 
 977   $form->{defaultcurrency}     = $form->get_default_currency(\%myconfig);
 
 981   delete @{$form}{qw(id closed delivered)};
 
 983   my ($script, $buysell);
 
 984   if ($form->{type} eq 'purchase_delivery_order') {
 
 985     $form->{title}  = $locale->text('Add Vendor Invoice');
 
 986     $form->{script} = 'ir.pl';
 
 991     $form->{title}  = $locale->text('Add Sales Invoice');
 
 992     $form->{script} = 'is.pl';
 
 997   for my $i (1 .. $form->{rowcount}) {
 
 998     map { $form->{"${_}_${i}"} = $form->parse_amount(\%myconfig, $form->{"${_}_${i}"}) if $form->{"${_}_${i}"} } qw(ship qty sellprice lastcost basefactor discount);
 
1000     # adds a customer/vendor discount, unless we have a workflow case
 
1001     # CAVEAT: has to be done, after the above parse_amount
 
1002     unless ($form->{"ordnumber"}) {
 
1003       if ($form->{discount}) { # Falls wir einen Lieferanten-/Kundenrabatt haben
 
1004         # und rabattfähig sind, dann
 
1005         unless ($form->{"not_discountable_$i"}) {
 
1006           $form->{"discount_$i"} = $form->{discount}*100; # ... nehmen wir diesen Rabatt
 
1010     $form->{"donumber_$i"} = $form->{donumber};
 
1011     $form->{"converted_from_delivery_order_items_id_$i"} = delete $form->{"delivery_order_items_id_$i"};
 
1014   $form->{type} = "invoice";
 
1017   $main::locale = Locale->new("$myconfig{countrycode}", "$script");
 
1018   $locale = $main::locale;
 
1020   require "bin/mozilla/$form->{script}";
 
1022   my $currency = $form->{currency};
 
1025   if ($form->{ordnumber}) {
 
1026     require SL::DB::Order;
 
1027     if (my $order = SL::DB::Manager::Order->find_by(ordnumber => $form->{ordnumber})) {
 
1029       $form->{orddate} = $order->transdate_as_date;
 
1030       $form->{$_}      = $order->$_ for qw(payment_id salesman_id taxzone_id quonumber);
 
1034   $form->{currency}     = $currency;
 
1035   $form->{exchangerate} = "";
 
1036   $form->{forex}        = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, $buysell);
 
1037   $form->{exchangerate} = $form->{forex} if ($form->{forex});
 
1042   for my $i (1 .. $form->{rowcount}) {
 
1043     $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
 
1045     my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
 
1047     my $decimalplaces = ($dec > 2) ? $dec : 2;
 
1049     # copy delivery date from reqdate for order -> invoice conversion
 
1050     $form->{"deliverydate_$i"} = $form->{"reqdate_$i"}
 
1051       unless $form->{"deliverydate_$i"};
 
1054     $form->{"sellprice_$i"} =
 
1055       $form->format_amount(\%myconfig, $form->{"sellprice_$i"},
 
1058     $form->{"lastcost_$i"} =
 
1059       $form->format_amount(\%myconfig, $form->{"lastcost_$i"},
 
1062     (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
 
1063     $dec_qty = length $dec_qty;
 
1065       $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
 
1071   $main::lxdebug->leave_sub();
 
1075   $main::lxdebug->enter_sub();
 
1077   my $form     = $main::form;
 
1078   my %myconfig = %main::myconfig;
 
1079   my $locale   = $main::locale;
 
1082   $main::auth->assert($form->{type} eq 'sales_delivery_order' ? 'invoice_edit' : 'vendor_invoice_edit');
 
1084   my @do_ids = map { $form->{"trans_id_$_"} } grep { $form->{"multi_id_$_"} } (1..$form->{rowcount});
 
1086   if (!scalar @do_ids) {
 
1087     $form->show_generic_error($locale->text('You have not selected any delivery order.'));
 
1090   map { delete $form->{$_} } grep { m/^(?:trans|multi)_id_\d+/ } keys %{ $form };
 
1092   if (!DO->retrieve('vc' => $form->{vc}, 'ids' => \@do_ids)) {
 
1093     $form->show_generic_error($form->{vc} eq 'customer' ?
 
1094                               $locale->text('You cannot create an invoice for delivery orders for different customers.') :
 
1095                               $locale->text('You cannot create an invoice for delivery orders from different vendors.'),
 
1096                               'back_button' => 1);
 
1099   my $source_type              = $form->{type};
 
1100   $form->{convert_from_do_ids} = join ' ', @do_ids;
 
1101   # bei der auswahl von mehreren Lieferscheinen fuer eine Rechnung, die einfach in donumber_array
 
1102   # zwischenspeichern (DO.pm) und als ' '-separierte Liste wieder zurueckschreiben
 
1103   # Hinweis: delete gibt den wert zurueck und loescht danach das element (nett und einfach)
 
1104   # $shell: perldoc perlunc; /delete EXPR
 
1105   $form->{donumber}            = delete $form->{donumber_array};
 
1106   $form->{ordnumber}           = delete $form->{ordnumber_array};
 
1107   $form->{cusordnumber}        = delete $form->{cusordnumber_array};
 
1108   $form->{deliverydate}        = $form->{transdate};
 
1109   $form->{transdate}           = $form->current_date(\%myconfig);
 
1110   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
 
1111   $form->{type}                = "invoice";
 
1112   $form->{closed}              = 0;
 
1113   $form->{defaultcurrency}     = $form->get_default_currency(\%myconfig);
 
1115   my ($script, $buysell);
 
1116   if ($source_type eq 'purchase_delivery_order') {
 
1117     $form->{title}  = $locale->text('Add Vendor Invoice');
 
1118     $form->{script} = 'ir.pl';
 
1123     $form->{title}  = $locale->text('Add Sales Invoice');
 
1124     $form->{script} = 'is.pl';
 
1129   map { delete $form->{$_} } qw(id subject message cc bcc printed emailed queued);
 
1131   # get vendor or customer discount
 
1133   my $saved_form = save_form();
 
1134   if ($form->{vc} eq 'vendor') {
 
1135     IR->get_vendor(\%myconfig, \%$form);
 
1136     $vc_discount = $form->{vendor_discount};
 
1138     IS->get_customer(\%myconfig, \%$form);
 
1139     $vc_discount = $form->{customer_discount};
 
1141   # use payment terms from customer or vendor
 
1142   restore_form($saved_form,0,qw(payment_id));
 
1144   $form->{rowcount} = 0;
 
1145   foreach my $ref (@{ $form->{form_details} }) {
 
1146     $form->{rowcount}++;
 
1147     $ref->{reqdate} ||= $ref->{dord_transdate}; # copy transdates into each invoice row
 
1148     map { $form->{"${_}_$form->{rowcount}"} = $ref->{$_} } keys %{ $ref };
 
1149     map { $form->{"${_}_$form->{rowcount}"} = $form->format_amount(\%myconfig, $ref->{$_}) } qw(qty sellprice lastcost);
 
1150     $form->{"converted_from_delivery_order_items_id_$form->{rowcount}"} = delete $form->{"delivery_order_items_id_$form->{rowcount}"};
 
1152     if ($vc_discount){ # falls wir einen Lieferanten/Kundenrabatt haben
 
1153       # und keinen anderen discount wert an $i ...
 
1154       $form->{"discount_$form->{rowcount}"} ||= $vc_discount; # ... nehmen wir diesen Rabatt
 
1157     $form->{"discount_$form->{rowcount}"}   = $form->{"discount_$form->{rowcount}"}  * 100; #s.a. Bug 1151
 
1158     # Anm.: Eine Änderung des discounts in der SL/DO.pm->retrieve (select (doi.discount * 100) as discount) ergibt in psql einen
 
1159     # Wert von 10.0000001490116. Ferner ist der Rabatt in der Rechnung dann bei 1.0 (?). Deswegen lasse ich das hier. jb 10.10.09
 
1161     $form->{"discount_$form->{rowcount}"} = $form->format_amount(\%myconfig, $form->{"discount_$form->{rowcount}"});
 
1163   delete $form->{form_details};
 
1165   $locale = Locale->new("$myconfig{countrycode}", "$script");
 
1167   require "bin/mozilla/$form->{script}";
 
1174   $main::lxdebug->leave_sub();
 
1178   $main::lxdebug->enter_sub();
 
1182   my $form     = $main::form;
 
1184   $form->{saveasnew} = 1;
 
1185   $form->{closed}    = 0;
 
1186   $form->{delivered} = 0;
 
1187   map { delete $form->{$_} } qw(printed emailed queued);
 
1188   delete @{ $form }{ grep { m/^stock_(?:in|out)_\d+/ } keys %{ $form } };
 
1189   $form->{"converted_from_delivery_order_items_id_$_"} = delete $form->{"delivery_order_items_id_$_"} for 1 .. $form->{"rowcount"};
 
1190   # Let kivitendo assign a new order number if the user hasn't changed the
 
1191   # previous one. If it has been changed manually then use it as-is.
 
1192   $form->{donumber} =~ s/^\s*//g;
 
1193   $form->{donumber} =~ s/\s*$//g;
 
1194   if ($form->{saved_donumber} && ($form->{saved_donumber} eq $form->{donumber})) {
 
1195     delete($form->{donumber});
 
1200   $main::lxdebug->leave_sub();
 
1203 sub calculate_stock_in_out {
 
1204   $main::lxdebug->enter_sub();
 
1206   my $form     = $main::form;
 
1210   if (!$form->{"id_${i}"}) {
 
1211     $main::lxdebug->leave_sub();
 
1215   my $all_units = AM->retrieve_all_units();
 
1217   my $in_out   = $form->{type} =~ /^sales/ ? 'out' : 'in';
 
1218   my $sinfo    = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_${i}"});
 
1220   my $do_qty   = AM->sum_with_unit($::form->{"qty_$i"}, $::form->{"unit_$i"});
 
1221   my $sum      = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $sinfo });
 
1222   my $matches  = $do_qty == $sum;
 
1224   my $content  = $form->format_amount_units('amount'      => $sum * 1,
 
1225                                             'part_unit'   => $form->{"partunit_$i"},
 
1226                                             'amount_unit' => $all_units->{$form->{"partunit_$i"}}->{base_unit},
 
1227                                             'conv_units'  => 'convertible_not_smaller',
 
1229   $content     = qq|<span id="stock_in_out_qty_display_${i}">${content}</span><input type=hidden id='stock_in_out_qty_matches_$i' value='$matches'> <input type="button" onclick="open_stock_in_out_window('${in_out}', $i);" value="?">|;
 
1231   $main::lxdebug->leave_sub();
 
1236 sub get_basic_bin_wh_info {
 
1237   $main::lxdebug->enter_sub();
 
1239   my $stock_info = shift;
 
1241   my $form     = $main::form;
 
1243   foreach my $sinfo (@{ $stock_info }) {
 
1244     next unless ($sinfo->{bin_id});
 
1246     my $bin_info = WH->get_basic_bin_info('id' => $sinfo->{bin_id});
 
1247     map { $sinfo->{"${_}_description"} = $sinfo->{"${_}description"} = $bin_info->{"${_}_description"} } qw(bin warehouse);
 
1250   $main::lxdebug->leave_sub();
 
1253 sub stock_in_out_form {
 
1254   $main::lxdebug->enter_sub();
 
1256   my $form     = $main::form;
 
1258   if ($form->{in_out} eq 'out') {
 
1264   $main::lxdebug->leave_sub();
 
1267 sub redo_stock_info {
 
1268   $main::lxdebug->enter_sub();
 
1272   my $form     = $main::form;
 
1274   my @non_empty = grep { $_->{qty} } @{ $params{stock_info} };
 
1276   if ($params{add_empty_row}) {
 
1278       'warehouse_id' => scalar(@non_empty) ? $non_empty[-1]->{warehouse_id} : undef,
 
1279       'bin_id'       => scalar(@non_empty) ? $non_empty[-1]->{bin_id}       : undef,
 
1283   @{ $params{stock_info} } = @non_empty;
 
1285   $main::lxdebug->leave_sub();
 
1288 sub update_stock_in {
 
1289   $main::lxdebug->enter_sub();
 
1291   my $form     = $main::form;
 
1292   my %myconfig = %main::myconfig;
 
1294   my $stock_info = [];
 
1296   foreach my $i (1..$form->{rowcount}) {
 
1297     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1298     push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(warehouse_id bin_id chargenumber
 
1299                                                                    bestbefore qty unit delivery_order_items_stock_id) };
 
1302   display_stock_in_form($stock_info);
 
1304   $main::lxdebug->leave_sub();
 
1308   $main::lxdebug->enter_sub();
 
1310   my $form     = $main::form;
 
1312   my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
 
1314   display_stock_in_form($stock_info);
 
1316   $main::lxdebug->leave_sub();
 
1319 sub display_stock_in_form {
 
1320   $main::lxdebug->enter_sub();
 
1322   my $stock_info = shift;
 
1324   my $form     = $main::form;
 
1325   my %myconfig = %main::myconfig;
 
1326   my $locale   = $main::locale;
 
1328   $form->{title} = $locale->text('Stock');
 
1330   my $part_info  = IC->get_basic_part_info('id' => $form->{parts_id});
 
1332   # Standardlagerplatz für Standard-Auslagern verwenden, falls keiner für die Ware explizit definiert wurde
 
1333   if ($::instance_conf->get_transfer_default_use_master_default_bin) {
 
1334     $part_info->{warehouse_id} ||= $::instance_conf->get_warehouse_id;
 
1335     $part_info->{bin_id}       ||= $::instance_conf->get_bin_id;
 
1338   my $units      = AM->retrieve_units(\%myconfig, $form);
 
1339   # der zweite Parameter von unit_select_data gibt den default-Namen (selected) vor
 
1340   my $units_data = AM->unit_select_data($units, $form->{do_unit}, undef, $part_info->{unit});
 
1342   $form->get_lists('warehouses' => { 'key'    => 'WAREHOUSES',
 
1343                                      'bins'   => 'BINS' });
 
1345   redo_stock_info('stock_info' => $stock_info, 'add_empty_row' => !$form->{delivered});
 
1347   get_basic_bin_wh_info($stock_info);
 
1349   $form->header(no_layout => 1);
 
1350   print $form->parse_html_template('do/stock_in_form', { 'UNITS'      => $units_data,
 
1351                                                          'STOCK_INFO' => $stock_info,
 
1352                                                          'PART_INFO'  => $part_info, });
 
1354   $main::lxdebug->leave_sub();
 
1357 sub _stock_in_out_set_qty_display {
 
1358   my $stock_info       = shift;
 
1360   my $all_units        = AM->retrieve_all_units();
 
1361   my $sum              = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1362   $form->{qty_display} = $form->format_amount_units(amount      => $sum * 1,
 
1363                                                     part_unit   => $form->{partunit},
 
1364                                                     amount_unit => $all_units->{ $form->{partunit} }->{base_unit},
 
1365                                                     conv_units  => 'convertible_not_smaller',
 
1370   $main::lxdebug->enter_sub();
 
1372   my $form     = $main::form;
 
1373   my %myconfig = %main::myconfig;
 
1375   my $stock_info = [];
 
1377   foreach my $i (1..$form->{rowcount}) {
 
1378     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1380     next if ($form->{"qty_$i"} <= 0);
 
1382     push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(delivery_order_items_stock_id warehouse_id bin_id chargenumber bestbefore qty unit) };
 
1385   $form->{stock} = YAML::Dump($stock_info);
 
1387   _stock_in_out_set_qty_display($stock_info);
 
1389   my $do_qty       = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
 
1390   my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1393   print $form->parse_html_template('do/set_stock_in_out', {
 
1394     qty_matches => $do_qty == $transfer_qty,
 
1397   $main::lxdebug->leave_sub();
 
1400 sub stock_out_form {
 
1401   $main::lxdebug->enter_sub();
 
1403   my $form     = $main::form;
 
1404   my %myconfig = %main::myconfig;
 
1405   my $locale   = $main::locale;
 
1407   $form->{title} = $locale->text('Release From Stock');
 
1409   my $part_info  = IC->get_basic_part_info('id' => $form->{parts_id});
 
1411   my $units      = AM->retrieve_units(\%myconfig, $form);
 
1412   my $units_data = AM->unit_select_data($units, undef, undef, $part_info->{unit});
 
1414   my @contents   = DO->get_item_availability('parts_id' => $form->{parts_id});
 
1416   my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
 
1418   if (!$form->{delivered}) {
 
1419     foreach my $row (@contents) {
 
1420       $row->{available_qty} = $form->format_amount_units('amount'      => $row->{qty} * 1,
 
1421                                                          'part_unit'   => $part_info->{unit},
 
1422                                                          'conv_units'  => 'convertible_not_smaller',
 
1425       foreach my $sinfo (@{ $stock_info }) {
 
1426         next if (($row->{bin_id}       != $sinfo->{bin_id}) ||
 
1427                  ($row->{warehouse_id} != $sinfo->{warehouse_id}) ||
 
1428                  ($row->{chargenumber} ne $sinfo->{chargenumber}) ||
 
1429                  ($row->{bestbefore}   ne $sinfo->{bestbefore}));
 
1431         map { $row->{"stock_$_"} = $sinfo->{$_} } qw(qty unit error delivery_order_items_stock_id);
 
1436     get_basic_bin_wh_info($stock_info);
 
1438     foreach my $sinfo (@{ $stock_info }) {
 
1439       map { $sinfo->{"stock_$_"} = $sinfo->{$_} } qw(qty unit);
 
1443   $form->header(no_layout => 1);
 
1444   print $form->parse_html_template('do/stock_out_form', { 'UNITS'      => $units_data,
 
1445                                                           'WHCONTENTS' => $form->{delivered} ? $stock_info : \@contents,
 
1446                                                           'PART_INFO'  => $part_info, });
 
1448   $main::lxdebug->leave_sub();
 
1452   $main::lxdebug->enter_sub();
 
1454   my $form     = $main::form;
 
1455   my %myconfig = %main::myconfig;
 
1456   my $locale   = $main::locale;
 
1458   my $stock_info = [];
 
1460   foreach my $i (1 .. $form->{rowcount}) {
 
1461     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1463     next if ($form->{"qty_$i"} <= 0);
 
1465     push @{ $stock_info }, {
 
1466       'warehouse_id' => $form->{"warehouse_id_$i"},
 
1467       'bin_id'       => $form->{"bin_id_$i"},
 
1468       'chargenumber' => $form->{"chargenumber_$i"},
 
1469       'bestbefore'   => $form->{"bestbefore_$i"},
 
1470       'qty'          => $form->{"qty_$i"},
 
1471       'unit'         => $form->{"unit_$i"},
 
1473       'delivery_order_items_stock_id'  => $form->{"delivery_order_items_stock_id_$i"},
 
1477   my @errors     = DO->check_stock_availability('requests' => $stock_info,
 
1478                                                 'parts_id' => $form->{parts_id});
 
1480   $form->{stock} = YAML::Dump($stock_info);
 
1483     $form->{ERRORS} = [];
 
1484     map { push @{ $form->{ERRORS} }, $locale->text('Error in row #1: The quantity you entered is bigger than the stocked quantity.', $_->{row}); } @errors;
 
1485     stock_in_out_form();
 
1488     _stock_in_out_set_qty_display($stock_info);
 
1490     my $do_qty       = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
 
1491     my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1494     print $form->parse_html_template('do/set_stock_in_out', {
 
1495       qty_matches => $do_qty == $transfer_qty,
 
1499   $main::lxdebug->leave_sub();
 
1503   $main::lxdebug->enter_sub();
 
1505   my $form     = $main::form;
 
1506   my %myconfig = %main::myconfig;
 
1507   my $locale   = $main::locale;
 
1509   if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
 
1510     $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred in.'));
 
1513   save(no_redirect => 1);
 
1515   my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_in_${_}"} } (1 .. $form->{rowcount});
 
1519     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1520     my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1523     $form->{ERRORS}   = [];
 
1525     foreach my $i (1 .. $form->{rowcount}) {
 
1526       next unless ($form->{"id_$i"} && $form->{"stock_in_$i"});
 
1528       my $row_sum_base_qty = 0;
 
1529       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1531       foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_in_$i"}) }) {
 
1532         $request->{parts_id}  = $form->{"id_$i"};
 
1533         $row_sum_base_qty    += $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
 
1535         $request->{project_id} = $form->{"project_id_$i"} || $form->{globalproject_id};
 
1537         push @all_requests, $request;
 
1540       next if (0 == $row_sum_base_qty);
 
1542       my $do_base_qty = $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1544 #      if ($do_base_qty != $row_sum_base_qty) {
 
1545 #        push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no stock at all or the full quantity of #2 #3.',
 
1546 #                                                 $i, $form->{"qty_$i"}, $form->{"unit_$i"});
 
1550     if (@{ $form->{ERRORS} }) {
 
1551       push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
 
1553       set_headings('edit');
 
1555       $main::lxdebug->leave_sub();
 
1557       $::dispatcher->end_request;
 
1561   DO->transfer_in_out('direction' => 'in',
 
1562                       'requests'  => \@all_requests);
 
1564   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1566   $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id});
 
1569   $main::lxdebug->leave_sub();
 
1573   $main::lxdebug->enter_sub();
 
1575   my $form     = $main::form;
 
1576   my %myconfig = %main::myconfig;
 
1577   my $locale   = $main::locale;
 
1579   if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
 
1580     $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred out.'));
 
1583   save(no_redirect => 1);
 
1585   my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_out_${_}"} } (1 .. $form->{rowcount});
 
1589     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1590     my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1593     $form->{ERRORS}   = [];
 
1595     foreach my $i (1 .. $form->{rowcount}) {
 
1596       next unless ($form->{"id_$i"} && $form->{"stock_out_$i"});
 
1598       my $row_sum_base_qty = 0;
 
1599       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1601       foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_out_$i"}) }) {
 
1602         $request->{parts_id} = $form->{"id_$i"};
 
1603         $request->{base_qty} = $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
 
1604         $request->{project_id} = $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id};
 
1606         my $map_key          = join '--', ($form->{"id_$i"}, @{$request}{qw(warehouse_id bin_id chargenumber bestbefore)});
 
1608         $request_map{$map_key}                 ||= $request;
 
1609         $request_map{$map_key}->{sum_base_qty} ||= 0;
 
1610         $request_map{$map_key}->{sum_base_qty}  += $request->{base_qty};
 
1611         $row_sum_base_qty                       += $request->{base_qty};
 
1613         push @all_requests, $request;
 
1616       next if (0 == $row_sum_base_qty);
 
1618       my $do_base_qty = $form->{"qty_$i"} * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1620 #      if ($do_base_qty != $row_sum_base_qty) {
 
1621 #        push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no transfer at all or the full quantity of #2 #3.',
 
1622 #                                                 $i, $form->{"qty_$i"}, $form->{"unit_$i"});
 
1627       my @bin_ids      = map { $_->{bin_id} } values %request_map;
 
1628       my %bin_info_map = WH->get_basic_bin_info('id' => \@bin_ids);
 
1629       my @contents     = DO->get_item_availability('parts_id' => \@part_ids);
 
1631       foreach my $inv (@contents) {
 
1632         my $map_key = join '--', @{$inv}{qw(parts_id warehouse_id bin_id chargenumber bestbefore)};
 
1634         next unless ($request_map{$map_key});
 
1636         my $request    = $request_map{$map_key};
 
1637         $request->{ok} = $request->{sum_base_qty} <= $inv->{qty};
 
1640       foreach my $request (values %request_map) {
 
1641         next if ($request->{ok});
 
1643         my $pinfo = $part_info_map{$request->{parts_id}};
 
1644         my $binfo = $bin_info_map{$request->{bin_id}};
 
1646         if ($::instance_conf->get_show_bestbefore) {
 
1647             push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, #5, for the transfer of #6.",
 
1648                                                      $pinfo->{description},
 
1649                                                      $binfo->{warehouse_description},
 
1650                                                      $binfo->{bin_description},
 
1651                                                      $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
 
1652                                                      $request->{bestbefore} ? $locale->text('bestbefore #1', $request->{bestbefore}) : $locale->text('no bestbefore'),
 
1653                                                      $form->format_amount_units('amount'      => $request->{sum_base_qty},
 
1654                                                                                 'part_unit'   => $pinfo->{unit},
 
1655                                                                                 'conv_units'  => 'convertible_not_smaller'));
 
1657             push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, for the transfer of #5.",
 
1658                                                      $pinfo->{description},
 
1659                                                      $binfo->{warehouse_description},
 
1660                                                      $binfo->{bin_description},
 
1661                                                      $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
 
1662                                                      $form->format_amount_units('amount'      => $request->{sum_base_qty},
 
1663                                                                                 'part_unit'   => $pinfo->{unit},
 
1664                                                                                 'conv_units'  => 'convertible_not_smaller'));
 
1669     if (@{ $form->{ERRORS} }) {
 
1670       push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
 
1672       set_headings('edit');
 
1674       $main::lxdebug->leave_sub();
 
1676       $::dispatcher->end_request;
 
1679   DO->transfer_in_out('direction' => 'out',
 
1680                       'requests'  => \@all_requests);
 
1682   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1684   $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id});
 
1687   $main::lxdebug->leave_sub();
 
1691   $main::lxdebug->enter_sub();
 
1693   my $form     = $main::form;
 
1695   DO->close_orders('ids' => [ $form->{id} ]);
 
1697   $form->{closed} = 1;
 
1701   $main::lxdebug->leave_sub();
 
1705   $::lxdebug->enter_sub;
 
1707   $::auth->assert('purchase_delivery_order_edit | sales_delivery_order_edit');
 
1710   retrieve_partunits();
 
1712   my $new_rowcount = $::form->{"rowcount"} * 1 + 1;
 
1713   $::form->{"project_id_${new_rowcount}"} = $::form->{"globalproject_id"};
 
1715   $::form->language_payment(\%::myconfig);
 
1717   Common::webdav_folder($::form);
 
1720   display_row(++$::form->{rowcount});
 
1723   $::lxdebug->leave_sub;
 
1727   call_sub($main::form->{yes_nextsub});
 
1731   call_sub($main::form->{no_nextsub});
 
1735   call_sub($main::form->{update_nextsub} || $main::form->{nextsub} || 'update_delivery_order');
 
1739   my $form     = $main::form;
 
1740   my $locale   = $main::locale;
 
1742   foreach my $action (qw(update print save transfer_out transfer_out_default sort
 
1743                          transfer_in transfer_in_default mark_closed save_as_new invoice delete)) {
 
1744     if ($form->{"action_${action}"}) {
 
1750   $form->error($locale->text('No action defined.'));
 
1753 sub transfer_out_default {
 
1754   $main::lxdebug->enter_sub();
 
1756   my $form     = $main::form;
 
1758   transfer_in_out_default('direction' => 'out');
 
1760   $main::lxdebug->leave_sub();
 
1763 sub transfer_in_default {
 
1764   $main::lxdebug->enter_sub();
 
1766   my $form     = $main::form;
 
1768   transfer_in_out_default('direction' => 'in');
 
1770   $main::lxdebug->leave_sub();
 
1773 # Falls das Standardlagerverfahren aktiv ist, wird
 
1774 # geprüft, ob alle Standardlagerplätze für die Auslager-
 
1775 # artikel vorhanden sind UND ob die Warenmenge ausreicht zum
 
1776 # Auslagern. Falls nicht wird entsprechend eine Fehlermeldung
 
1777 # generiert. Offen Chargennummer / bestbefore wird nicht berücksichtigt
 
1778 sub transfer_in_out_default {
 
1779   $main::lxdebug->enter_sub();
 
1781   my $form     = $main::form;
 
1782   my %myconfig = %main::myconfig;
 
1783   my $locale   = $main::locale;
 
1786   my (%missing_default_bins, %qty_parts, @all_requests, %part_info_map, $default_warehouse_id, $default_bin_id);
 
1788   Common::check_params(\%params, qw(direction));
 
1790   # entsprechende defaults holen, falls standardlagerplatz verwendet werden soll
 
1791   if ($::instance_conf->get_transfer_default_use_master_default_bin) {
 
1792     $default_warehouse_id = $::instance_conf->get_warehouse_id;
 
1793     $default_bin_id       = $::instance_conf->get_bin_id;
 
1797   my @part_ids = map { $form->{"id_${_}"} } (1 .. $form->{rowcount});
 
1799     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1800     %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1801     foreach my $i (1 .. $form->{rowcount}) {
 
1802       next unless ($form->{"id_$i"});
 
1803       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1804       my $qty =   $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1806       $form->show_generic_error($locale->text("Cannot transfer negative entries." )) if ($qty < 0);
 
1807       # if we do not want to transfer services and this part is a service, set qty to zero
 
1808       # ... and do not create a hash entry in %qty_parts below (will skip check for bins for the transfer == out case)
 
1809       # ... 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)
 
1811       $qty = 0 if (!$::instance_conf->get_transfer_default_services && $part_info_map{$form->{"id_$i"}}->{part_type} eq 'service');
 
1812       $qty_parts{$form->{"id_$i"}} += $qty;
 
1814         delete $qty_parts{$form->{"id_$i"}} unless $qty_parts{$form->{"id_$i"}};
 
1815         undef $form->{"stock_in_$i"};
 
1818       $part_info_map{$form->{"id_$i"}}{bin_id}       ||= $default_bin_id;
 
1819       $part_info_map{$form->{"id_$i"}}{warehouse_id} ||= $default_warehouse_id;
 
1821       push @all_requests, ($qty == 0) ? { } : {
 
1822                         'chargenumber' => '',  #?? die müsste entsprechend geholt werden
 
1823                         #'bestbefore' => undef, # TODO wird nicht berücksichtigt
 
1824                         'bin_id' => $part_info_map{$form->{"id_$i"}}{bin_id},
 
1826                         'parts_id' => $form->{"id_$i"},
 
1827                         'comment' => $locale->text("Default transfer delivery order"),
 
1828                         'unit' => $part_info_map{$form->{"id_$i"}}{unit},
 
1829                         'warehouse_id' => $part_info_map{$form->{"id_$i"}}{warehouse_id},
 
1830                         'oe_id' => $form->{id},
 
1831                         'project_id' => $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id}
 
1835     # jetzt wird erst überprüft, ob die Stückzahl entsprechend stimmt.
 
1836     # check if bin (transfer in and transfer out and qty (transfer out) is correct
 
1837     foreach my $key (keys %qty_parts) {
 
1839       $missing_default_bins{$key}{missing_bin} = 1 unless ($part_info_map{$key}{bin_id});
 
1840       next unless ($part_info_map{$key}{bin_id}); # abbruch
 
1842       if ($params{direction} eq 'out') {  # wird nur für ausgehende Mengen benötigt
 
1843         my ($max_qty, $error) = WH->get_max_qty_parts_bin(parts_id => $key, bin_id => $part_info_map{$key}{bin_id});
 
1845           # wir können nicht entscheiden, welche charge oder mhd (bestbefore) ausgewählt sein soll
 
1846           # deshalb rückmeldung nach oben geben, manuell auszulagern
 
1847           # TODO Bei nur einem Treffer mit Charge oder bestbefore wäre das noch möglich
 
1848           $missing_default_bins{$key}{chargenumber} = 1;
 
1850         if ($max_qty < $qty_parts{$key}){
 
1851           $missing_default_bins{$key}{missing_qty} = $max_qty - $qty_parts{$key};
 
1857   # Abfrage für Fehlerbehandlung (nur bei direction == out)
 
1858   if (scalar (keys %missing_default_bins)) {
 
1860     foreach my $fehler (keys %missing_default_bins) {
 
1862       my $ware = WH->get_part_description(parts_id => $fehler);
 
1863       if ($missing_default_bins{$fehler}{missing_bin}){
 
1864         $fehlertext .= "Kein Standardlagerplatz definiert bei $ware <br>";
 
1866       if ($missing_default_bins{$fehler}{missing_qty}) {  # missing_qty
 
1867         $fehlertext .= "Es fehlen " . $missing_default_bins{$fehler}{missing_qty}*-1 .
 
1868                        " von $ware auf dem Standard-Lagerplatz " . $part_info_map{$fehler}{bin} .   " zum Auslagern<br>";
 
1870       if ($missing_default_bins{$fehler}{chargenumber}){
 
1871         $fehlertext .= "Die Ware hat eine Chargennummer oder eine Mindesthaltbarkeit definiert.
 
1872                         Hier kann man nicht automatisch entscheiden.
 
1873                         Bitte diesen Lieferschein manuell auslagern.
 
1876       # auslagern soll immer gehen, auch wenn nicht genügend auf lager ist.
 
1877       # der lagerplatz ist hier extra konfigurierbar, bspw. Lager-Korrektur mit
 
1878       # Lagerplatz Lagerplatz-Korrektur
 
1879       my $default_warehouse_id_ignore_onhand = $::instance_conf->get_warehouse_id_ignore_onhand;
 
1880       my $default_bin_id_ignore_onhand       = $::instance_conf->get_bin_id_ignore_onhand;
 
1881       if ($::instance_conf->get_transfer_default_ignore_onhand && $default_bin_id_ignore_onhand) {
 
1882         # entsprechende defaults holen
 
1883         # falls chargenumber, bestbefore oder anzahl nicht stimmt, auf automatischen
 
1884         # lagerplatz wegbuchen!
 
1885         foreach (@all_requests) {
 
1886           if ($_->{parts_id} eq $fehler){
 
1887           $_->{bin_id}        = $default_bin_id_ignore_onhand;
 
1888           $_->{warehouse_id}  = $default_warehouse_id_ignore_onhand;
 
1892         #$main::lxdebug->message(0, 'Fehlertext: ' . $fehlertext);
 
1893         $form->show_generic_error($locale->text("Cannot transfer. <br> Reason:<br>#1", $fehlertext ));
 
1899   # hier der eigentliche fallunterschied für in oder out
 
1900   my $prefix   = $params{direction} eq 'in' ? 'in' : 'out';
 
1902   # dieser array_ref ist für DO->save da:
 
1903   # einmal die all_requests in YAML verwandeln, damit delivery_order_items_stock
 
1904   # gefüllt werden kann.
 
1905   # could be dumped to the form in the first loop,
 
1906   # but maybe bin_id and warehouse_id has changed to the "korrekturlager" with
 
1907   # allowed negative qty ($::instance_conf->get_warehouse_id_ignore_onhand) ...
 
1909   foreach (@all_requests){
 
1911     next unless scalar(%{ $_ });
 
1912     $form->{"stock_${prefix}_$i"} = YAML::Dump([$_]);
 
1915   save(no_redirect => 1); # Wir können auslagern, deshalb beleg speichern
 
1916                           # und in delivery_order_items_stock speichern
 
1918   # ... and fill back the persistent dois_id for inventory fk
 
1919   undef (@all_requests);
 
1920   foreach my $i (1 .. $form->{rowcount}) {
 
1921     next unless ($form->{"id_$i"} && $form->{"stock_${prefix}_$i"});
 
1922     push @all_requests, @{ DO->unpack_stock_information('packed' => $form->{"stock_${prefix}_$i"}) };
 
1924   DO->transfer_in_out('direction' => $prefix,
 
1925                       'requests'  => \@all_requests);
 
1927   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1929   $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'out';
 
1930   $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'in';
 
1936   $main::lxdebug->enter_sub();
 
1940   my $form     = $main::form;
 
1943   save(no_redirect => 1); # has to be done, at least for newly added positions
 
1945   # hashify partnumbers, positions. key is delivery_order_items_id
 
1946   for my $i (1 .. ($form->{rowcount}) ) {
 
1947     $temp_hash{$form->{"delivery_order_items_id_$i"}} = { runningnumber => $form->{"runningnumber_$i"}, partnumber => $form->{"partnumber_$i"} };
 
1948     if ($form->{id} && $form->{"discount_$i"}) {
 
1949       # prepare_order assumes a db value if there is a form->id and multiplies *100
 
1950       # We hope for new controller code (no more format_amount/parse_amount distinction)
 
1951       $form->{"discount_$i"} /=100;
 
1954   # naturally sort partnumbers and get a sorted array of doi_ids
 
1955   my @sorted_doi_ids =  sort { Sort::Naturally::ncmp($temp_hash{$a}->{"partnumber"}, $temp_hash{$b}->{"partnumber"}) }  keys %temp_hash;
 
1960   for (@sorted_doi_ids) {
 
1961     $form->{"runningnumber_$temp_hash{$_}->{runningnumber}"} = $new_number;
 
1964   # all parse_amounts changes are in form (i.e. , to .) therefore we need
 
1965   # another format_amount to change it back, for the next save ;-(
 
1966   # works great except for row discounts (see above comment)
 
1970     $main::lxdebug->leave_sub();
 
1982 do.pl - Script for all calls to delivery order
 
1990 Sorts all position with Natural Sort. Can be activated in form_footer.html like this
 
1991 C<E<lt>input class="submit" type="submit" name="action_sort" id="sort_button" value="[% 'Sort and Save' | $T8 %]"E<gt>>
 
1997 Sort and Save can be implemented as an optional button if configuration ca be set by client config.
 
1998 Example coding for database scripts and templates in (git show af2f24b8), check also
 
1999 autogeneration for rose (scripts/rose_auto_create_model.pl --h)