2 #=====================================================================
 
   5 # Based on SQL-Ledger Version 2.1.9
 
   6 # Web http://www.lx-office.org
 
   8 #=====================================================================
 
   9 # SQL-Ledger, Accounting
 
  10 # Copyright (c) 1998-2003
 
  12 #  Author: Dieter Simader
 
  13 #   Email: dsimader@sql-ledger.org
 
  14 #     Web: http://www.sql-ledger.org
 
  17 # This program is free software; you can redistribute it and/or modify
 
  18 # it under the terms of the GNU General Public License as published by
 
  19 # the Free Software Foundation; either version 2 of the License, or
 
  20 # (at your option) any later version.
 
  22 # This program is distributed in the hope that it will be useful,
 
  23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  25 # GNU General Public License for more details.
 
  26 # You should have received a copy of the GNU General Public License
 
  27 # along with this program; if not, write to the Free Software
 
  28 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
  30 #======================================================================
 
  33 #======================================================================
 
  36 use List::MoreUtils qw(uniq);
 
  37 use List::Util qw(max sum);
 
  38 use POSIX qw(strftime);
 
  40 use SL::DB::DeliveryOrder;
 
  44 use SL::MoreCommon qw(ary_diff restore_form save_form);
 
  45 use SL::ReportGenerator;
 
  48 use Sort::Naturally ();
 
  49 require "bin/mozilla/common.pl";
 
  50 require "bin/mozilla/io.pl";
 
  51 require "bin/mozilla/reportgenerator.pl";
 
  60   $main::auth->assert($main::form->{type} . '_edit');
 
  64   $main::lxdebug->enter_sub();
 
  70   my $form     = $main::form;
 
  71   my $locale   = $main::locale;
 
  73   if ($form->{type} eq 'purchase_delivery_order') {
 
  74     $form->{vc}    = 'vendor';
 
  75     $form->{title} = $action eq "edit" ? $locale->text('Edit Purchase Delivery Order') : $locale->text('Add Purchase Delivery Order');
 
  77     $form->{vc}    = 'customer';
 
  78     $form->{title} = $action eq "edit" ? $locale->text('Edit Sales Delivery Order') : $locale->text('Add Sales Delivery Order');
 
  81   $form->{heading} = $locale->text('Delivery Order');
 
  83   $main::lxdebug->leave_sub();
 
  87   $main::lxdebug->enter_sub();
 
  91   if (($::form->{type} =~ /purchase/) && !$::instance_conf->get_allow_new_purchase_invoice) {
 
  92     $::form->show_generic_error($::locale->text("You do not have the permissions to access this function."));
 
  95   my $form     = $main::form;
 
  99   $form->{show_details} = $::myconfig{show_form_details};
 
 100   $form->{callback} = build_std_url('action=add', 'type', 'vc') unless ($form->{callback});
 
 106   $main::lxdebug->leave_sub();
 
 110   $main::lxdebug->enter_sub();
 
 114   my $form     = $main::form;
 
 116   $form->{show_details} = $::myconfig{show_form_details};
 
 118   # show history button
 
 119   $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
 
 120   #/show hhistory button
 
 122   $form->{simple_save} = 0;
 
 124   set_headings("edit");
 
 126   # editing without stuff to edit? try adding it first
 
 127   if ($form->{rowcount} && !$form->{print_and_save}) {
 
 128 #     map { $id++ if $form->{"multi_id_$_"} } (1 .. $form->{rowcount});
 
 132       undef $form->{rowcount};
 
 134       $main::lxdebug->leave_sub();
 
 137   } elsif (!$form->{id}) {
 
 139     $main::lxdebug->leave_sub();
 
 143   my ($language_id, $printer_id);
 
 144   if ($form->{print_and_save}) {
 
 145     $form->{action}   = "dispatcher";
 
 146     $form->{action_print} = "1";
 
 147     $form->{resubmit} = 1;
 
 148     $language_id      = $form->{language_id};
 
 149     $printer_id       = $form->{printer_id};
 
 152   set_headings("edit");
 
 157   if ($form->{print_and_save}) {
 
 158     $form->{language_id} = $language_id;
 
 159     $form->{printer_id}  = $printer_id;
 
 164   $main::lxdebug->leave_sub();
 
 168   $main::lxdebug->enter_sub();
 
 172   my $form     = $main::form;
 
 173   my %myconfig = %main::myconfig;
 
 175   # retrieve order/quotation
 
 176   my $editing = $form->{id};
 
 178   DO->retrieve('vc'  => $form->{vc},
 
 179                'ids' => $form->{id});
 
 181   $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes delivery_term_id currency));
 
 183   # get customer / vendor
 
 184   if ($form->{vc} eq 'vendor') {
 
 185     IR->get_vendor(\%myconfig, \%$form);
 
 186     $form->{discount} = $form->{vendor_discount};
 
 188     IS->get_customer(\%myconfig, \%$form);
 
 189     $form->{discount} = $form->{customer_discount};
 
 192   $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id delivery_term_id));
 
 193   $form->restore_vars(qw(currency)) if ($form->{id} || $form->{convert_from_oe_ids});
 
 194   $form->restore_vars(qw(taxincluded)) if $form->{id};
 
 195   $form->restore_vars(qw(salesman_id)) if $editing;
 
 197   $main::lxdebug->leave_sub();
 
 201   $main::lxdebug->enter_sub();
 
 205   my $form     = $main::form;
 
 206   my %myconfig = %main::myconfig;
 
 208   $form->{formname} = $form->{type} unless $form->{formname};
 
 211   foreach my $ref (@{ $form->{form_details} }) {
 
 212     $form->{rowcount} = ++$i;
 
 214     map { $form->{"${_}_$i"} = $ref->{$_} } keys %{$ref};
 
 216   for my $i (1 .. $form->{rowcount}) {
 
 218       $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100);
 
 220       $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
 
 222     my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
 
 224     my $decimalplaces = ($dec > 2) ? $dec : 2;
 
 226     # copy reqdate from deliverydate for invoice -> order conversion
 
 227     $form->{"reqdate_$i"} = $form->{"deliverydate_$i"} unless $form->{"reqdate_$i"};
 
 229     $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
 
 230     $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}, $decimalplaces);
 
 232     (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
 
 233     $dec_qty = length $dec_qty;
 
 234     $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
 
 237   $main::lxdebug->leave_sub();
 
 240 sub setup_do_action_bar {
 
 241   my @transfer_qty   = qw(kivi.SalesPurchase.delivery_order_check_transfer_qty);
 
 242   my @req_trans_desc = qw(kivi.SalesPurchase.check_transaction_description) x!!$::instance_conf->get_require_transaction_description_ps;
 
 243   my $is_customer    = $::form->{vc} eq 'customer';
 
 245   for my $bar ($::request->layout->get('actionbar')) {
 
 249           submit    => [ '#form', { action => "update" } ],
 
 250           id        => 'update_button',
 
 251           accesskey => 'enter',
 
 257           submit   => [ '#form', { action => "save" } ],
 
 258           checks   => [ 'kivi.validate_form' ],
 
 259           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 263           submit   => [ '#form', { action => "save_as_new" } ],
 
 264           checks   => [ 'kivi.validate_form' ],
 
 265           disabled => !$::form->{id},
 
 268           t8('Mark as closed'),
 
 269           submit   => [ '#form', { action => "mark_closed" } ],
 
 270           checks   => [ 'kivi.validate_form' ],
 
 271           confirm  => t8('This will remove the delivery order from showing as open even if contents are not delivered. Proceed?'),
 
 272           disabled => !$::form->{id}    ? t8('This record has not been saved yet.')
 
 273                     : $::form->{closed} ? t8('This record has already been closed.')
 
 276       ], # end of combobox "Save"
 
 280         submit   => [ '#form', { action => "delete" } ],
 
 281         confirm  => t8('Do you really want to delete this object?'),
 
 282         disabled => !$::form->{id}                                                                              ? t8('This record has not been saved yet.')
 
 283                   : $::form->{delivered}                                                                        ? t8('This record has already been delivered.')
 
 284                   : ($::form->{vc} eq 'customer' && !$::instance_conf->get_sales_delivery_order_show_delete)    ? t8('Deleting this type of record has been disabled in the configuration.')
 
 285                   : ($::form->{vc} eq 'vendor'   && !$::instance_conf->get_purchase_delivery_order_show_delete) ? t8('Deleting this type of record has been disabled in the configuration.')
 
 292           submit   => [ '#form', { action => "transfer_out" } ],
 
 293           checks   => [ 'kivi.validate_form', @transfer_qty ],
 
 294           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 295           only_if  => $is_customer,
 
 298           t8('Transfer out via default'),
 
 299           submit   => [ '#form', { action => "transfer_out_default" } ],
 
 300           checks   => [ 'kivi.validate_form' ],
 
 301           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 302           only_if  => $is_customer && $::instance_conf->get_transfer_default,
 
 306           submit   => [ '#form', { action => "transfer_in" } ],
 
 307           checks   => [ 'kivi.validate_form', @transfer_qty ],
 
 308           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 309           only_if  => !$is_customer,
 
 312           t8('Transfer in via default'),
 
 313           submit   => [ '#form', { action => "transfer_in_default" } ],
 
 314           checks   => [ 'kivi.validate_form' ],
 
 315           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 316           only_if  => !$is_customer && $::instance_conf->get_transfer_default,
 
 318       ], # end of combobox "Transfer out"
 
 325         submit => [ '#form', { action => "invoice" } ],
 
 326         disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
 
 330         action => [ t8('Export') ],
 
 333           call   => [ 'kivi.SalesPurchase.show_print_dialog' ],
 
 334           checks => [ 'kivi.validate_form' ],
 
 338           call   => [ 'kivi.SalesPurchase.show_email_dialog' ],
 
 339           checks => [ 'kivi.validate_form' ],
 
 341       ], # end of combobox "Export"
 
 344         action => [ t8('more') ],
 
 347           call     => [ 'set_history_window', $::form->{id} * 1, 'id' ],
 
 348           disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
 
 352           call     => [ 'follow_up_window' ],
 
 353           disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
 
 355       ], # end if combobox "more"
 
 358   $::request->layout->add_javascripts('kivi.Validator.js');
 
 361 sub setup_do_search_action_bar {
 
 364   for my $bar ($::request->layout->get('actionbar')) {
 
 368         submit    => [ '#form' ],
 
 369         accesskey => 'enter',
 
 370         checks    => [ 'kivi.validate_form' ],
 
 374   $::request->layout->add_javascripts('kivi.Validator.js');
 
 377 sub setup_do_orders_action_bar {
 
 380   for my $bar ($::request->layout->get('actionbar')) {
 
 384         submit    => [ '#form', { action => 'invoice_multi' } ],
 
 385         checks    => [ [ 'kivi.check_if_entries_selected', '#form tbody input[type=checkbox]' ] ],
 
 386         accesskey => 'enter',
 
 390         call   => [ 'kivi.SalesPurchase.show_print_dialog', 'js:kivi.MassDeliveryOrderPrint.submitMultiOrders' ],
 
 391         checks => [ [ 'kivi.check_if_entries_selected', '#form tbody input[type=checkbox]' ] ],
 
 398   $main::lxdebug->enter_sub();
 
 402   my $form     = $main::form;
 
 403   my %myconfig = %main::myconfig;
 
 405   my $class       = "SL::DB::" . ($form->{vc} eq 'customer' ? 'Customer' : 'Vendor');
 
 406   $form->{VC_OBJ} = $class->load_cached($form->{ $form->{vc} . '_id' });
 
 408   $form->{CONTACT_OBJ}   = $form->{cp_id} ? SL::DB::Contact->load_cached($form->{cp_id}) : undef;
 
 409   my $current_employee   = SL::DB::Manager::Employee->current;
 
 410   $form->{employee_id}   = $form->{old_employee_id} if $form->{old_employee_id};
 
 411   $form->{salesman_id}   = $form->{old_salesman_id} if $form->{old_salesman_id};
 
 412   $form->{employee_id} ||= $current_employee->id;
 
 413   $form->{salesman_id} ||= $current_employee->id;
 
 415   my $vc = $form->{vc} eq "customer" ? "customers" : "vendors";
 
 416   $form->get_lists("price_factors"  => "ALL_PRICE_FACTORS",
 
 417                    "business_types" => "ALL_BUSINESS_TYPES",
 
 419   $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all_sorted;
 
 422   my @old_project_ids = uniq grep { $_ } map { $_ * 1 } ($form->{"globalproject_id"}, map { $form->{"project_id_$_"} } 1..$form->{"rowcount"});
 
 423   my @old_ids_cond    = @old_project_ids ? (id => \@old_project_ids) : ();
 
 425   if (($vc eq 'customers') && $::instance_conf->get_customer_projects_only_in_sales) {
 
 428         customer_id          => $::form->{customer_id},
 
 429         billable_customer_id => $::form->{customer_id},
 
 434       and => [ active => 1, @customer_cond ],
 
 438   $::form->{ALL_PROJECTS}          = SL::DB::Manager::Project->get_all_sorted(query => \@conditions);
 
 439   $::form->{ALL_EMPLOYEES}         = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{employee_id},  deleted => 0 ] ]);
 
 440   $::form->{ALL_SALESMEN}          = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{salesman_id},  deleted => 0 ] ]);
 
 441   $::form->{ALL_SHIPTO}            = SL::DB::Manager::Shipto->get_all_sorted(query => [
 
 442     or => [ trans_id  => $::form->{"$::form->{vc}_id"} * 1, and => [ shipto_id => $::form->{shipto_id} * 1, trans_id => undef ] ]
 
 444   $::form->{ALL_CONTACTS}          = SL::DB::Manager::Contact->get_all_sorted(query => [
 
 446       cp_cv_id => $::form->{"$::form->{vc}_id"} * 1,
 
 449         cp_id    => $::form->{cp_id} * 1
 
 454   my $dispatch_to_popup = '';
 
 455   if ($form->{resubmit} && ($form->{format} eq "html")) {
 
 456     $dispatch_to_popup  = "window.open('about:blank','Beleg'); document.do.target = 'Beleg';";
 
 457     $dispatch_to_popup .= "document.do.submit();";
 
 458   } elsif ($form->{resubmit}) {
 
 459     # emulate click for resubmitting actions
 
 460     $dispatch_to_popup  = "document.do.${_}.click(); " for grep { /^action_/ } keys %$form;
 
 462   $::request->{layout}->add_javascripts_inline("\$(function(){$dispatch_to_popup});");
 
 465   $form->{follow_up_trans_info} = $form->{donumber} .'('. $form->{VC_OBJ}->name .')' if $form->{VC_OBJ};
 
 467   $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.File kivi.MassDeliveryOrderPrint kivi.SalesPurchase kivi.Part kivi.CustomerVendor kivi.Validator ckeditor/ckeditor ckeditor/adapters/jquery kivi.io));
 
 470   push @custom_hidden, map { "shiptocvar_" . $_->name } @{ SL::DB::Manager::CustomVariableConfig->get_all(where => [ module => 'ShipTo' ]) };
 
 472   $::form->{HIDDENS} = [ map { +{ name => $_, value => $::form->{$_} } } (@custom_hidden) ];
 
 474   setup_do_action_bar();
 
 477   # Fix für Bug 1082 Erwartet wird: 'abteilungsNAME--abteilungsID'
 
 478   # und Erweiterung für Bug 1760:
 
 479   # Das war leider nur ein Teil-Fix, da das Verhalten den 'Erneuern'-Knopf
 
 480   # nicht überlebt. Konsequent jetzt auf L umgestellt
 
 481   #   $ perldoc SL::Template::Plugin::L
 
 482   # Daher entsprechend nur die Anpassung in form_header
 
 483   # und in DO.pm gemacht. 4 Testfälle:
 
 484   # department_id speichern                 | i.O.
 
 485   # department_id lesen                     | i.O.
 
 486   # department leer überlebt erneuern       | i.O.
 
 487   # department nicht leer überlebt erneuern | i.O.
 
 488   # $main::lxdebug->message(0, 'ABTEILUNGS ID in form?' . $form->{department_id});
 
 489   print $form->parse_html_template('do/form_header');
 
 491   $main::lxdebug->leave_sub();
 
 495   $main::lxdebug->enter_sub();
 
 499   my $form     = $main::form;
 
 501   $form->{PRINT_OPTIONS}      = setup_sales_purchase_print_options();
 
 502   $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
 
 504   print $form->parse_html_template('do/form_footer',
 
 505     {transfer_default         => ($::instance_conf->get_transfer_default)});
 
 507   $main::lxdebug->leave_sub();
 
 510 sub update_delivery_order {
 
 511   $main::lxdebug->enter_sub();
 
 515   my $form     = $main::form;
 
 516   my %myconfig = %main::myconfig;
 
 518   set_headings($form->{"id"} ? "edit" : "add");
 
 520   $form->{insertdate} = SL::DB::DeliveryOrder->new(id => $form->{id})->load->itime_as_date if $form->{id};
 
 525   $payment_id = $form->{payment_id} if $form->{payment_id};
 
 527   my $vc = $form->{vc};
 
 528   if (($form->{"previous_${vc}_id"} || $form->{"${vc}_id"}) != $form->{"${vc}_id"}) {
 
 529     $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
 
 531     IS->get_customer(\%myconfig, $form) if $vc eq 'customer';
 
 532     IR->get_vendor(\%myconfig, $form)   if $vc eq 'vendor';
 
 535   $form->{discount} =  $form->{"$form->{vc}_discount"} if defined $form->{"$form->{vc}_discount"};
 
 536   # Problem: Wenn man ohne Erneuern einen Kunden/Lieferanten
 
 537   # wechselt, wird der entsprechende Kunden/ Lieferantenrabatt
 
 538   # nicht übernommen. Grundproblem: In Commit 82574e78
 
 539   # hab ich aus discount customer_discount und vendor_discount
 
 540   # gemacht und entsprechend an den Oberflächen richtig hin-
 
 541   # geschoben. Die damals bessere Lösung wäre gewesen:
 
 542   # In den Templates nur die hidden für form-discount wieder ein-
 
 543   # setzen dann wäre die Verrenkung jetzt nicht notwendig.
 
 544   # TODO: Ggf. Bugfix 1284, 1575 und 817 wieder zusammenführen
 
 545   # Testfälle: Kunden mit Rabatt 0 -> Rabatt 20 i.O.
 
 546   #            Kunde mit Rabatt 20 -> Rabatt 0  i.O.
 
 547   #            Kunde mit Rabatt 20 -> Rabatt 5,5 i.O.
 
 548   $form->{payment_id} = $payment_id if $form->{payment_id} eq "";
 
 550   my $i = $form->{rowcount};
 
 552   if (   ($form->{"partnumber_$i"} eq "")
 
 553       && ($form->{"description_$i"} eq "")
 
 554       && ($form->{"partsgroup_$i"}  eq "")) {
 
 561     if ($form->{type} eq 'purchase_delivery_order') {
 
 562       IR->retrieve_item(\%myconfig, $form);
 
 565       IS->retrieve_item(\%myconfig, $form);
 
 569     my $rows = scalar @{ $form->{item_list} };
 
 572       $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
 573       if( !$form->{"qty_$i"} ) {
 
 574         $form->{"qty_$i"} = 1;
 
 579         select_item(mode => $mode, pre_entered_qty => $form->{"qty_$i"});
 
 580         $::dispatcher->end_request;
 
 584         my $sellprice = $form->parse_amount(\%myconfig, $form->{"sellprice_$i"});
 
 586         map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} } keys %{ $form->{item_list}[0] };
 
 588         $form->{"marge_price_factor_$i"} = $form->{item_list}->[0]->{price_factor};
 
 591           $form->{"sellprice_$i"} = $sellprice;
 
 593           my $record        = _make_record();
 
 594           my $price_source  = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
 
 595           my $best_price    = $price_source->best_price;
 
 596           my $best_discount = $price_source->best_discount;
 
 599             $::form->{"sellprice_$i"}           = $best_price->price;
 
 600             $::form->{"active_price_source_$i"} = $best_price->source;
 
 602           if ($best_discount) {
 
 603             $::form->{"discount_$i"}               = $best_discount->discount;
 
 604             $::form->{"active_discount_source_$i"} = $best_discount->source;
 
 608         $form->{"sellprice_$i"}          = $form->format_amount(\%myconfig, $form->{"sellprice_$i"});
 
 609         $form->{"lastcost_$i"}           = $form->format_amount(\%myconfig, $form->{"lastcost_$i"});
 
 610         $form->{"qty_$i"}                = $form->format_amount(\%myconfig, $form->{"qty_$i"});
 
 611         $form->{"discount_$i"}           = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0);
 
 618       # ok, so this is a new part
 
 619       # ask if it is a part or service item
 
 621       if (   $form->{"partsgroup_$i"}
 
 622           && ($form->{"partsnumber_$i"} eq "")
 
 623           && ($form->{"description_$i"} eq "")) {
 
 625         $form->{"discount_$i"} = "";
 
 626         $form->{"not_discountable_$i"} = "";
 
 630         $form->{"id_$i"}   = 0;
 
 636   $main::lxdebug->leave_sub();
 
 640   $main::lxdebug->enter_sub();
 
 644   my $form     = $main::form;
 
 645   my %myconfig = %main::myconfig;
 
 646   my $locale   = $main::locale;
 
 648   $form->{vc} = $form->{type} eq 'purchase_delivery_order' ? 'vendor' : 'customer';
 
 650   $form->get_lists("projects"       => { "key" => "ALL_PROJECTS",
 
 652                    "business_types" => "ALL_BUSINESS_TYPES");
 
 653   $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]);
 
 654   $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all_sorted;
 
 655   $form->{title}             = $locale->text('Delivery Orders');
 
 657   setup_do_search_action_bar();
 
 661   print $form->parse_html_template('do/search');
 
 663   $main::lxdebug->leave_sub();
 
 667   $main::lxdebug->enter_sub();
 
 671   my $form     = $main::form;
 
 672   my %myconfig = %main::myconfig;
 
 673   my $locale   = $main::locale;
 
 674   my $cgi      = $::request->{cgi};
 
 676   $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.MassDeliveryOrderPrint kivi.SalesPurchase));
 
 677   ($form->{ $form->{vc} }, $form->{"$form->{vc}_id"}) = split(/--/, $form->{ $form->{vc} });
 
 679   report_generator_set_default_sort('transdate', 1);
 
 683   $form->{rowcount} = scalar @{ $form->{DO} };
 
 686     ids                     transdate               reqdate
 
 688     ordnumber               customernumber          cusordnumber
 
 689     name                    employee  salesman
 
 690     shipvia                 globalprojectnumber
 
 691     transaction_description department
 
 696   $form->{l_open}      = $form->{l_closed} = "Y" if ($form->{open}      && $form->{closed});
 
 697   $form->{l_delivered} = "Y"                     if ($form->{delivered} && $form->{notdelivered});
 
 699   $form->{title}       = $locale->text('Delivery Orders');
 
 701   my $attachment_basename = $form->{vc} eq 'vendor' ? $locale->text('purchase_delivery_order_list') : $locale->text('sales_delivery_order_list');
 
 703   my $report = SL::ReportGenerator->new(\%myconfig, $form);
 
 705   my @hidden_variables = map { "l_${_}" } @columns;
 
 706   push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber serialnumber cusordnumber
 
 707                                           transaction_description transdatefrom transdateto reqdatefrom reqdateto
 
 708                                           type vc employee_id salesman_id project_id parts_partnumber parts_description
 
 709                                           insertdatefrom insertdateto business_id all);
 
 711   my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
 
 714     'ids'                     => { raw_header_data => SL::Presenter::Tag::checkbox_tag("", id => "multi_all", checkall => "[data-checkall=1]"), align => 'center' },
 
 715     'transdate'               => { 'text' => $locale->text('Delivery Order Date'), },
 
 716     'reqdate'                 => { 'text' => $locale->text('Reqdate'), },
 
 717     'id'                      => { 'text' => $locale->text('ID'), },
 
 718     'donumber'                => { 'text' => $locale->text('Delivery Order'), },
 
 719     'ordnumber'               => { 'text' => $locale->text('Order'), },
 
 720     'customernumber'          => { 'text' => $locale->text('Customer Number'), },
 
 721     'cusordnumber'            => { 'text' => $locale->text('Customer Order Number'), },
 
 722     'name'                    => { 'text' => $form->{vc} eq 'customer' ? $locale->text('Customer') : $locale->text('Vendor'), },
 
 723     'employee'                => { 'text' => $locale->text('Employee'), },
 
 724     'salesman'                => { 'text' => $locale->text('Salesman'), },
 
 725     'shipvia'                 => { 'text' => $locale->text('Ship via'), },
 
 726     'globalprojectnumber'     => { 'text' => $locale->text('Project Number'), },
 
 727     'transaction_description' => { 'text' => $locale->text('Transaction description'), },
 
 728     'open'                    => { 'text' => $locale->text('Open'), },
 
 729     'delivered'               => { 'text' => $locale->text('Delivered'), },
 
 730     'department'              => { 'text' => $locale->text('Department'), },
 
 731     'insertdate'              => { 'text' => $locale->text('Insert Date'), },
 
 734   foreach my $name (qw(id transdate reqdate donumber ordnumber name employee salesman shipvia transaction_description department insertdate)) {
 
 735     my $sortdir                 = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
 
 736     $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
 
 739   $form->{"l_type"} = "Y";
 
 740   map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
 
 742   $column_defs{ids}->{visible} = 'HTML';
 
 744   $report->set_columns(%column_defs);
 
 745   $report->set_column_order(@columns);
 
 747   $report->set_export_options('orders', @hidden_variables, qw(sort sortdir));
 
 749   $report->set_sort_indicator($form->{sort}, $form->{sortdir});
 
 752   if ($form->{customer}) {
 
 753     push @options, $locale->text('Customer') . " : $form->{customer}";
 
 755   if ($form->{vendor}) {
 
 756     push @options, $locale->text('Vendor') . " : $form->{vendor}";
 
 758   if ($form->{cp_name}) {
 
 759     push @options, $locale->text('Contact Person') . " : $form->{cp_name}";
 
 761   if ($form->{department_id}) {
 
 762     push @options, $locale->text('Department') . " : " . SL::DB::Department->new(id => $form->{department_id})->load->description;
 
 764   if ($form->{donumber}) {
 
 765     push @options, $locale->text('Delivery Order Number') . " : $form->{donumber}";
 
 767   if ($form->{ordnumber}) {
 
 768     push @options, $locale->text('Order Number') . " : $form->{ordnumber}";
 
 770   push @options, $locale->text('Serial Number') . " : $form->{serialnumber}" if $form->{serialnumber};
 
 771   if ($form->{business_id}) {
 
 772     my $vc_type_label = $form->{vc} eq 'customer' ? $locale->text('Customer type') : $locale->text('Vendor type');
 
 773     push @options, $vc_type_label . " : " . SL::DB::Business->new(id => $form->{business_id})->load->description;
 
 775   if ($form->{transaction_description}) {
 
 776     push @options, $locale->text('Transaction description') . " : $form->{transaction_description}";
 
 778   if ($form->{parts_description}) {
 
 779     push @options, $locale->text('Part Description') . " : $form->{parts_description}";
 
 781   if ($form->{parts_partnumber}) {
 
 782     push @options, $locale->text('Part Number') . " : $form->{parts_partnumber}";
 
 784   if ( $form->{transdatefrom} or $form->{transdateto} ) {
 
 785     push @options, $locale->text('Delivery Order Date');
 
 786     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1)     if $form->{transdatefrom};
 
 787     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{transdateto},   1)     if $form->{transdateto};
 
 789   if ( $form->{reqdatefrom} or $form->{reqdateto} ) {
 
 790     push @options, $locale->text('Reqdate');
 
 791     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1)       if $form->{reqdatefrom};
 
 792     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{reqdateto},   1)       if $form->{reqdateto};
 
 794   if ( $form->{insertdatefrom} or $form->{insertdateto} ) {
 
 795     push @options, $locale->text('Insert Date');
 
 796     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1)    if $form->{insertdatefrom};
 
 797     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{insertdateto},   1)    if $form->{insertdateto};
 
 800     push @options, $locale->text('Open');
 
 802   if ($form->{closed}) {
 
 803     push @options, $locale->text('Closed');
 
 805   if ($form->{delivered}) {
 
 806     push @options, $locale->text('Delivered');
 
 808   if ($form->{notdelivered}) {
 
 809     push @options, $locale->text('Not delivered');
 
 811   push @options, $locale->text('Quick Search') . " : $form->{all}" if $form->{all};
 
 813   my $pr = SL::DB::Manager::Printer->find_by(
 
 814       printer_description => $::locale->text("sales_delivery_order_printer"));
 
 816       $form->{printer_id} = $pr->id;
 
 819   my $print_options = SL::Helper::PrintOptions->get_print_options(
 
 821       hide_language_id => 1,
 
 827   $report->set_options('top_info_text'        => join("\n", @options),
 
 828                        'raw_top_info_text'    => $form->parse_html_template('do/orders_top'),
 
 829                        'raw_bottom_info_text' => $form->parse_html_template('do/orders_bottom', { print_options => $print_options }),
 
 830                        'output_format'        => 'HTML',
 
 831                        'title'                => $form->{title},
 
 832                        'attachment_basename'  => $attachment_basename . strftime('_%Y%m%d', localtime time),
 
 834   $report->set_options_from_form();
 
 835   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
 
 837   # add sort and escape callback, this one we use for the add sub
 
 838   $form->{callback} = $href .= "&sort=$form->{sort}";
 
 840   # escape callback for href
 
 841   my $callback = $form->escape($href);
 
 843   my $edit_url       = build_std_url('action=edit', 'type', 'vc');
 
 844   my $edit_order_url = ($::instance_conf->get_feature_experimental_order)
 
 845                      ? build_std_url('script=controller.pl', 'action=Order/edit', 'type=' . ($form->{type} eq 'sales_delivery_order' ? 'sales_order' : 'purchase_order'))
 
 846                      : build_std_url('script=oe.pl',         'action=edit',       'type=' . ($form->{type} eq 'sales_delivery_order' ? 'sales_order' : 'purchase_order'));
 
 850   foreach my $dord (@{ $form->{DO} }) {
 
 851     $dord->{open}      = $dord->{closed}    ? $locale->text('No')  : $locale->text('Yes');
 
 852     $dord->{delivered} = $dord->{delivered} ? $locale->text('Yes') : $locale->text('No');
 
 854     my $row = { map { $_ => { 'data' => $dord->{$_} } } @columns };
 
 856     my $ord_id = $dord->{id};
 
 858       'raw_data' =>   $cgi->hidden('-name' => "trans_id_${idx}", '-value' => $ord_id)
 
 859                     . $cgi->checkbox('-name' => "multi_id_${idx}",' id' => "multi_id_id_".$ord_id, '-value' => 1, 'data-checkall' => 1, '-label' => ''),
 
 860       'valign'   => 'center',
 
 864     $row->{donumber}->{link}  = $edit_url       . "&id=" . E($dord->{id})      . "&callback=${callback}";
 
 865     $row->{ordnumber}->{link} = $edit_order_url . "&id=" . E($dord->{oe_id})   . "&callback=${callback}" if $dord->{oe_id};
 
 866     $report->add_data($row);
 
 871   setup_do_orders_action_bar();
 
 873   $report->generate_with_headers();
 
 875   $main::lxdebug->leave_sub();
 
 879   $main::lxdebug->enter_sub();
 
 885   my $form     = $main::form;
 
 886   my %myconfig = %main::myconfig;
 
 887   my $locale   = $main::locale;
 
 889   $form->mtime_ischanged('delivery_orders');
 
 891   $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
 
 893   $form->isblank("transdate", $locale->text('Delivery Order Date missing!'));
 
 895   $form->{donumber} =~ s/^\s*//g;
 
 896   $form->{donumber} =~ s/\s*$//g;
 
 898   my $msg = ucfirst $form->{vc};
 
 899   $form->isblank($form->{vc} . "_id", $locale->text($msg . " missing!"));
 
 901   # $locale->text('Customer missing!');
 
 902   # $locale->text('Vendor missing!');
 
 904   remove_emptied_rows();
 
 907   # if the name changed get new values
 
 908   my $vc = $form->{vc};
 
 909   if (($form->{"previous_${vc}_id"} || $form->{"${vc}_id"}) != $form->{"${vc}_id"}) {
 
 910     $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
 
 912     IS->get_customer(\%myconfig, $form) if $vc eq 'customer';
 
 913     IR->get_vendor(\%myconfig, $form)   if $vc eq 'vendor';
 
 916     $::dispatcher->end_request;
 
 919   $form->{id} = 0 if $form->{saveasnew};
 
 923   if(!exists $form->{addition}) {
 
 924     $form->{snumbers} = qq|donumber_| . $form->{donumber};
 
 925     $form->{addition} = "SAVED";
 
 928   # /saving the history
 
 930   $form->{simple_save} = 1;
 
 931   if (!$params{no_redirect} && !$form->{print_and_save}) {
 
 932     delete @{$form}{ary_diff([keys %{ $form }], [qw(login id script type cursor_fokus)])};
 
 934     $::dispatcher->end_request;
 
 936   $main::lxdebug->leave_sub();
 
 940   $main::lxdebug->enter_sub();
 
 944   my $form     = $main::form;
 
 945   my %myconfig = %main::myconfig;
 
 946   my $locale   = $main::locale;
 
 948   if ($ret = DO->delete()) {
 
 950     if(!exists $form->{addition}) {
 
 951       $form->{snumbers} = qq|donumber_| . $form->{donumber};
 
 952       $form->{addition} = "DELETED";
 
 955     # /saving the history
 
 957     $form->info($locale->text('Delivery Order deleted!'));
 
 958     $::dispatcher->end_request;
 
 961   $form->error($locale->text('Cannot delete delivery order!') . $ret);
 
 963   $main::lxdebug->leave_sub();
 
 967   $main::lxdebug->enter_sub();
 
 969   my $form     = $main::form;
 
 970   my %myconfig = %main::myconfig;
 
 971   my $locale   = $main::locale;
 
 974   $form->mtime_ischanged('delivery_orders');
 
 976   $main::auth->assert($form->{type} eq 'purchase_delivery_order' ? 'vendor_invoice_edit' : 'invoice_edit');
 
 978   $form->{convert_from_do_ids} = $form->{id};
 
 979   # if we have a reqdate (Liefertermin), this is definetely the preferred
 
 980   # deliverydate for invoices
 
 981   $form->{deliverydate}        = $form->{reqdate} || $form->{transdate};
 
 982   $form->{transdate}           = $form->{invdate} = $form->current_date(\%myconfig);
 
 983   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
 
 984   $form->{defaultcurrency}     = $form->get_default_currency(\%myconfig);
 
 988   delete @{$form}{qw(id closed delivered)};
 
 990   my ($script, $buysell);
 
 991   if ($form->{type} eq 'purchase_delivery_order') {
 
 992     $form->{title}  = $locale->text('Add Vendor Invoice');
 
 993     $form->{script} = 'ir.pl';
 
 998     $form->{title}  = $locale->text('Add Sales Invoice');
 
 999     $form->{script} = 'is.pl';
 
1004   for my $i (1 .. $form->{rowcount}) {
 
1005     map { $form->{"${_}_${i}"} = $form->parse_amount(\%myconfig, $form->{"${_}_${i}"}) if $form->{"${_}_${i}"} } qw(ship qty sellprice lastcost basefactor discount);
 
1007     # adds a customer/vendor discount, unless we have a workflow case
 
1008     # CAVEAT: has to be done, after the above parse_amount
 
1009     unless ($form->{"ordnumber"}) {
 
1010       if ($form->{discount}) { # Falls wir einen Lieferanten-/Kundenrabatt haben
 
1011         # und rabattfähig sind, dann
 
1012         unless ($form->{"not_discountable_$i"}) {
 
1013           $form->{"discount_$i"} = $form->{discount}*100; # ... nehmen wir diesen Rabatt
 
1017     $form->{"donumber_$i"} = $form->{donumber};
 
1018     $form->{"converted_from_delivery_order_items_id_$i"} = delete $form->{"delivery_order_items_id_$i"};
 
1021   $form->{type} = "invoice";
 
1024   $main::locale = Locale->new("$myconfig{countrycode}", "$script");
 
1025   $locale = $main::locale;
 
1027   require "bin/mozilla/$form->{script}";
 
1029   my $currency = $form->{currency};
 
1032   if ($form->{ordnumber}) {
 
1033     require SL::DB::Order;
 
1034     my $vc_id  = $form->{type} =~ /^sales/ ? 'customer_id' : 'vendor_id';
 
1035     if (my $order = SL::DB::Manager::Order->find_by(ordnumber => $form->{ordnumber}, $vc_id => $form->{"$vc_id"})) {
 
1037       $form->{orddate} = $order->transdate_as_date;
 
1038       $form->{$_}      = $order->$_ for qw(payment_id salesman_id taxzone_id quonumber);
 
1042   $form->{currency}     = $currency;
 
1043   $form->{exchangerate} = "";
 
1044   $form->{forex}        = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, $buysell);
 
1045   $form->{exchangerate} = $form->{forex} if ($form->{forex});
 
1050   for my $i (1 .. $form->{rowcount}) {
 
1051     $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
 
1053     my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
 
1055     my $decimalplaces = ($dec > 2) ? $dec : 2;
 
1057     # copy delivery date from reqdate for order -> invoice conversion
 
1058     $form->{"deliverydate_$i"} = $form->{"reqdate_$i"}
 
1059       unless $form->{"deliverydate_$i"};
 
1062     $form->{"sellprice_$i"} =
 
1063       $form->format_amount(\%myconfig, $form->{"sellprice_$i"},
 
1066     $form->{"lastcost_$i"} =
 
1067       $form->format_amount(\%myconfig, $form->{"lastcost_$i"},
 
1070     (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
 
1071     $dec_qty = length $dec_qty;
 
1073       $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
 
1079   $main::lxdebug->leave_sub();
 
1083   $main::lxdebug->enter_sub();
 
1085   my $form     = $main::form;
 
1086   my %myconfig = %main::myconfig;
 
1087   my $locale   = $main::locale;
 
1090   $main::auth->assert($form->{type} eq 'sales_delivery_order' ? 'invoice_edit' : 'vendor_invoice_edit');
 
1092   my @do_ids = map { $form->{"trans_id_$_"} } grep { $form->{"multi_id_$_"} } (1..$form->{rowcount});
 
1094   if (!scalar @do_ids) {
 
1095     $form->show_generic_error($locale->text('You have not selected any delivery order.'));
 
1098   map { delete $form->{$_} } grep { m/^(?:trans|multi)_id_\d+/ } keys %{ $form };
 
1100   if (!DO->retrieve('vc' => $form->{vc}, 'ids' => \@do_ids)) {
 
1101     $form->show_generic_error($form->{vc} eq 'customer' ?
 
1102                               $locale->text('You cannot create an invoice for delivery orders for different customers.') :
 
1103                               $locale->text('You cannot create an invoice for delivery orders from different vendors.'),
 
1104                               'back_button' => 1);
 
1107   my $source_type              = $form->{type};
 
1108   $form->{convert_from_do_ids} = join ' ', @do_ids;
 
1109   # bei der auswahl von mehreren Lieferscheinen fuer eine Rechnung, die einfach in donumber_array
 
1110   # zwischenspeichern (DO.pm) und als ' '-separierte Liste wieder zurueckschreiben
 
1111   # Hinweis: delete gibt den wert zurueck und loescht danach das element (nett und einfach)
 
1112   # $shell: perldoc perlunc; /delete EXPR
 
1113   $form->{donumber}            = delete $form->{donumber_array};
 
1114   $form->{ordnumber}           = delete $form->{ordnumber_array};
 
1115   $form->{cusordnumber}        = delete $form->{cusordnumber_array};
 
1116   $form->{deliverydate}        = $form->{transdate};
 
1117   $form->{transdate}           = $form->current_date(\%myconfig);
 
1118   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
 
1119   $form->{type}                = "invoice";
 
1120   $form->{closed}              = 0;
 
1121   $form->{defaultcurrency}     = $form->get_default_currency(\%myconfig);
 
1123   my ($script, $buysell);
 
1124   if ($source_type eq 'purchase_delivery_order') {
 
1125     $form->{title}  = $locale->text('Add Vendor Invoice');
 
1126     $form->{script} = 'ir.pl';
 
1131     $form->{title}  = $locale->text('Add Sales Invoice');
 
1132     $form->{script} = 'is.pl';
 
1137   map { delete $form->{$_} } qw(id subject message cc bcc printed emailed queued);
 
1139   # get vendor or customer discount
 
1141   my $saved_form = save_form();
 
1142   if ($form->{vc} eq 'vendor') {
 
1143     IR->get_vendor(\%myconfig, \%$form);
 
1144     $vc_discount = $form->{vendor_discount};
 
1146     IS->get_customer(\%myconfig, \%$form);
 
1147     $vc_discount = $form->{customer_discount};
 
1149   # use payment terms from customer or vendor
 
1150   restore_form($saved_form,0,qw(payment_id));
 
1152   $form->{rowcount} = 0;
 
1153   foreach my $ref (@{ $form->{form_details} }) {
 
1154     $form->{rowcount}++;
 
1155     $ref->{reqdate} ||= $ref->{dord_transdate}; # copy transdates into each invoice row
 
1156     map { $form->{"${_}_$form->{rowcount}"} = $ref->{$_} } keys %{ $ref };
 
1157     map { $form->{"${_}_$form->{rowcount}"} = $form->format_amount(\%myconfig, $ref->{$_}) } qw(qty sellprice lastcost);
 
1158     $form->{"converted_from_delivery_order_items_id_$form->{rowcount}"} = delete $form->{"delivery_order_items_id_$form->{rowcount}"};
 
1160     if ($vc_discount){ # falls wir einen Lieferanten/Kundenrabatt haben
 
1161       # und keinen anderen discount wert an $i ...
 
1162       $form->{"discount_$form->{rowcount}"} ||= $vc_discount; # ... nehmen wir diesen Rabatt
 
1165     $form->{"discount_$form->{rowcount}"}   = $form->{"discount_$form->{rowcount}"}  * 100; #s.a. Bug 1151
 
1166     # Anm.: Eine Änderung des discounts in der SL/DO.pm->retrieve (select (doi.discount * 100) as discount) ergibt in psql einen
 
1167     # Wert von 10.0000001490116. Ferner ist der Rabatt in der Rechnung dann bei 1.0 (?). Deswegen lasse ich das hier. jb 10.10.09
 
1169     $form->{"discount_$form->{rowcount}"} = $form->format_amount(\%myconfig, $form->{"discount_$form->{rowcount}"});
 
1171   delete $form->{form_details};
 
1173   $locale = Locale->new("$myconfig{countrycode}", "$script");
 
1175   require "bin/mozilla/$form->{script}";
 
1182   $main::lxdebug->leave_sub();
 
1186   $main::lxdebug->enter_sub();
 
1190   my $form     = $main::form;
 
1192   $form->{saveasnew} = 1;
 
1193   $form->{closed}    = 0;
 
1194   $form->{delivered} = 0;
 
1195   map { delete $form->{$_} } qw(printed emailed queued);
 
1196   delete @{ $form }{ grep { m/^stock_(?:in|out)_\d+/ } keys %{ $form } };
 
1197   $form->{"converted_from_delivery_order_items_id_$_"} = delete $form->{"delivery_order_items_id_$_"} for 1 .. $form->{"rowcount"};
 
1198   # Let kivitendo assign a new order number if the user hasn't changed the
 
1199   # previous one. If it has been changed manually then use it as-is.
 
1200   $form->{donumber} =~ s/^\s*//g;
 
1201   $form->{donumber} =~ s/\s*$//g;
 
1202   if ($form->{saved_donumber} && ($form->{saved_donumber} eq $form->{donumber})) {
 
1203     delete($form->{donumber});
 
1208   $main::lxdebug->leave_sub();
 
1211 sub calculate_stock_in_out {
 
1212   $main::lxdebug->enter_sub();
 
1214   my $form     = $main::form;
 
1218   if (!$form->{"id_${i}"}) {
 
1219     $main::lxdebug->leave_sub();
 
1223   my $all_units = AM->retrieve_all_units();
 
1225   my $in_out   = $form->{type} =~ /^sales/ ? 'out' : 'in';
 
1226   my $sinfo    = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_${i}"});
 
1228   my $do_qty   = AM->sum_with_unit($::form->{"qty_$i"}, $::form->{"unit_$i"});
 
1229   my $sum      = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $sinfo });
 
1230   my $matches  = $do_qty == $sum;
 
1232   my $content  = $form->format_amount_units('amount'      => $sum * 1,
 
1233                                             'part_unit'   => $form->{"partunit_$i"},
 
1234                                             'amount_unit' => $all_units->{$form->{"partunit_$i"}}->{base_unit},
 
1235                                             'conv_units'  => 'convertible_not_smaller',
 
1237   $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="?">|;
 
1239   $main::lxdebug->leave_sub();
 
1244 sub get_basic_bin_wh_info {
 
1245   $main::lxdebug->enter_sub();
 
1247   my $stock_info = shift;
 
1249   my $form     = $main::form;
 
1251   foreach my $sinfo (@{ $stock_info }) {
 
1252     next unless ($sinfo->{bin_id});
 
1254     my $bin_info = WH->get_basic_bin_info('id' => $sinfo->{bin_id});
 
1255     map { $sinfo->{"${_}_description"} = $sinfo->{"${_}description"} = $bin_info->{"${_}_description"} } qw(bin warehouse);
 
1258   $main::lxdebug->leave_sub();
 
1261 sub stock_in_out_form {
 
1262   $main::lxdebug->enter_sub();
 
1264   my $form     = $main::form;
 
1266   if ($form->{in_out} eq 'out') {
 
1272   $main::lxdebug->leave_sub();
 
1275 sub redo_stock_info {
 
1276   $main::lxdebug->enter_sub();
 
1280   my $form     = $main::form;
 
1282   my @non_empty = grep { $_->{qty} } @{ $params{stock_info} };
 
1284   if ($params{add_empty_row}) {
 
1286       'warehouse_id' => scalar(@non_empty) ? $non_empty[-1]->{warehouse_id} : undef,
 
1287       'bin_id'       => scalar(@non_empty) ? $non_empty[-1]->{bin_id}       : undef,
 
1291   @{ $params{stock_info} } = @non_empty;
 
1293   $main::lxdebug->leave_sub();
 
1296 sub update_stock_in {
 
1297   $main::lxdebug->enter_sub();
 
1299   my $form     = $main::form;
 
1300   my %myconfig = %main::myconfig;
 
1302   my $stock_info = [];
 
1304   foreach my $i (1..$form->{rowcount}) {
 
1305     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1306     push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(warehouse_id bin_id chargenumber
 
1307                                                                    bestbefore qty unit delivery_order_items_stock_id) };
 
1310   display_stock_in_form($stock_info);
 
1312   $main::lxdebug->leave_sub();
 
1316   $main::lxdebug->enter_sub();
 
1318   my $form     = $main::form;
 
1320   my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
 
1322   display_stock_in_form($stock_info);
 
1324   $main::lxdebug->leave_sub();
 
1327 sub display_stock_in_form {
 
1328   $main::lxdebug->enter_sub();
 
1330   my $stock_info = shift;
 
1332   my $form     = $main::form;
 
1333   my %myconfig = %main::myconfig;
 
1334   my $locale   = $main::locale;
 
1336   $form->{title} = $locale->text('Stock');
 
1338   my $part_info  = IC->get_basic_part_info('id' => $form->{parts_id});
 
1340   # Standardlagerplatz für Standard-Auslagern verwenden, falls keiner für die Ware explizit definiert wurde
 
1341   if ($::instance_conf->get_transfer_default_use_master_default_bin) {
 
1342     $part_info->{warehouse_id} ||= $::instance_conf->get_warehouse_id;
 
1343     $part_info->{bin_id}       ||= $::instance_conf->get_bin_id;
 
1346   my $units      = AM->retrieve_units(\%myconfig, $form);
 
1347   # der zweite Parameter von unit_select_data gibt den default-Namen (selected) vor
 
1348   my $units_data = AM->unit_select_data($units, $form->{do_unit}, undef, $part_info->{unit});
 
1350   $form->get_lists('warehouses' => { 'key'    => 'WAREHOUSES',
 
1351                                      'bins'   => 'BINS' });
 
1353   redo_stock_info('stock_info' => $stock_info, 'add_empty_row' => !$form->{delivered});
 
1355   get_basic_bin_wh_info($stock_info);
 
1357   $form->header(no_layout => 1);
 
1358   print $form->parse_html_template('do/stock_in_form', { 'UNITS'      => $units_data,
 
1359                                                          'STOCK_INFO' => $stock_info,
 
1360                                                          'PART_INFO'  => $part_info, });
 
1362   $main::lxdebug->leave_sub();
 
1365 sub _stock_in_out_set_qty_display {
 
1366   my $stock_info       = shift;
 
1368   my $all_units        = AM->retrieve_all_units();
 
1369   my $sum              = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1370   $form->{qty_display} = $form->format_amount_units(amount      => $sum * 1,
 
1371                                                     part_unit   => $form->{partunit},
 
1372                                                     amount_unit => $all_units->{ $form->{partunit} }->{base_unit},
 
1373                                                     conv_units  => 'convertible_not_smaller',
 
1378   $main::lxdebug->enter_sub();
 
1380   my $form     = $main::form;
 
1381   my %myconfig = %main::myconfig;
 
1383   my $stock_info = [];
 
1385   foreach my $i (1..$form->{rowcount}) {
 
1386     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1388     next if ($form->{"qty_$i"} <= 0);
 
1390     push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(delivery_order_items_stock_id warehouse_id bin_id chargenumber bestbefore qty unit) };
 
1393   $form->{stock} = SL::YAML::Dump($stock_info);
 
1395   _stock_in_out_set_qty_display($stock_info);
 
1397   my $do_qty       = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
 
1398   my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1401   print $form->parse_html_template('do/set_stock_in_out', {
 
1402     qty_matches => $do_qty == $transfer_qty,
 
1405   $main::lxdebug->leave_sub();
 
1408 sub stock_out_form {
 
1409   $main::lxdebug->enter_sub();
 
1411   my $form     = $main::form;
 
1412   my %myconfig = %main::myconfig;
 
1413   my $locale   = $main::locale;
 
1415   $form->{title} = $locale->text('Release From Stock');
 
1417   my $part_info  = IC->get_basic_part_info('id' => $form->{parts_id});
 
1419   my $units      = AM->retrieve_units(\%myconfig, $form);
 
1420   my $units_data = AM->unit_select_data($units, undef, undef, $part_info->{unit});
 
1422   my @contents   = DO->get_item_availability('parts_id' => $form->{parts_id});
 
1424   my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
 
1426   if (!$form->{delivered}) {
 
1427     foreach my $row (@contents) {
 
1428       $row->{available_qty} = $form->format_amount_units('amount'      => $row->{qty} * 1,
 
1429                                                          'part_unit'   => $part_info->{unit},
 
1430                                                          'conv_units'  => 'convertible_not_smaller',
 
1433       foreach my $sinfo (@{ $stock_info }) {
 
1434         next if (($row->{bin_id}       != $sinfo->{bin_id}) ||
 
1435                  ($row->{warehouse_id} != $sinfo->{warehouse_id}) ||
 
1436                  ($row->{chargenumber} ne $sinfo->{chargenumber}) ||
 
1437                  ($row->{bestbefore}   ne $sinfo->{bestbefore}));
 
1439         map { $row->{"stock_$_"} = $sinfo->{$_} } qw(qty unit error delivery_order_items_stock_id);
 
1444     get_basic_bin_wh_info($stock_info);
 
1446     foreach my $sinfo (@{ $stock_info }) {
 
1447       map { $sinfo->{"stock_$_"} = $sinfo->{$_} } qw(qty unit);
 
1451   $form->header(no_layout => 1);
 
1452   print $form->parse_html_template('do/stock_out_form', { 'UNITS'      => $units_data,
 
1453                                                           'WHCONTENTS' => $form->{delivered} ? $stock_info : \@contents,
 
1454                                                           'PART_INFO'  => $part_info, });
 
1456   $main::lxdebug->leave_sub();
 
1460   $main::lxdebug->enter_sub();
 
1462   my $form     = $main::form;
 
1463   my %myconfig = %main::myconfig;
 
1464   my $locale   = $main::locale;
 
1466   my $stock_info = [];
 
1468   foreach my $i (1 .. $form->{rowcount}) {
 
1469     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1471     next if ($form->{"qty_$i"} <= 0);
 
1473     push @{ $stock_info }, {
 
1474       'warehouse_id' => $form->{"warehouse_id_$i"},
 
1475       'bin_id'       => $form->{"bin_id_$i"},
 
1476       'chargenumber' => $form->{"chargenumber_$i"},
 
1477       'bestbefore'   => $form->{"bestbefore_$i"},
 
1478       'qty'          => $form->{"qty_$i"},
 
1479       'unit'         => $form->{"unit_$i"},
 
1481       'delivery_order_items_stock_id'  => $form->{"delivery_order_items_stock_id_$i"},
 
1485   my @errors     = DO->check_stock_availability('requests' => $stock_info,
 
1486                                                 'parts_id' => $form->{parts_id});
 
1488   $form->{stock} = SL::YAML::Dump($stock_info);
 
1491     $form->{ERRORS} = [];
 
1492     map { push @{ $form->{ERRORS} }, $locale->text('Error in row #1: The quantity you entered is bigger than the stocked quantity.', $_->{row}); } @errors;
 
1493     stock_in_out_form();
 
1496     _stock_in_out_set_qty_display($stock_info);
 
1498     my $do_qty       = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
 
1499     my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1502     print $form->parse_html_template('do/set_stock_in_out', {
 
1503       qty_matches => $do_qty == $transfer_qty,
 
1507   $main::lxdebug->leave_sub();
 
1511   $main::lxdebug->enter_sub();
 
1513   my $form     = $main::form;
 
1514   my %myconfig = %main::myconfig;
 
1515   my $locale   = $main::locale;
 
1517   if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
 
1518     $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred in.'));
 
1521   save(no_redirect => 1);
 
1523   my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_in_${_}"} } (1 .. $form->{rowcount});
 
1527     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1528     my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1531     $form->{ERRORS}   = [];
 
1533     foreach my $i (1 .. $form->{rowcount}) {
 
1534       next unless ($form->{"id_$i"} && $form->{"stock_in_$i"});
 
1536       my $row_sum_base_qty = 0;
 
1537       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1539       foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_in_$i"}) }) {
 
1540         $request->{parts_id}  = $form->{"id_$i"};
 
1541         $row_sum_base_qty    += $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
 
1543         $request->{project_id} = $form->{"project_id_$i"} || $form->{globalproject_id};
 
1545         push @all_requests, $request;
 
1548       next if (0 == $row_sum_base_qty);
 
1550       my $do_base_qty = $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1552 #      if ($do_base_qty != $row_sum_base_qty) {
 
1553 #        push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no stock at all or the full quantity of #2 #3.',
 
1554 #                                                 $i, $form->{"qty_$i"}, $form->{"unit_$i"});
 
1558     if (@{ $form->{ERRORS} }) {
 
1559       push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
 
1561       set_headings('edit');
 
1563       $main::lxdebug->leave_sub();
 
1565       $::dispatcher->end_request;
 
1569   DO->transfer_in_out('direction' => 'in',
 
1570                       'requests'  => \@all_requests);
 
1572   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1574   $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id});
 
1577   $main::lxdebug->leave_sub();
 
1581   $main::lxdebug->enter_sub();
 
1583   my $form     = $main::form;
 
1584   my %myconfig = %main::myconfig;
 
1585   my $locale   = $main::locale;
 
1587   if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
 
1588     $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred out.'));
 
1591   save(no_redirect => 1);
 
1593   my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_out_${_}"} } (1 .. $form->{rowcount});
 
1597     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1598     my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1601     $form->{ERRORS}   = [];
 
1603     foreach my $i (1 .. $form->{rowcount}) {
 
1604       next unless ($form->{"id_$i"} && $form->{"stock_out_$i"});
 
1606       my $row_sum_base_qty = 0;
 
1607       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1609       foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_out_$i"}) }) {
 
1610         $request->{parts_id} = $form->{"id_$i"};
 
1611         $request->{base_qty} = $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
 
1612         $request->{project_id} = $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id};
 
1614         my $map_key          = join '--', ($form->{"id_$i"}, @{$request}{qw(warehouse_id bin_id chargenumber bestbefore)});
 
1616         $request_map{$map_key}                 ||= $request;
 
1617         $request_map{$map_key}->{sum_base_qty} ||= 0;
 
1618         $request_map{$map_key}->{sum_base_qty}  += $request->{base_qty};
 
1619         $row_sum_base_qty                       += $request->{base_qty};
 
1621         push @all_requests, $request;
 
1624       next if (0 == $row_sum_base_qty);
 
1626       my $do_base_qty = $form->{"qty_$i"} * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1628 #      if ($do_base_qty != $row_sum_base_qty) {
 
1629 #        push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no transfer at all or the full quantity of #2 #3.',
 
1630 #                                                 $i, $form->{"qty_$i"}, $form->{"unit_$i"});
 
1635       my @bin_ids      = map { $_->{bin_id} } values %request_map;
 
1636       my %bin_info_map = WH->get_basic_bin_info('id' => \@bin_ids);
 
1637       my @contents     = DO->get_item_availability('parts_id' => \@part_ids);
 
1639       foreach my $inv (@contents) {
 
1640         my $map_key = join '--', @{$inv}{qw(parts_id warehouse_id bin_id chargenumber bestbefore)};
 
1642         next unless ($request_map{$map_key});
 
1644         my $request    = $request_map{$map_key};
 
1645         $request->{ok} = $request->{sum_base_qty} <= $inv->{qty};
 
1648       foreach my $request (values %request_map) {
 
1649         next if ($request->{ok});
 
1651         my $pinfo = $part_info_map{$request->{parts_id}};
 
1652         my $binfo = $bin_info_map{$request->{bin_id}};
 
1654         if ($::instance_conf->get_show_bestbefore) {
 
1655             push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, #5, for the transfer of #6.",
 
1656                                                      $pinfo->{description},
 
1657                                                      $binfo->{warehouse_description},
 
1658                                                      $binfo->{bin_description},
 
1659                                                      $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
 
1660                                                      $request->{bestbefore} ? $locale->text('bestbefore #1', $request->{bestbefore}) : $locale->text('no bestbefore'),
 
1661                                                      $form->format_amount_units('amount'      => $request->{sum_base_qty},
 
1662                                                                                 'part_unit'   => $pinfo->{unit},
 
1663                                                                                 'conv_units'  => 'convertible_not_smaller'));
 
1665             push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, for the transfer of #5.",
 
1666                                                      $pinfo->{description},
 
1667                                                      $binfo->{warehouse_description},
 
1668                                                      $binfo->{bin_description},
 
1669                                                      $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
 
1670                                                      $form->format_amount_units('amount'      => $request->{sum_base_qty},
 
1671                                                                                 'part_unit'   => $pinfo->{unit},
 
1672                                                                                 'conv_units'  => 'convertible_not_smaller'));
 
1677     if (@{ $form->{ERRORS} }) {
 
1678       push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
 
1680       set_headings('edit');
 
1682       $main::lxdebug->leave_sub();
 
1684       $::dispatcher->end_request;
 
1687   DO->transfer_in_out('direction' => 'out',
 
1688                       'requests'  => \@all_requests);
 
1690   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1692   $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id});
 
1695   $main::lxdebug->leave_sub();
 
1699   $main::lxdebug->enter_sub();
 
1701   my $form     = $main::form;
 
1703   DO->close_orders('ids' => [ $form->{id} ]);
 
1705   $form->{closed} = 1;
 
1709   $main::lxdebug->leave_sub();
 
1713   $::lxdebug->enter_sub;
 
1715   $::auth->assert('purchase_delivery_order_edit | sales_delivery_order_edit');
 
1718   retrieve_partunits();
 
1720   my $new_rowcount = $::form->{"rowcount"} * 1 + 1;
 
1721   $::form->{"project_id_${new_rowcount}"} = $::form->{"globalproject_id"};
 
1723   $::form->language_payment(\%::myconfig);
 
1725   Common::webdav_folder($::form);
 
1728   display_row(++$::form->{rowcount});
 
1731   $::lxdebug->leave_sub;
 
1735   call_sub($main::form->{yes_nextsub});
 
1739   call_sub($main::form->{no_nextsub});
 
1743   call_sub($main::form->{update_nextsub} || $main::form->{nextsub} || 'update_delivery_order');
 
1747   my $form     = $main::form;
 
1748   my $locale   = $main::locale;
 
1750   foreach my $action (qw(update print save transfer_out transfer_out_default sort
 
1751                          transfer_in transfer_in_default mark_closed save_as_new invoice delete)) {
 
1752     if ($form->{"action_${action}"}) {
 
1758   $form->error($locale->text('No action defined.'));
 
1761 sub transfer_out_default {
 
1762   $main::lxdebug->enter_sub();
 
1764   my $form     = $main::form;
 
1766   transfer_in_out_default('direction' => 'out');
 
1768   $main::lxdebug->leave_sub();
 
1771 sub transfer_in_default {
 
1772   $main::lxdebug->enter_sub();
 
1774   my $form     = $main::form;
 
1776   transfer_in_out_default('direction' => 'in');
 
1778   $main::lxdebug->leave_sub();
 
1781 # Falls das Standardlagerverfahren aktiv ist, wird
 
1782 # geprüft, ob alle Standardlagerplätze für die Auslager-
 
1783 # artikel vorhanden sind UND ob die Warenmenge ausreicht zum
 
1784 # Auslagern. Falls nicht wird entsprechend eine Fehlermeldung
 
1785 # generiert. Offen Chargennummer / bestbefore wird nicht berücksichtigt
 
1786 sub transfer_in_out_default {
 
1787   $main::lxdebug->enter_sub();
 
1789   my $form     = $main::form;
 
1790   my %myconfig = %main::myconfig;
 
1791   my $locale   = $main::locale;
 
1794   my (%missing_default_bins, %qty_parts, @all_requests, %part_info_map, $default_warehouse_id, $default_bin_id);
 
1796   Common::check_params(\%params, qw(direction));
 
1798   # entsprechende defaults holen, falls standardlagerplatz verwendet werden soll
 
1799   if ($::instance_conf->get_transfer_default_use_master_default_bin) {
 
1800     $default_warehouse_id = $::instance_conf->get_warehouse_id;
 
1801     $default_bin_id       = $::instance_conf->get_bin_id;
 
1805   my @part_ids = map { $form->{"id_${_}"} } (1 .. $form->{rowcount});
 
1807     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1808     %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1809     foreach my $i (1 .. $form->{rowcount}) {
 
1810       next unless ($form->{"id_$i"});
 
1811       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1812       my $qty =   $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1814       $form->show_generic_error($locale->text("Cannot transfer negative entries." )) if ($qty < 0);
 
1815       # if we do not want to transfer services and this part is a service, set qty to zero
 
1816       # ... and do not create a hash entry in %qty_parts below (will skip check for bins for the transfer == out case)
 
1817       # ... 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)
 
1819       $qty = 0 if (!$::instance_conf->get_transfer_default_services && $part_info_map{$form->{"id_$i"}}->{part_type} eq 'service');
 
1820       $qty_parts{$form->{"id_$i"}} += $qty;
 
1822         delete $qty_parts{$form->{"id_$i"}} unless $qty_parts{$form->{"id_$i"}};
 
1823         undef $form->{"stock_in_$i"};
 
1826       $part_info_map{$form->{"id_$i"}}{bin_id}       ||= $default_bin_id;
 
1827       $part_info_map{$form->{"id_$i"}}{warehouse_id} ||= $default_warehouse_id;
 
1829       push @all_requests, ($qty == 0) ? { } : {
 
1830                         'chargenumber' => '',  #?? die müsste entsprechend geholt werden
 
1831                         #'bestbefore' => undef, # TODO wird nicht berücksichtigt
 
1832                         'bin_id' => $part_info_map{$form->{"id_$i"}}{bin_id},
 
1834                         'parts_id' => $form->{"id_$i"},
 
1835                         'comment' => $locale->text("Default transfer delivery order"),
 
1836                         'unit' => $part_info_map{$form->{"id_$i"}}{unit},
 
1837                         'warehouse_id' => $part_info_map{$form->{"id_$i"}}{warehouse_id},
 
1838                         'oe_id' => $form->{id},
 
1839                         'project_id' => $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id}
 
1843     # jetzt wird erst überprüft, ob die Stückzahl entsprechend stimmt.
 
1844     # check if bin (transfer in and transfer out and qty (transfer out) is correct
 
1845     foreach my $key (keys %qty_parts) {
 
1847       $missing_default_bins{$key}{missing_bin} = 1 unless ($part_info_map{$key}{bin_id});
 
1848       next unless ($part_info_map{$key}{bin_id}); # abbruch
 
1850       if ($params{direction} eq 'out') {  # wird nur für ausgehende Mengen benötigt
 
1851         my ($max_qty, $error) = WH->get_max_qty_parts_bin(parts_id => $key, bin_id => $part_info_map{$key}{bin_id});
 
1853           # wir können nicht entscheiden, welche charge oder mhd (bestbefore) ausgewählt sein soll
 
1854           # deshalb rückmeldung nach oben geben, manuell auszulagern
 
1855           # TODO Bei nur einem Treffer mit Charge oder bestbefore wäre das noch möglich
 
1856           $missing_default_bins{$key}{chargenumber} = 1;
 
1858         if ($max_qty < $qty_parts{$key}){
 
1859           $missing_default_bins{$key}{missing_qty} = $max_qty - $qty_parts{$key};
 
1865   # Abfrage für Fehlerbehandlung (nur bei direction == out)
 
1866   if (scalar (keys %missing_default_bins)) {
 
1868     foreach my $fehler (keys %missing_default_bins) {
 
1870       my $ware = WH->get_part_description(parts_id => $fehler);
 
1871       if ($missing_default_bins{$fehler}{missing_bin}){
 
1872         $fehlertext .= "Kein Standardlagerplatz definiert bei $ware <br>";
 
1874       if ($missing_default_bins{$fehler}{missing_qty}) {  # missing_qty
 
1875         $fehlertext .= "Es fehlen " . $missing_default_bins{$fehler}{missing_qty}*-1 .
 
1876                        " von $ware auf dem Standard-Lagerplatz " . $part_info_map{$fehler}{bin} .   " zum Auslagern<br>";
 
1878       if ($missing_default_bins{$fehler}{chargenumber}){
 
1879         $fehlertext .= "Die Ware hat eine Chargennummer oder eine Mindesthaltbarkeit definiert.
 
1880                         Hier kann man nicht automatisch entscheiden.
 
1881                         Bitte diesen Lieferschein manuell auslagern.
 
1884       # auslagern soll immer gehen, auch wenn nicht genügend auf lager ist.
 
1885       # der lagerplatz ist hier extra konfigurierbar, bspw. Lager-Korrektur mit
 
1886       # Lagerplatz Lagerplatz-Korrektur
 
1887       my $default_warehouse_id_ignore_onhand = $::instance_conf->get_warehouse_id_ignore_onhand;
 
1888       my $default_bin_id_ignore_onhand       = $::instance_conf->get_bin_id_ignore_onhand;
 
1889       if ($::instance_conf->get_transfer_default_ignore_onhand && $default_bin_id_ignore_onhand) {
 
1890         # entsprechende defaults holen
 
1891         # falls chargenumber, bestbefore oder anzahl nicht stimmt, auf automatischen
 
1892         # lagerplatz wegbuchen!
 
1893         foreach (@all_requests) {
 
1894           if ($_->{parts_id} eq $fehler){
 
1895           $_->{bin_id}        = $default_bin_id_ignore_onhand;
 
1896           $_->{warehouse_id}  = $default_warehouse_id_ignore_onhand;
 
1900         #$main::lxdebug->message(0, 'Fehlertext: ' . $fehlertext);
 
1901         $form->show_generic_error($locale->text("Cannot transfer. <br> Reason:<br>#1", $fehlertext ));
 
1907   # hier der eigentliche fallunterschied für in oder out
 
1908   my $prefix   = $params{direction} eq 'in' ? 'in' : 'out';
 
1910   # dieser array_ref ist für DO->save da:
 
1911   # einmal die all_requests in YAML verwandeln, damit delivery_order_items_stock
 
1912   # gefüllt werden kann.
 
1913   # could be dumped to the form in the first loop,
 
1914   # but maybe bin_id and warehouse_id has changed to the "korrekturlager" with
 
1915   # allowed negative qty ($::instance_conf->get_warehouse_id_ignore_onhand) ...
 
1917   foreach (@all_requests){
 
1919     next unless scalar(%{ $_ });
 
1920     $form->{"stock_${prefix}_$i"} = SL::YAML::Dump([$_]);
 
1923   save(no_redirect => 1); # Wir können auslagern, deshalb beleg speichern
 
1924                           # und in delivery_order_items_stock speichern
 
1926   # ... and fill back the persistent dois_id for inventory fk
 
1927   undef (@all_requests);
 
1928   foreach my $i (1 .. $form->{rowcount}) {
 
1929     next unless ($form->{"id_$i"} && $form->{"stock_${prefix}_$i"});
 
1930     push @all_requests, @{ DO->unpack_stock_information('packed' => $form->{"stock_${prefix}_$i"}) };
 
1932   DO->transfer_in_out('direction' => $prefix,
 
1933                       'requests'  => \@all_requests);
 
1935   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1937   $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'out';
 
1938   $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'in';
 
1944   $main::lxdebug->enter_sub();
 
1948   my $form     = $main::form;
 
1951   save(no_redirect => 1); # has to be done, at least for newly added positions
 
1953   # hashify partnumbers, positions. key is delivery_order_items_id
 
1954   for my $i (1 .. ($form->{rowcount}) ) {
 
1955     $temp_hash{$form->{"delivery_order_items_id_$i"}} = { runningnumber => $form->{"runningnumber_$i"}, partnumber => $form->{"partnumber_$i"} };
 
1956     if ($form->{id} && $form->{"discount_$i"}) {
 
1957       # prepare_order assumes a db value if there is a form->id and multiplies *100
 
1958       # We hope for new controller code (no more format_amount/parse_amount distinction)
 
1959       $form->{"discount_$i"} /=100;
 
1962   # naturally sort partnumbers and get a sorted array of doi_ids
 
1963   my @sorted_doi_ids =  sort { Sort::Naturally::ncmp($temp_hash{$a}->{"partnumber"}, $temp_hash{$b}->{"partnumber"}) }  keys %temp_hash;
 
1968   for (@sorted_doi_ids) {
 
1969     $form->{"runningnumber_$temp_hash{$_}->{runningnumber}"} = $new_number;
 
1972   # all parse_amounts changes are in form (i.e. , to .) therefore we need
 
1973   # another format_amount to change it back, for the next save ;-(
 
1974   # works great except for row discounts (see above comment)
 
1978     $main::lxdebug->leave_sub();
 
1990 do.pl - Script for all calls to delivery order
 
1998 Sorts all position with Natural Sort. Can be activated in form_footer.html like this
 
1999 C<E<lt>input class="submit" type="submit" name="action_sort" id="sort_button" value="[% 'Sort and Save' | $T8 %]"E<gt>>
 
2005 Sort and Save can be implemented as an optional button if configuration ca be set by client config.
 
2006 Example coding for database scripts and templates in (git show af2f24b8), check also
 
2007 autogeneration for rose (scripts/rose_auto_create_model.pl --h)