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, @transfer_qty ],
 
 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, @transfer_qty ],
 
 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   my $current_employee   = SL::DB::Manager::Employee->current;
 
 405   $form->{employee_id}   = $form->{old_employee_id} if $form->{old_employee_id};
 
 406   $form->{salesman_id}   = $form->{old_salesman_id} if $form->{old_salesman_id};
 
 407   $form->{employee_id} ||= $current_employee->id;
 
 408   $form->{salesman_id} ||= $current_employee->id;
 
 410   my $vc = $form->{vc} eq "customer" ? "customers" : "vendors";
 
 411   $form->get_lists("price_factors"  => "ALL_PRICE_FACTORS",
 
 412                    "business_types" => "ALL_BUSINESS_TYPES",
 
 414   $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all;
 
 417   my @old_project_ids = uniq grep { $_ } map { $_ * 1 } ($form->{"globalproject_id"}, map { $form->{"project_id_$_"} } 1..$form->{"rowcount"});
 
 418   my @old_ids_cond    = @old_project_ids ? (id => \@old_project_ids) : ();
 
 420   if (($vc eq 'customers') && $::instance_conf->get_customer_projects_only_in_sales) {
 
 423         customer_id          => $::form->{customer_id},
 
 424         billable_customer_id => $::form->{customer_id},
 
 429       and => [ active => 1, @customer_cond ],
 
 433   $::form->{ALL_PROJECTS}          = SL::DB::Manager::Project->get_all_sorted(query => \@conditions);
 
 434   $::form->{ALL_EMPLOYEES}         = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{employee_id},  deleted => 0 ] ]);
 
 435   $::form->{ALL_SALESMEN}          = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{salesman_id},  deleted => 0 ] ]);
 
 436   $::form->{ALL_SHIPTO}            = SL::DB::Manager::Shipto->get_all_sorted(query => [
 
 437     or => [ trans_id  => $::form->{"$::form->{vc}_id"} * 1, and => [ shipto_id => $::form->{shipto_id} * 1, trans_id => undef ] ]
 
 439   $::form->{ALL_CONTACTS}          = SL::DB::Manager::Contact->get_all_sorted(query => [
 
 441       cp_cv_id => $::form->{"$::form->{vc}_id"} * 1,
 
 444         cp_id    => $::form->{cp_id} * 1
 
 449   my $dispatch_to_popup = '';
 
 450   if ($form->{resubmit} && ($form->{format} eq "html")) {
 
 451     $dispatch_to_popup  = "window.open('about:blank','Beleg'); document.do.target = 'Beleg';";
 
 452     $dispatch_to_popup .= "document.do.submit();";
 
 453   } elsif ($form->{resubmit}) {
 
 454     # emulate click for resubmitting actions
 
 455     $dispatch_to_popup  = "document.do.${_}.click(); " for grep { /^action_/ } keys %$form;
 
 457   $::request->{layout}->add_javascripts_inline("\$(function(){$dispatch_to_popup});");
 
 460   $form->{follow_up_trans_info} = $form->{donumber} .'('. $form->{VC_OBJ}->name .')';
 
 462   $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.File kivi.MassDeliveryOrderPrint kivi.SalesPurchase kivi.Part ckeditor/ckeditor ckeditor/adapters/jquery kivi.io autocomplete_customer));
 
 465   push @custom_hidden, map { "shiptocvar_" . $_->name } @{ SL::DB::Manager::CustomVariableConfig->get_all(where => [ module => 'ShipTo' ]) };
 
 467   $::form->{HIDDENS} = [ map { +{ name => $_, value => $::form->{$_} } } (@custom_hidden) ];
 
 469   setup_do_action_bar();
 
 472   # Fix für Bug 1082 Erwartet wird: 'abteilungsNAME--abteilungsID'
 
 473   # und Erweiterung für Bug 1760:
 
 474   # Das war leider nur ein Teil-Fix, da das Verhalten den 'Erneuern'-Knopf
 
 475   # nicht überlebt. Konsequent jetzt auf L umgestellt
 
 476   #   $ perldoc SL::Template::Plugin::L
 
 477   # Daher entsprechend nur die Anpassung in form_header
 
 478   # und in DO.pm gemacht. 4 Testfälle:
 
 479   # department_id speichern                 | i.O.
 
 480   # department_id lesen                     | i.O.
 
 481   # department leer überlebt erneuern       | i.O.
 
 482   # department nicht leer überlebt erneuern | i.O.
 
 483   # $main::lxdebug->message(0, 'ABTEILUNGS ID in form?' . $form->{department_id});
 
 484   print $form->parse_html_template('do/form_header');
 
 486   $main::lxdebug->leave_sub();
 
 490   $main::lxdebug->enter_sub();
 
 494   my $form     = $main::form;
 
 496   $form->{PRINT_OPTIONS}      = setup_sales_purchase_print_options();
 
 497   $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
 
 499   print $form->parse_html_template('do/form_footer',
 
 500     {transfer_default         => ($::instance_conf->get_transfer_default)});
 
 502   $main::lxdebug->leave_sub();
 
 505 sub update_delivery_order {
 
 506   $main::lxdebug->enter_sub();
 
 510   my $form     = $main::form;
 
 511   my %myconfig = %main::myconfig;
 
 513   set_headings($form->{"id"} ? "edit" : "add");
 
 515   $form->{insertdate} = SL::DB::DeliveryOrder->new(id => $form->{id})->load->itime_as_date if $form->{id};
 
 520   $payment_id = $form->{payment_id} if $form->{payment_id};
 
 522   my $vc = $form->{vc};
 
 523   if (($form->{"previous_${vc}_id"} || $form->{"${vc}_id"}) != $form->{"${vc}_id"}) {
 
 524     $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
 
 526     IS->get_customer(\%myconfig, $form) if $vc eq 'customer';
 
 527     IR->get_vendor(\%myconfig, $form)   if $vc eq 'vendor';
 
 530   $form->{discount} =  $form->{"$form->{vc}_discount"} if defined $form->{"$form->{vc}_discount"};
 
 531   # Problem: Wenn man ohne Erneuern einen Kunden/Lieferanten
 
 532   # wechselt, wird der entsprechende Kunden/ Lieferantenrabatt
 
 533   # nicht übernommen. Grundproblem: In Commit 82574e78
 
 534   # hab ich aus discount customer_discount und vendor_discount
 
 535   # gemacht und entsprechend an den Oberflächen richtig hin-
 
 536   # geschoben. Die damals bessere Lösung wäre gewesen:
 
 537   # In den Templates nur die hidden für form-discount wieder ein-
 
 538   # setzen dann wäre die Verrenkung jetzt nicht notwendig.
 
 539   # TODO: Ggf. Bugfix 1284, 1575 und 817 wieder zusammenführen
 
 540   # Testfälle: Kunden mit Rabatt 0 -> Rabatt 20 i.O.
 
 541   #            Kunde mit Rabatt 20 -> Rabatt 0  i.O.
 
 542   #            Kunde mit Rabatt 20 -> Rabatt 5,5 i.O.
 
 543   $form->{payment_id} = $payment_id if $form->{payment_id} eq "";
 
 545   my $i = $form->{rowcount};
 
 547   if (   ($form->{"partnumber_$i"} eq "")
 
 548       && ($form->{"description_$i"} eq "")
 
 549       && ($form->{"partsgroup_$i"}  eq "")) {
 
 556     if ($form->{type} eq 'purchase_delivery_order') {
 
 557       IR->retrieve_item(\%myconfig, $form);
 
 560       IS->retrieve_item(\%myconfig, $form);
 
 564     my $rows = scalar @{ $form->{item_list} };
 
 567       $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
 568       if( !$form->{"qty_$i"} ) {
 
 569         $form->{"qty_$i"} = 1;
 
 574         select_item(mode => $mode, pre_entered_qty => $form->{"qty_$i"});
 
 575         $::dispatcher->end_request;
 
 579         my $sellprice = $form->parse_amount(\%myconfig, $form->{"sellprice_$i"});
 
 581         map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} } keys %{ $form->{item_list}[0] };
 
 583         $form->{"marge_price_factor_$i"} = $form->{item_list}->[0]->{price_factor};
 
 586           $form->{"sellprice_$i"} = $sellprice;
 
 588           my $record        = _make_record();
 
 589           my $price_source  = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
 
 590           my $best_price    = $price_source->best_price;
 
 591           my $best_discount = $price_source->best_discount;
 
 594             $::form->{"sellprice_$i"}           = $best_price->price;
 
 595             $::form->{"active_price_source_$i"} = $best_price->source;
 
 597           if ($best_discount) {
 
 598             $::form->{"discount_$i"}               = $best_discount->discount;
 
 599             $::form->{"active_discount_source_$i"} = $best_discount->source;
 
 603         $form->{"sellprice_$i"}          = $form->format_amount(\%myconfig, $form->{"sellprice_$i"});
 
 604         $form->{"lastcost_$i"}           = $form->format_amount(\%myconfig, $form->{"lastcost_$i"});
 
 605         $form->{"qty_$i"}                = $form->format_amount(\%myconfig, $form->{"qty_$i"});
 
 606         $form->{"discount_$i"}           = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0);
 
 613       # ok, so this is a new part
 
 614       # ask if it is a part or service item
 
 616       if (   $form->{"partsgroup_$i"}
 
 617           && ($form->{"partsnumber_$i"} eq "")
 
 618           && ($form->{"description_$i"} eq "")) {
 
 620         $form->{"discount_$i"} = "";
 
 621         $form->{"not_discountable_$i"} = "";
 
 625         $form->{"id_$i"}   = 0;
 
 631   $main::lxdebug->leave_sub();
 
 635   $main::lxdebug->enter_sub();
 
 639   my $form     = $main::form;
 
 640   my %myconfig = %main::myconfig;
 
 641   my $locale   = $main::locale;
 
 643   $form->{vc} = $form->{type} eq 'purchase_delivery_order' ? 'vendor' : 'customer';
 
 645   $form->get_lists("projects"       => { "key" => "ALL_PROJECTS",
 
 647                    "business_types" => "ALL_BUSINESS_TYPES");
 
 648   $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]);
 
 649   $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all;
 
 650   $form->{title}             = $locale->text('Delivery Orders');
 
 652   setup_do_search_action_bar();
 
 656   print $form->parse_html_template('do/search');
 
 658   $main::lxdebug->leave_sub();
 
 662   $main::lxdebug->enter_sub();
 
 666   my $form     = $main::form;
 
 667   my %myconfig = %main::myconfig;
 
 668   my $locale   = $main::locale;
 
 669   my $cgi      = $::request->{cgi};
 
 671   $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.MassDeliveryOrderPrint kivi.SalesPurchase));
 
 672   ($form->{ $form->{vc} }, $form->{"$form->{vc}_id"}) = split(/--/, $form->{ $form->{vc} });
 
 674   report_generator_set_default_sort('transdate', 1);
 
 678   $form->{rowcount} = scalar @{ $form->{DO} };
 
 681     ids                     transdate               reqdate
 
 683     ordnumber               customernumber          cusordnumber
 
 684     name                    employee  salesman
 
 685     shipvia                 globalprojectnumber
 
 686     transaction_description department
 
 691   $form->{l_open}      = $form->{l_closed} = "Y" if ($form->{open}      && $form->{closed});
 
 692   $form->{l_delivered} = "Y"                     if ($form->{delivered} && $form->{notdelivered});
 
 694   $form->{title}       = $locale->text('Delivery Orders');
 
 696   my $attachment_basename = $form->{vc} eq 'vendor' ? $locale->text('purchase_delivery_order_list') : $locale->text('sales_delivery_order_list');
 
 698   my $report = SL::ReportGenerator->new(\%myconfig, $form);
 
 700   my @hidden_variables = map { "l_${_}" } @columns;
 
 701   push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber serialnumber cusordnumber
 
 702                                           transaction_description transdatefrom transdateto reqdatefrom reqdateto
 
 703                                           type vc employee_id salesman_id project_id parts_partnumber parts_description
 
 704                                           insertdatefrom insertdateto business_id);
 
 706   my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
 
 709     'ids'                     => { 'text' => '<input type="checkbox" id="multi_all" value="1">', 'align' => 'center' },
 
 710     'transdate'               => { 'text' => $locale->text('Delivery Order Date'), },
 
 711     'reqdate'                 => { 'text' => $locale->text('Reqdate'), },
 
 712     'id'                      => { 'text' => $locale->text('ID'), },
 
 713     'donumber'                => { 'text' => $locale->text('Delivery Order'), },
 
 714     'ordnumber'               => { 'text' => $locale->text('Order'), },
 
 715     'customernumber'          => { 'text' => $locale->text('Customer Number'), },
 
 716     'cusordnumber'            => { 'text' => $locale->text('Customer Order Number'), },
 
 717     'name'                    => { 'text' => $form->{vc} eq 'customer' ? $locale->text('Customer') : $locale->text('Vendor'), },
 
 718     'employee'                => { 'text' => $locale->text('Employee'), },
 
 719     'salesman'                => { 'text' => $locale->text('Salesman'), },
 
 720     'shipvia'                 => { 'text' => $locale->text('Ship via'), },
 
 721     'globalprojectnumber'     => { 'text' => $locale->text('Project Number'), },
 
 722     'transaction_description' => { 'text' => $locale->text('Transaction description'), },
 
 723     'open'                    => { 'text' => $locale->text('Open'), },
 
 724     'delivered'               => { 'text' => $locale->text('Delivered'), },
 
 725     'department'              => { 'text' => $locale->text('Department'), },
 
 726     'insertdate'              => { 'text' => $locale->text('Insert Date'), },
 
 729   foreach my $name (qw(id transdate reqdate donumber ordnumber name employee salesman shipvia transaction_description department insertdate)) {
 
 730     my $sortdir                 = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
 
 731     $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
 
 734   $form->{"l_type"} = "Y";
 
 735   map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
 
 737   $column_defs{ids}->{visible} = 'HTML';
 
 739   $report->set_columns(%column_defs);
 
 740   $report->set_column_order(@columns);
 
 742   $report->set_export_options('orders', @hidden_variables, qw(sort sortdir));
 
 744   $report->set_sort_indicator($form->{sort}, $form->{sortdir});
 
 747   if ($form->{customer}) {
 
 748     push @options, $locale->text('Customer') . " : $form->{customer}";
 
 750   if ($form->{vendor}) {
 
 751     push @options, $locale->text('Vendor') . " : $form->{vendor}";
 
 753   if ($form->{cp_name}) {
 
 754     push @options, $locale->text('Contact Person') . " : $form->{cp_name}";
 
 756   if ($form->{department_id}) {
 
 757     push @options, $locale->text('Department') . " : " . SL::DB::Department->new(id => $form->{department_id})->load->description;
 
 759   if ($form->{donumber}) {
 
 760     push @options, $locale->text('Delivery Order Number') . " : $form->{donumber}";
 
 762   if ($form->{ordnumber}) {
 
 763     push @options, $locale->text('Order Number') . " : $form->{ordnumber}";
 
 765   push @options, $locale->text('Serial Number') . " : $form->{serialnumber}" if $form->{serialnumber};
 
 766   if ($form->{business_id}) {
 
 767     my $vc_type_label = $form->{vc} eq 'customer' ? $locale->text('Customer type') : $locale->text('Vendor type');
 
 768     push @options, $vc_type_label . " : " . SL::DB::Business->new(id => $form->{business_id})->load->description;
 
 770   if ($form->{transaction_description}) {
 
 771     push @options, $locale->text('Transaction description') . " : $form->{transaction_description}";
 
 773   if ($form->{parts_description}) {
 
 774     push @options, $locale->text('Part Description') . " : $form->{parts_description}";
 
 776   if ($form->{parts_partnumber}) {
 
 777     push @options, $locale->text('Part Number') . " : $form->{parts_partnumber}";
 
 779   if ( $form->{transdatefrom} or $form->{transdateto} ) {
 
 780     push @options, $locale->text('Delivery Order Date');
 
 781     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1)     if $form->{transdatefrom};
 
 782     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{transdateto},   1)     if $form->{transdateto};
 
 784   if ( $form->{reqdatefrom} or $form->{reqdateto} ) {
 
 785     push @options, $locale->text('Reqdate');
 
 786     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1)       if $form->{reqdatefrom};
 
 787     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{reqdateto},   1)       if $form->{reqdateto};
 
 789   if ( $form->{insertdatefrom} or $form->{insertdateto} ) {
 
 790     push @options, $locale->text('Insert Date');
 
 791     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1)    if $form->{insertdatefrom};
 
 792     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{insertdateto},   1)    if $form->{insertdateto};
 
 795     push @options, $locale->text('Open');
 
 797   if ($form->{closed}) {
 
 798     push @options, $locale->text('Closed');
 
 800   if ($form->{delivered}) {
 
 801     push @options, $locale->text('Delivered');
 
 803   if ($form->{notdelivered}) {
 
 804     push @options, $locale->text('Not delivered');
 
 807   my $pr = SL::DB::Manager::Printer->find_by(
 
 808       printer_description => $::locale->text("sales_delivery_order_printer"));
 
 810       $form->{printer_id} = $pr->id;
 
 813   my $print_options = SL::Helper::PrintOptions->get_print_options(
 
 815       hide_language_id => 1,
 
 821   $report->set_options('top_info_text'        => join("\n", @options),
 
 822                        'raw_top_info_text'    => $form->parse_html_template('do/orders_top'),
 
 823                        'raw_bottom_info_text' => $form->parse_html_template('do/orders_bottom', { print_options => $print_options }),
 
 824                        'output_format'        => 'HTML',
 
 825                        'title'                => $form->{title},
 
 826                        'attachment_basename'  => $attachment_basename . strftime('_%Y%m%d', localtime time),
 
 828   $report->set_options_from_form();
 
 829   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
 
 831   # add sort and escape callback, this one we use for the add sub
 
 832   $form->{callback} = $href .= "&sort=$form->{sort}";
 
 834   # escape callback for href
 
 835   my $callback = $form->escape($href);
 
 837   my $edit_url       = build_std_url('action=edit', 'type', 'vc');
 
 838   my $edit_order_url = build_std_url('script=oe.pl', 'type=' . ($form->{type} eq 'sales_delivery_order' ? 'sales_order' : 'purchase_order'), 'action=edit');
 
 842   foreach my $dord (@{ $form->{DO} }) {
 
 843     $dord->{open}      = $dord->{closed}    ? $locale->text('No')  : $locale->text('Yes');
 
 844     $dord->{delivered} = $dord->{delivered} ? $locale->text('Yes') : $locale->text('No');
 
 846     my $row = { map { $_ => { 'data' => $dord->{$_} } } @columns };
 
 848     my $ord_id = $dord->{id};
 
 850       'raw_data' =>   $cgi->hidden('-name' => "trans_id_${idx}", '-value' => $ord_id)
 
 851                     . $cgi->checkbox('-name' => "multi_id_${idx}",' id' => "multi_id_id_".$ord_id, '-value' => 1, '-label' => ''),
 
 852       'valign'   => 'center',
 
 856     $row->{donumber}->{link}  = $edit_url       . "&id=" . E($dord->{id})      . "&callback=${callback}";
 
 857     $row->{ordnumber}->{link} = $edit_order_url . "&id=" . E($dord->{oe_id})   . "&callback=${callback}" if $dord->{oe_id};
 
 858     $report->add_data($row);
 
 863   setup_do_orders_action_bar();
 
 865   $report->generate_with_headers();
 
 867   $main::lxdebug->leave_sub();
 
 871   $main::lxdebug->enter_sub();
 
 877   my $form     = $main::form;
 
 878   my %myconfig = %main::myconfig;
 
 879   my $locale   = $main::locale;
 
 881   $form->mtime_ischanged('delivery_orders');
 
 883   $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
 
 885   $form->isblank("transdate", $locale->text('Delivery Order Date missing!'));
 
 887   $form->{donumber} =~ s/^\s*//g;
 
 888   $form->{donumber} =~ s/\s*$//g;
 
 890   my $msg = ucfirst $form->{vc};
 
 891   $form->isblank($form->{vc} . "_id", $locale->text($msg . " missing!"));
 
 893   # $locale->text('Customer missing!');
 
 894   # $locale->text('Vendor missing!');
 
 896   remove_emptied_rows();
 
 899   # if the name changed get new values
 
 900   my $vc = $form->{vc};
 
 901   if (($form->{"previous_${vc}_id"} || $form->{"${vc}_id"}) != $form->{"${vc}_id"}) {
 
 902     $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
 
 904     IS->get_customer(\%myconfig, $form) if $vc eq 'customer';
 
 905     IR->get_vendor(\%myconfig, $form)   if $vc eq 'vendor';
 
 908     $::dispatcher->end_request;
 
 911   $form->{id} = 0 if $form->{saveasnew};
 
 915   if(!exists $form->{addition}) {
 
 916     $form->{snumbers} = qq|donumber_| . $form->{donumber};
 
 917     $form->{addition} = "SAVED";
 
 920   # /saving the history
 
 922   $form->{simple_save} = 1;
 
 923   if (!$params{no_redirect} && !$form->{print_and_save}) {
 
 924     delete @{$form}{ary_diff([keys %{ $form }], [qw(login id script type cursor_fokus)])};
 
 926     $::dispatcher->end_request;
 
 928   $main::lxdebug->leave_sub();
 
 932   $main::lxdebug->enter_sub();
 
 936   my $form     = $main::form;
 
 937   my %myconfig = %main::myconfig;
 
 938   my $locale   = $main::locale;
 
 942     if(!exists $form->{addition}) {
 
 943       $form->{snumbers} = qq|donumber_| . $form->{donumber};
 
 944       $form->{addition} = "DELETED";
 
 947     # /saving the history
 
 949     $form->info($locale->text('Delivery Order deleted!'));
 
 950     $::dispatcher->end_request;
 
 953   $form->error($locale->text('Cannot delete delivery order!'));
 
 955   $main::lxdebug->leave_sub();
 
 959   $main::lxdebug->enter_sub();
 
 961   my $form     = $main::form;
 
 962   my %myconfig = %main::myconfig;
 
 963   my $locale   = $main::locale;
 
 966   $form->mtime_ischanged('delivery_orders');
 
 968   $main::auth->assert($form->{type} eq 'purchase_delivery_order' ? 'vendor_invoice_edit' : 'invoice_edit');
 
 970   $form->{convert_from_do_ids} = $form->{id};
 
 971   $form->{deliverydate}        = $form->{transdate};
 
 972   $form->{transdate}           = $form->{invdate} = $form->current_date(\%myconfig);
 
 973   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
 
 974   $form->{defaultcurrency}     = $form->get_default_currency(\%myconfig);
 
 978   delete @{$form}{qw(id closed delivered)};
 
 980   my ($script, $buysell);
 
 981   if ($form->{type} eq 'purchase_delivery_order') {
 
 982     $form->{title}  = $locale->text('Add Vendor Invoice');
 
 983     $form->{script} = 'ir.pl';
 
 988     $form->{title}  = $locale->text('Add Sales Invoice');
 
 989     $form->{script} = 'is.pl';
 
 994   for my $i (1 .. $form->{rowcount}) {
 
 995     map { $form->{"${_}_${i}"} = $form->parse_amount(\%myconfig, $form->{"${_}_${i}"}) if $form->{"${_}_${i}"} } qw(ship qty sellprice lastcost basefactor discount);
 
 997     # adds a customer/vendor discount, unless we have a workflow case
 
 998     # CAVEAT: has to be done, after the above parse_amount
 
 999     unless ($form->{"ordnumber"}) {
 
1000       if ($form->{discount}) { # Falls wir einen Lieferanten-/Kundenrabatt haben
 
1001         # und rabattfähig sind, dann
 
1002         unless ($form->{"not_discountable_$i"}) {
 
1003           $form->{"discount_$i"} = $form->{discount}*100; # ... nehmen wir diesen Rabatt
 
1007     $form->{"donumber_$i"} = $form->{donumber};
 
1008     $form->{"converted_from_delivery_order_items_id_$i"} = delete $form->{"delivery_order_items_id_$i"};
 
1011   $form->{type} = "invoice";
 
1014   $main::locale = Locale->new("$myconfig{countrycode}", "$script");
 
1015   $locale = $main::locale;
 
1017   require "bin/mozilla/$form->{script}";
 
1019   my $currency = $form->{currency};
 
1022   if ($form->{ordnumber}) {
 
1023     require SL::DB::Order;
 
1024     if (my $order = SL::DB::Manager::Order->find_by(ordnumber => $form->{ordnumber})) {
 
1026       $form->{orddate} = $order->transdate_as_date;
 
1027       $form->{$_}      = $order->$_ for qw(payment_id salesman_id taxzone_id quonumber);
 
1031   $form->{currency}     = $currency;
 
1032   $form->{exchangerate} = "";
 
1033   $form->{forex}        = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, $buysell);
 
1034   $form->{exchangerate} = $form->{forex} if ($form->{forex});
 
1039   for my $i (1 .. $form->{rowcount}) {
 
1040     $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
 
1042     my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
 
1044     my $decimalplaces = ($dec > 2) ? $dec : 2;
 
1046     # copy delivery date from reqdate for order -> invoice conversion
 
1047     $form->{"deliverydate_$i"} = $form->{"reqdate_$i"}
 
1048       unless $form->{"deliverydate_$i"};
 
1051     $form->{"sellprice_$i"} =
 
1052       $form->format_amount(\%myconfig, $form->{"sellprice_$i"},
 
1055     $form->{"lastcost_$i"} =
 
1056       $form->format_amount(\%myconfig, $form->{"lastcost_$i"},
 
1059     (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
 
1060     $dec_qty = length $dec_qty;
 
1062       $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
 
1068   $main::lxdebug->leave_sub();
 
1072   $main::lxdebug->enter_sub();
 
1074   my $form     = $main::form;
 
1075   my %myconfig = %main::myconfig;
 
1076   my $locale   = $main::locale;
 
1079   $main::auth->assert($form->{type} eq 'sales_delivery_order' ? 'invoice_edit' : 'vendor_invoice_edit');
 
1081   my @do_ids = map { $form->{"trans_id_$_"} } grep { $form->{"multi_id_$_"} } (1..$form->{rowcount});
 
1083   if (!scalar @do_ids) {
 
1084     $form->show_generic_error($locale->text('You have not selected any delivery order.'));
 
1087   map { delete $form->{$_} } grep { m/^(?:trans|multi)_id_\d+/ } keys %{ $form };
 
1089   if (!DO->retrieve('vc' => $form->{vc}, 'ids' => \@do_ids)) {
 
1090     $form->show_generic_error($form->{vc} eq 'customer' ?
 
1091                               $locale->text('You cannot create an invoice for delivery orders for different customers.') :
 
1092                               $locale->text('You cannot create an invoice for delivery orders from different vendors.'),
 
1093                               'back_button' => 1);
 
1096   my $source_type              = $form->{type};
 
1097   $form->{convert_from_do_ids} = join ' ', @do_ids;
 
1098   # bei der auswahl von mehreren Lieferscheinen fuer eine Rechnung, die einfach in donumber_array
 
1099   # zwischenspeichern (DO.pm) und als ' '-separierte Liste wieder zurueckschreiben
 
1100   # Hinweis: delete gibt den wert zurueck und loescht danach das element (nett und einfach)
 
1101   # $shell: perldoc perlunc; /delete EXPR
 
1102   $form->{donumber}            = delete $form->{donumber_array};
 
1103   $form->{ordnumber}           = delete $form->{ordnumber_array};
 
1104   $form->{cusordnumber}        = delete $form->{cusordnumber_array};
 
1105   $form->{deliverydate}        = $form->{transdate};
 
1106   $form->{transdate}           = $form->current_date(\%myconfig);
 
1107   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
 
1108   $form->{type}                = "invoice";
 
1109   $form->{closed}              = 0;
 
1110   $form->{defaultcurrency}     = $form->get_default_currency(\%myconfig);
 
1112   my ($script, $buysell);
 
1113   if ($source_type eq 'purchase_delivery_order') {
 
1114     $form->{title}  = $locale->text('Add Vendor Invoice');
 
1115     $form->{script} = 'ir.pl';
 
1120     $form->{title}  = $locale->text('Add Sales Invoice');
 
1121     $form->{script} = 'is.pl';
 
1126   map { delete $form->{$_} } qw(id subject message cc bcc printed emailed queued);
 
1128   # get vendor or customer discount
 
1130   my $saved_form = save_form();
 
1131   if ($form->{vc} eq 'vendor') {
 
1132     IR->get_vendor(\%myconfig, \%$form);
 
1133     $vc_discount = $form->{vendor_discount};
 
1135     IS->get_customer(\%myconfig, \%$form);
 
1136     $vc_discount = $form->{customer_discount};
 
1138   # use payment terms from customer or vendor
 
1139   restore_form($saved_form,0,qw(payment_id));
 
1141   $form->{rowcount} = 0;
 
1142   foreach my $ref (@{ $form->{form_details} }) {
 
1143     $form->{rowcount}++;
 
1144     $ref->{reqdate} ||= $ref->{dord_transdate}; # copy transdates into each invoice row
 
1145     map { $form->{"${_}_$form->{rowcount}"} = $ref->{$_} } keys %{ $ref };
 
1146     map { $form->{"${_}_$form->{rowcount}"} = $form->format_amount(\%myconfig, $ref->{$_}) } qw(qty sellprice lastcost);
 
1147     $form->{"converted_from_delivery_order_items_id_$form->{rowcount}"} = delete $form->{"delivery_order_items_id_$form->{rowcount}"};
 
1149     if ($vc_discount){ # falls wir einen Lieferanten/Kundenrabatt haben
 
1150       # und keinen anderen discount wert an $i ...
 
1151       $form->{"discount_$form->{rowcount}"} ||= $vc_discount; # ... nehmen wir diesen Rabatt
 
1154     $form->{"discount_$form->{rowcount}"}   = $form->{"discount_$form->{rowcount}"}  * 100; #s.a. Bug 1151
 
1155     # Anm.: Eine Änderung des discounts in der SL/DO.pm->retrieve (select (doi.discount * 100) as discount) ergibt in psql einen
 
1156     # Wert von 10.0000001490116. Ferner ist der Rabatt in der Rechnung dann bei 1.0 (?). Deswegen lasse ich das hier. jb 10.10.09
 
1158     $form->{"discount_$form->{rowcount}"} = $form->format_amount(\%myconfig, $form->{"discount_$form->{rowcount}"});
 
1160   delete $form->{form_details};
 
1162   $locale = Locale->new("$myconfig{countrycode}", "$script");
 
1164   require "bin/mozilla/$form->{script}";
 
1171   $main::lxdebug->leave_sub();
 
1175   $main::lxdebug->enter_sub();
 
1179   my $form     = $main::form;
 
1181   $form->{saveasnew} = 1;
 
1182   $form->{closed}    = 0;
 
1183   $form->{delivered} = 0;
 
1184   map { delete $form->{$_} } qw(printed emailed queued);
 
1185   delete @{ $form }{ grep { m/^stock_(?:in|out)_\d+/ } keys %{ $form } };
 
1186   $form->{"converted_from_delivery_order_items_id_$_"} = delete $form->{"delivery_order_items_id_$_"} for 1 .. $form->{"rowcount"};
 
1187   # Let kivitendo assign a new order number if the user hasn't changed the
 
1188   # previous one. If it has been changed manually then use it as-is.
 
1189   $form->{donumber} =~ s/^\s*//g;
 
1190   $form->{donumber} =~ s/\s*$//g;
 
1191   if ($form->{saved_donumber} && ($form->{saved_donumber} eq $form->{donumber})) {
 
1192     delete($form->{donumber});
 
1197   $main::lxdebug->leave_sub();
 
1200 sub calculate_stock_in_out {
 
1201   $main::lxdebug->enter_sub();
 
1203   my $form     = $main::form;
 
1207   if (!$form->{"id_${i}"}) {
 
1208     $main::lxdebug->leave_sub();
 
1212   my $all_units = AM->retrieve_all_units();
 
1214   my $in_out   = $form->{type} =~ /^sales/ ? 'out' : 'in';
 
1215   my $sinfo    = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_${i}"});
 
1217   my $do_qty   = AM->sum_with_unit($::form->{"qty_$i"}, $::form->{"unit_$i"});
 
1218   my $sum      = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $sinfo });
 
1219   my $matches  = $do_qty == $sum;
 
1221   my $content  = $form->format_amount_units('amount'      => $sum * 1,
 
1222                                             'part_unit'   => $form->{"partunit_$i"},
 
1223                                             'amount_unit' => $all_units->{$form->{"partunit_$i"}}->{base_unit},
 
1224                                             'conv_units'  => 'convertible_not_smaller',
 
1226   $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="?">|;
 
1228   $main::lxdebug->leave_sub();
 
1233 sub get_basic_bin_wh_info {
 
1234   $main::lxdebug->enter_sub();
 
1236   my $stock_info = shift;
 
1238   my $form     = $main::form;
 
1240   foreach my $sinfo (@{ $stock_info }) {
 
1241     next unless ($sinfo->{bin_id});
 
1243     my $bin_info = WH->get_basic_bin_info('id' => $sinfo->{bin_id});
 
1244     map { $sinfo->{"${_}_description"} = $sinfo->{"${_}description"} = $bin_info->{"${_}_description"} } qw(bin warehouse);
 
1247   $main::lxdebug->leave_sub();
 
1250 sub stock_in_out_form {
 
1251   $main::lxdebug->enter_sub();
 
1253   my $form     = $main::form;
 
1255   if ($form->{in_out} eq 'out') {
 
1261   $main::lxdebug->leave_sub();
 
1264 sub redo_stock_info {
 
1265   $main::lxdebug->enter_sub();
 
1269   my $form     = $main::form;
 
1271   my @non_empty = grep { $_->{qty} } @{ $params{stock_info} };
 
1273   if ($params{add_empty_row}) {
 
1275       'warehouse_id' => scalar(@non_empty) ? $non_empty[-1]->{warehouse_id} : undef,
 
1276       'bin_id'       => scalar(@non_empty) ? $non_empty[-1]->{bin_id}       : undef,
 
1280   @{ $params{stock_info} } = @non_empty;
 
1282   $main::lxdebug->leave_sub();
 
1285 sub update_stock_in {
 
1286   $main::lxdebug->enter_sub();
 
1288   my $form     = $main::form;
 
1289   my %myconfig = %main::myconfig;
 
1291   my $stock_info = [];
 
1293   foreach my $i (1..$form->{rowcount}) {
 
1294     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1295     push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(warehouse_id bin_id chargenumber
 
1296                                                                    bestbefore qty unit delivery_order_items_stock_id) };
 
1299   display_stock_in_form($stock_info);
 
1301   $main::lxdebug->leave_sub();
 
1305   $main::lxdebug->enter_sub();
 
1307   my $form     = $main::form;
 
1309   my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
 
1311   display_stock_in_form($stock_info);
 
1313   $main::lxdebug->leave_sub();
 
1316 sub display_stock_in_form {
 
1317   $main::lxdebug->enter_sub();
 
1319   my $stock_info = shift;
 
1321   my $form     = $main::form;
 
1322   my %myconfig = %main::myconfig;
 
1323   my $locale   = $main::locale;
 
1325   $form->{title} = $locale->text('Stock');
 
1327   my $part_info  = IC->get_basic_part_info('id' => $form->{parts_id});
 
1329   # Standardlagerplatz für Standard-Auslagern verwenden, falls keiner für die Ware explizit definiert wurde
 
1330   if ($::instance_conf->get_transfer_default_use_master_default_bin) {
 
1331     $part_info->{warehouse_id} ||= $::instance_conf->get_warehouse_id;
 
1332     $part_info->{bin_id}       ||= $::instance_conf->get_bin_id;
 
1335   my $units      = AM->retrieve_units(\%myconfig, $form);
 
1336   # der zweite Parameter von unit_select_data gibt den default-Namen (selected) vor
 
1337   my $units_data = AM->unit_select_data($units, $form->{do_unit}, undef, $part_info->{unit});
 
1339   $form->get_lists('warehouses' => { 'key'    => 'WAREHOUSES',
 
1340                                      'bins'   => 'BINS' });
 
1342   redo_stock_info('stock_info' => $stock_info, 'add_empty_row' => !$form->{delivered});
 
1344   get_basic_bin_wh_info($stock_info);
 
1346   $form->header(no_layout => 1);
 
1347   print $form->parse_html_template('do/stock_in_form', { 'UNITS'      => $units_data,
 
1348                                                          'STOCK_INFO' => $stock_info,
 
1349                                                          'PART_INFO'  => $part_info, });
 
1351   $main::lxdebug->leave_sub();
 
1354 sub _stock_in_out_set_qty_display {
 
1355   my $stock_info       = shift;
 
1357   my $all_units        = AM->retrieve_all_units();
 
1358   my $sum              = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1359   $form->{qty_display} = $form->format_amount_units(amount      => $sum * 1,
 
1360                                                     part_unit   => $form->{partunit},
 
1361                                                     amount_unit => $all_units->{ $form->{partunit} }->{base_unit},
 
1362                                                     conv_units  => 'convertible_not_smaller',
 
1367   $main::lxdebug->enter_sub();
 
1369   my $form     = $main::form;
 
1370   my %myconfig = %main::myconfig;
 
1372   my $stock_info = [];
 
1374   foreach my $i (1..$form->{rowcount}) {
 
1375     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1377     next if ($form->{"qty_$i"} <= 0);
 
1379     push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(delivery_order_items_stock_id warehouse_id bin_id chargenumber bestbefore qty unit) };
 
1382   $form->{stock} = YAML::Dump($stock_info);
 
1384   _stock_in_out_set_qty_display($stock_info);
 
1386   my $do_qty       = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
 
1387   my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1390   print $form->parse_html_template('do/set_stock_in_out', {
 
1391     qty_matches => $do_qty == $transfer_qty,
 
1394   $main::lxdebug->leave_sub();
 
1397 sub stock_out_form {
 
1398   $main::lxdebug->enter_sub();
 
1400   my $form     = $main::form;
 
1401   my %myconfig = %main::myconfig;
 
1402   my $locale   = $main::locale;
 
1404   $form->{title} = $locale->text('Release From Stock');
 
1406   my $part_info  = IC->get_basic_part_info('id' => $form->{parts_id});
 
1408   my $units      = AM->retrieve_units(\%myconfig, $form);
 
1409   my $units_data = AM->unit_select_data($units, undef, undef, $part_info->{unit});
 
1411   my @contents   = DO->get_item_availability('parts_id' => $form->{parts_id});
 
1413   my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
 
1415   if (!$form->{delivered}) {
 
1416     foreach my $row (@contents) {
 
1417       $row->{available_qty} = $form->format_amount_units('amount'      => $row->{qty} * 1,
 
1418                                                          'part_unit'   => $part_info->{unit},
 
1419                                                          'conv_units'  => 'convertible_not_smaller',
 
1422       foreach my $sinfo (@{ $stock_info }) {
 
1423         next if (($row->{bin_id}       != $sinfo->{bin_id}) ||
 
1424                  ($row->{warehouse_id} != $sinfo->{warehouse_id}) ||
 
1425                  ($row->{chargenumber} ne $sinfo->{chargenumber}) ||
 
1426                  ($row->{bestbefore}   ne $sinfo->{bestbefore}));
 
1428         map { $row->{"stock_$_"} = $sinfo->{$_} } qw(qty unit error delivery_order_items_stock_id);
 
1433     get_basic_bin_wh_info($stock_info);
 
1435     foreach my $sinfo (@{ $stock_info }) {
 
1436       map { $sinfo->{"stock_$_"} = $sinfo->{$_} } qw(qty unit);
 
1440   $form->header(no_layout => 1);
 
1441   print $form->parse_html_template('do/stock_out_form', { 'UNITS'      => $units_data,
 
1442                                                           'WHCONTENTS' => $form->{delivered} ? $stock_info : \@contents,
 
1443                                                           'PART_INFO'  => $part_info, });
 
1445   $main::lxdebug->leave_sub();
 
1449   $main::lxdebug->enter_sub();
 
1451   my $form     = $main::form;
 
1452   my %myconfig = %main::myconfig;
 
1453   my $locale   = $main::locale;
 
1455   my $stock_info = [];
 
1457   foreach my $i (1 .. $form->{rowcount}) {
 
1458     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1460     next if ($form->{"qty_$i"} <= 0);
 
1462     push @{ $stock_info }, {
 
1463       'warehouse_id' => $form->{"warehouse_id_$i"},
 
1464       'bin_id'       => $form->{"bin_id_$i"},
 
1465       'chargenumber' => $form->{"chargenumber_$i"},
 
1466       'bestbefore'   => $form->{"bestbefore_$i"},
 
1467       'qty'          => $form->{"qty_$i"},
 
1468       'unit'         => $form->{"unit_$i"},
 
1470       'delivery_order_items_stock_id'  => $form->{"delivery_order_items_stock_id_$i"},
 
1474   my @errors     = DO->check_stock_availability('requests' => $stock_info,
 
1475                                                 'parts_id' => $form->{parts_id});
 
1477   $form->{stock} = YAML::Dump($stock_info);
 
1480     $form->{ERRORS} = [];
 
1481     map { push @{ $form->{ERRORS} }, $locale->text('Error in row #1: The quantity you entered is bigger than the stocked quantity.', $_->{row}); } @errors;
 
1482     stock_in_out_form();
 
1485     _stock_in_out_set_qty_display($stock_info);
 
1487     my $do_qty       = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
 
1488     my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1491     print $form->parse_html_template('do/set_stock_in_out', {
 
1492       qty_matches => $do_qty == $transfer_qty,
 
1496   $main::lxdebug->leave_sub();
 
1500   $main::lxdebug->enter_sub();
 
1502   my $form     = $main::form;
 
1503   my %myconfig = %main::myconfig;
 
1504   my $locale   = $main::locale;
 
1506   if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
 
1507     $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred in.'));
 
1510   save(no_redirect => 1);
 
1512   my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_in_${_}"} } (1 .. $form->{rowcount});
 
1516     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1517     my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1520     $form->{ERRORS}   = [];
 
1522     foreach my $i (1 .. $form->{rowcount}) {
 
1523       next unless ($form->{"id_$i"} && $form->{"stock_in_$i"});
 
1525       my $row_sum_base_qty = 0;
 
1526       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1528       foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_in_$i"}) }) {
 
1529         $request->{parts_id}  = $form->{"id_$i"};
 
1530         $row_sum_base_qty    += $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
 
1532         $request->{project_id} = $form->{"project_id_$i"} || $form->{globalproject_id};
 
1534         push @all_requests, $request;
 
1537       next if (0 == $row_sum_base_qty);
 
1539       my $do_base_qty = $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1541 #      if ($do_base_qty != $row_sum_base_qty) {
 
1542 #        push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no stock at all or the full quantity of #2 #3.',
 
1543 #                                                 $i, $form->{"qty_$i"}, $form->{"unit_$i"});
 
1547     if (@{ $form->{ERRORS} }) {
 
1548       push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
 
1550       set_headings('edit');
 
1552       $main::lxdebug->leave_sub();
 
1554       $::dispatcher->end_request;
 
1558   DO->transfer_in_out('direction' => 'in',
 
1559                       'requests'  => \@all_requests);
 
1561   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1563   $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id});
 
1566   $main::lxdebug->leave_sub();
 
1570   $main::lxdebug->enter_sub();
 
1572   my $form     = $main::form;
 
1573   my %myconfig = %main::myconfig;
 
1574   my $locale   = $main::locale;
 
1576   if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
 
1577     $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred out.'));
 
1580   save(no_redirect => 1);
 
1582   my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_out_${_}"} } (1 .. $form->{rowcount});
 
1586     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1587     my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1590     $form->{ERRORS}   = [];
 
1592     foreach my $i (1 .. $form->{rowcount}) {
 
1593       next unless ($form->{"id_$i"} && $form->{"stock_out_$i"});
 
1595       my $row_sum_base_qty = 0;
 
1596       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1598       foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_out_$i"}) }) {
 
1599         $request->{parts_id} = $form->{"id_$i"};
 
1600         $request->{base_qty} = $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
 
1601         $request->{project_id} = $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id};
 
1603         my $map_key          = join '--', ($form->{"id_$i"}, @{$request}{qw(warehouse_id bin_id chargenumber bestbefore)});
 
1605         $request_map{$map_key}                 ||= $request;
 
1606         $request_map{$map_key}->{sum_base_qty} ||= 0;
 
1607         $request_map{$map_key}->{sum_base_qty}  += $request->{base_qty};
 
1608         $row_sum_base_qty                       += $request->{base_qty};
 
1610         push @all_requests, $request;
 
1613       next if (0 == $row_sum_base_qty);
 
1615       my $do_base_qty = $form->{"qty_$i"} * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1617 #      if ($do_base_qty != $row_sum_base_qty) {
 
1618 #        push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no transfer at all or the full quantity of #2 #3.',
 
1619 #                                                 $i, $form->{"qty_$i"}, $form->{"unit_$i"});
 
1624       my @bin_ids      = map { $_->{bin_id} } values %request_map;
 
1625       my %bin_info_map = WH->get_basic_bin_info('id' => \@bin_ids);
 
1626       my @contents     = DO->get_item_availability('parts_id' => \@part_ids);
 
1628       foreach my $inv (@contents) {
 
1629         my $map_key = join '--', @{$inv}{qw(parts_id warehouse_id bin_id chargenumber bestbefore)};
 
1631         next unless ($request_map{$map_key});
 
1633         my $request    = $request_map{$map_key};
 
1634         $request->{ok} = $request->{sum_base_qty} <= $inv->{qty};
 
1637       foreach my $request (values %request_map) {
 
1638         next if ($request->{ok});
 
1640         my $pinfo = $part_info_map{$request->{parts_id}};
 
1641         my $binfo = $bin_info_map{$request->{bin_id}};
 
1643         if ($::instance_conf->get_show_bestbefore) {
 
1644             push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, #5, for the transfer of #6.",
 
1645                                                      $pinfo->{description},
 
1646                                                      $binfo->{warehouse_description},
 
1647                                                      $binfo->{bin_description},
 
1648                                                      $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
 
1649                                                      $request->{bestbefore} ? $locale->text('bestbefore #1', $request->{bestbefore}) : $locale->text('no bestbefore'),
 
1650                                                      $form->format_amount_units('amount'      => $request->{sum_base_qty},
 
1651                                                                                 'part_unit'   => $pinfo->{unit},
 
1652                                                                                 'conv_units'  => 'convertible_not_smaller'));
 
1654             push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, for the transfer of #5.",
 
1655                                                      $pinfo->{description},
 
1656                                                      $binfo->{warehouse_description},
 
1657                                                      $binfo->{bin_description},
 
1658                                                      $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
 
1659                                                      $form->format_amount_units('amount'      => $request->{sum_base_qty},
 
1660                                                                                 'part_unit'   => $pinfo->{unit},
 
1661                                                                                 'conv_units'  => 'convertible_not_smaller'));
 
1666     if (@{ $form->{ERRORS} }) {
 
1667       push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
 
1669       set_headings('edit');
 
1671       $main::lxdebug->leave_sub();
 
1673       $::dispatcher->end_request;
 
1676   DO->transfer_in_out('direction' => 'out',
 
1677                       'requests'  => \@all_requests);
 
1679   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1681   $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id});
 
1684   $main::lxdebug->leave_sub();
 
1688   $main::lxdebug->enter_sub();
 
1690   my $form     = $main::form;
 
1692   DO->close_orders('ids' => [ $form->{id} ]);
 
1694   $form->{closed} = 1;
 
1698   $main::lxdebug->leave_sub();
 
1702   $::lxdebug->enter_sub;
 
1704   $::auth->assert('purchase_delivery_order_edit | sales_delivery_order_edit');
 
1707   retrieve_partunits();
 
1709   my $new_rowcount = $::form->{"rowcount"} * 1 + 1;
 
1710   $::form->{"project_id_${new_rowcount}"} = $::form->{"globalproject_id"};
 
1712   $::form->language_payment(\%::myconfig);
 
1714   Common::webdav_folder($::form);
 
1717   display_row(++$::form->{rowcount});
 
1720   $::lxdebug->leave_sub;
 
1724   call_sub($main::form->{yes_nextsub});
 
1728   call_sub($main::form->{no_nextsub});
 
1732   call_sub($main::form->{update_nextsub} || $main::form->{nextsub} || 'update_delivery_order');
 
1736   my $form     = $main::form;
 
1737   my $locale   = $main::locale;
 
1739   foreach my $action (qw(update print save transfer_out transfer_out_default sort
 
1740                          transfer_in transfer_in_default mark_closed save_as_new invoice delete)) {
 
1741     if ($form->{"action_${action}"}) {
 
1747   $form->error($locale->text('No action defined.'));
 
1750 sub transfer_out_default {
 
1751   $main::lxdebug->enter_sub();
 
1753   my $form     = $main::form;
 
1755   transfer_in_out_default('direction' => 'out');
 
1757   $main::lxdebug->leave_sub();
 
1760 sub transfer_in_default {
 
1761   $main::lxdebug->enter_sub();
 
1763   my $form     = $main::form;
 
1765   transfer_in_out_default('direction' => 'in');
 
1767   $main::lxdebug->leave_sub();
 
1770 # Falls das Standardlagerverfahren aktiv ist, wird
 
1771 # geprüft, ob alle Standardlagerplätze für die Auslager-
 
1772 # artikel vorhanden sind UND ob die Warenmenge ausreicht zum
 
1773 # Auslagern. Falls nicht wird entsprechend eine Fehlermeldung
 
1774 # generiert. Offen Chargennummer / bestbefore wird nicht berücksichtigt
 
1775 sub transfer_in_out_default {
 
1776   $main::lxdebug->enter_sub();
 
1778   my $form     = $main::form;
 
1779   my %myconfig = %main::myconfig;
 
1780   my $locale   = $main::locale;
 
1783   my (%missing_default_bins, %qty_parts, @all_requests, %part_info_map, $default_warehouse_id, $default_bin_id);
 
1785   Common::check_params(\%params, qw(direction));
 
1787   # entsprechende defaults holen, falls standardlagerplatz verwendet werden soll
 
1788   if ($::instance_conf->get_transfer_default_use_master_default_bin) {
 
1789     $default_warehouse_id = $::instance_conf->get_warehouse_id;
 
1790     $default_bin_id       = $::instance_conf->get_bin_id;
 
1794   my @part_ids = map { $form->{"id_${_}"} } (1 .. $form->{rowcount});
 
1796     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1797     %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1798     foreach my $i (1 .. $form->{rowcount}) {
 
1799       next unless ($form->{"id_$i"});
 
1800       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1801       my $qty =   $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1803       $form->show_generic_error($locale->text("Cannot transfer negative entries." )) if ($qty < 0);
 
1804       # if we do not want to transfer services and this part is a service, set qty to zero
 
1805       # ... and do not create a hash entry in %qty_parts below (will skip check for bins for the transfer == out case)
 
1806       # ... 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)
 
1808       $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});
 
1809       $qty_parts{$form->{"id_$i"}} += $qty;
 
1811         delete $qty_parts{$form->{"id_$i"}} unless $qty_parts{$form->{"id_$i"}};
 
1812         undef $form->{"stock_in_$i"};
 
1815       $part_info_map{$form->{"id_$i"}}{bin_id}       ||= $default_bin_id;
 
1816       $part_info_map{$form->{"id_$i"}}{warehouse_id} ||= $default_warehouse_id;
 
1818       push @all_requests, ($qty == 0) ? { } : {
 
1819                         'chargenumber' => '',  #?? die müsste entsprechend geholt werden
 
1820                         #'bestbefore' => undef, # TODO wird nicht berücksichtigt
 
1821                         'bin_id' => $part_info_map{$form->{"id_$i"}}{bin_id},
 
1823                         'parts_id' => $form->{"id_$i"},
 
1824                         'comment' => $locale->text("Default transfer delivery order"),
 
1825                         'unit' => $part_info_map{$form->{"id_$i"}}{unit},
 
1826                         'warehouse_id' => $part_info_map{$form->{"id_$i"}}{warehouse_id},
 
1827                         'oe_id' => $form->{id},
 
1828                         'project_id' => $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id}
 
1832     # jetzt wird erst überprüft, ob die Stückzahl entsprechend stimmt.
 
1833     # check if bin (transfer in and transfer out and qty (transfer out) is correct
 
1834     foreach my $key (keys %qty_parts) {
 
1836       $missing_default_bins{$key}{missing_bin} = 1 unless ($part_info_map{$key}{bin_id});
 
1837       next unless ($part_info_map{$key}{bin_id}); # abbruch
 
1839       if ($params{direction} eq 'out') {  # wird nur für ausgehende Mengen benötigt
 
1840         my ($max_qty, $error) = WH->get_max_qty_parts_bin(parts_id => $key, bin_id => $part_info_map{$key}{bin_id});
 
1842           # wir können nicht entscheiden, welche charge oder mhd (bestbefore) ausgewählt sein soll
 
1843           # deshalb rückmeldung nach oben geben, manuell auszulagern
 
1844           # TODO Bei nur einem Treffer mit Charge oder bestbefore wäre das noch möglich
 
1845           $missing_default_bins{$key}{chargenumber} = 1;
 
1847         if ($max_qty < $qty_parts{$key}){
 
1848           $missing_default_bins{$key}{missing_qty} = $max_qty - $qty_parts{$key};
 
1854   # Abfrage für Fehlerbehandlung (nur bei direction == out)
 
1855   if (scalar (keys %missing_default_bins)) {
 
1857     foreach my $fehler (keys %missing_default_bins) {
 
1859       my $ware = WH->get_part_description(parts_id => $fehler);
 
1860       if ($missing_default_bins{$fehler}{missing_bin}){
 
1861         $fehlertext .= "Kein Standardlagerplatz definiert bei $ware <br>";
 
1863       if ($missing_default_bins{$fehler}{missing_qty}) {  # missing_qty
 
1864         $fehlertext .= "Es fehlen " . $missing_default_bins{$fehler}{missing_qty}*-1 .
 
1865                        " von $ware auf dem Standard-Lagerplatz " . $part_info_map{$fehler}{bin} .   " zum Auslagern<br>";
 
1867       if ($missing_default_bins{$fehler}{chargenumber}){
 
1868         $fehlertext .= "Die Ware hat eine Chargennummer oder eine Mindesthaltbarkeit definiert.
 
1869                         Hier kann man nicht automatisch entscheiden.
 
1870                         Bitte diesen Lieferschein manuell auslagern.
 
1873       # auslagern soll immer gehen, auch wenn nicht genügend auf lager ist.
 
1874       # der lagerplatz ist hier extra konfigurierbar, bspw. Lager-Korrektur mit
 
1875       # Lagerplatz Lagerplatz-Korrektur
 
1876       my $default_warehouse_id_ignore_onhand = $::instance_conf->get_warehouse_id_ignore_onhand;
 
1877       my $default_bin_id_ignore_onhand       = $::instance_conf->get_bin_id_ignore_onhand;
 
1878       if ($::instance_conf->get_transfer_default_ignore_onhand && $default_bin_id_ignore_onhand) {
 
1879         # entsprechende defaults holen
 
1880         # falls chargenumber, bestbefore oder anzahl nicht stimmt, auf automatischen
 
1881         # lagerplatz wegbuchen!
 
1882         foreach (@all_requests) {
 
1883           if ($_->{parts_id} eq $fehler){
 
1884           $_->{bin_id}        = $default_bin_id_ignore_onhand;
 
1885           $_->{warehouse_id}  = $default_warehouse_id_ignore_onhand;
 
1889         #$main::lxdebug->message(0, 'Fehlertext: ' . $fehlertext);
 
1890         $form->show_generic_error($locale->text("Cannot transfer. <br> Reason:<br>#1", $fehlertext ));
 
1896   # hier der eigentliche fallunterschied für in oder out
 
1897   my $prefix   = $params{direction} eq 'in' ? 'in' : 'out';
 
1899   # dieser array_ref ist für DO->save da:
 
1900   # einmal die all_requests in YAML verwandeln, damit delivery_order_items_stock
 
1901   # gefüllt werden kann.
 
1902   # could be dumped to the form in the first loop,
 
1903   # but maybe bin_id and warehouse_id has changed to the "korrekturlager" with
 
1904   # allowed negative qty ($::instance_conf->get_warehouse_id_ignore_onhand) ...
 
1906   foreach (@all_requests){
 
1908     next unless scalar(%{ $_ });
 
1909     $form->{"stock_${prefix}_$i"} = YAML::Dump([$_]);
 
1912   save(no_redirect => 1); # Wir können auslagern, deshalb beleg speichern
 
1913                           # und in delivery_order_items_stock speichern
 
1915   # ... and fill back the persistent dois_id for inventory fk
 
1916   undef (@all_requests);
 
1917   foreach my $i (1 .. $form->{rowcount}) {
 
1918     next unless ($form->{"id_$i"} && $form->{"stock_${prefix}_$i"});
 
1919     push @all_requests, @{ DO->unpack_stock_information('packed' => $form->{"stock_${prefix}_$i"}) };
 
1921   DO->transfer_in_out('direction' => $prefix,
 
1922                       'requests'  => \@all_requests);
 
1924   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1926   $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'out';
 
1927   $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'in';
 
1933   $main::lxdebug->enter_sub();
 
1937   my $form     = $main::form;
 
1940   save(no_redirect => 1); # has to be done, at least for newly added positions
 
1942   # hashify partnumbers, positions. key is delivery_order_items_id
 
1943   for my $i (1 .. ($form->{rowcount}) ) {
 
1944     $temp_hash{$form->{"delivery_order_items_id_$i"}} = { runningnumber => $form->{"runningnumber_$i"}, partnumber => $form->{"partnumber_$i"} };
 
1945     if ($form->{id} && $form->{"discount_$i"}) {
 
1946       # prepare_order assumes a db value if there is a form->id and multiplies *100
 
1947       # We hope for new controller code (no more format_amount/parse_amount distinction)
 
1948       $form->{"discount_$i"} /=100;
 
1951   # naturally sort partnumbers and get a sorted array of doi_ids
 
1952   my @sorted_doi_ids =  sort { Sort::Naturally::ncmp($temp_hash{$a}->{"partnumber"}, $temp_hash{$b}->{"partnumber"}) }  keys %temp_hash;
 
1957   for (@sorted_doi_ids) {
 
1958     $form->{"runningnumber_$temp_hash{$_}->{runningnumber}"} = $new_number;
 
1961   # all parse_amounts changes are in form (i.e. , to .) therefore we need
 
1962   # another format_amount to change it back, for the next save ;-(
 
1963   # works great except for row discounts (see above comment)
 
1967     $main::lxdebug->leave_sub();
 
1979 do.pl - Script for all calls to delivery order
 
1987 Sorts all position with Natural Sort. Can be activated in form_footer.html like this
 
1988 C<E<lt>input class="submit" type="submit" name="action_sort" id="sort_button" value="[% 'Sort and Save' | $T8 %]"E<gt>>
 
1994 Sort and Save can be implemented as an optional button if configuration ca be set by client config.
 
1995 Example coding for database scripts and templates in (git show af2f24b8), check also
 
1996 autogeneration for rose (scripts/rose_auto_create_model.pl --h)