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);
 
  39 use SL::Controller::DeliveryOrder;
 
  40 use SL::DB::DeliveryOrder;
 
  41 use SL::DB::DeliveryOrder::TypeData qw(:types validate_type);
 
  45 use SL::MoreCommon qw(ary_diff restore_form save_form);
 
  46 use SL::ReportGenerator;
 
  49 use Sort::Naturally ();
 
  50 require "bin/mozilla/common.pl";
 
  51 require "bin/mozilla/io.pl";
 
  52 require "bin/mozilla/reportgenerator.pl";
 
  61   validate_type($::form->{type});
 
  63   my $right = SL::DB::DeliveryOrder::TypeData::get($::form->{type}, "right");
 
  64   $main::auth->assert($right);
 
  68   $main::lxdebug->enter_sub();
 
  74   my $form     = $main::form;
 
  75   my $locale   = $main::locale;
 
  77   if ($form->{type} eq 'purchase_delivery_order') {
 
  78     $form->{vc}    = 'vendor';
 
  79     $form->{title} = $action eq "edit" ? $locale->text('Edit Purchase Delivery Order') : $locale->text('Add Purchase Delivery Order');
 
  81     $form->{vc}    = 'customer';
 
  82     $form->{title} = $action eq "edit" ? $locale->text('Edit Sales Delivery Order') : $locale->text('Add Sales Delivery Order');
 
  85   $form->{heading} = $locale->text('Delivery Order');
 
  87   $main::lxdebug->leave_sub();
 
  91   $main::lxdebug->enter_sub();
 
  95   if (($::form->{type} =~ /purchase/) && !$::instance_conf->get_allow_new_purchase_invoice) {
 
  96     $::form->show_generic_error($::locale->text("You do not have the permissions to access this function."));
 
  99   my $form     = $main::form;
 
 103   $form->{show_details} = $::myconfig{show_form_details};
 
 104   $form->{callback} = build_std_url('action=add', 'type', 'vc') unless ($form->{callback});
 
 106   order_links(is_new => 1);
 
 110   $main::lxdebug->leave_sub();
 
 114   $main::lxdebug->enter_sub();
 
 118   my $form     = $main::form;
 
 120   $form->{show_details} = $::myconfig{show_form_details};
 
 122   # show history button
 
 123   $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
 
 124   #/show hhistory button
 
 126   $form->{simple_save} = 0;
 
 128   set_headings("edit");
 
 130   # editing without stuff to edit? try adding it first
 
 131   if ($form->{rowcount} && !$form->{print_and_save}) {
 
 132 #     map { $id++ if $form->{"multi_id_$_"} } (1 .. $form->{rowcount});
 
 136       undef $form->{rowcount};
 
 138       $main::lxdebug->leave_sub();
 
 141   } elsif (!$form->{id}) {
 
 143     $main::lxdebug->leave_sub();
 
 147   my ($language_id, $printer_id);
 
 148   if ($form->{print_and_save}) {
 
 149     $form->{action}   = "dispatcher";
 
 150     $form->{action_print} = "1";
 
 151     $form->{resubmit} = 1;
 
 152     $language_id      = $form->{language_id};
 
 153     $printer_id       = $form->{printer_id};
 
 156   set_headings("edit");
 
 161   if ($form->{print_and_save}) {
 
 162     $form->{language_id} = $language_id;
 
 163     $form->{printer_id}  = $printer_id;
 
 168   $main::lxdebug->leave_sub();
 
 172   $main::lxdebug->enter_sub();
 
 177   my $form     = $main::form;
 
 178   my %myconfig = %main::myconfig;
 
 180   # retrieve order/quotation
 
 181   my $editing = $form->{id};
 
 183   DO->retrieve('vc'  => $form->{vc},
 
 184                'ids' => $form->{id});
 
 186   $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes delivery_term_id currency));
 
 188   # get customer / vendor
 
 189   if ($form->{vc} eq 'vendor') {
 
 190     IR->get_vendor(\%myconfig, \%$form);
 
 191     $form->{discount} = $form->{vendor_discount};
 
 193     IS->get_customer(\%myconfig, \%$form);
 
 194     $form->{discount} = $form->{customer_discount};
 
 195     $form->{billing_address_id} = $form->{default_billing_address_id} if $params{is_new};
 
 198   $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id delivery_term_id));
 
 199   $form->restore_vars(qw(currency)) if ($form->{id} || $form->{convert_from_oe_ids});
 
 200   $form->restore_vars(qw(taxincluded)) if $form->{id};
 
 201   $form->restore_vars(qw(salesman_id)) if $editing;
 
 203   $main::lxdebug->leave_sub();
 
 207   $main::lxdebug->enter_sub();
 
 211   my $form     = $main::form;
 
 212   my %myconfig = %main::myconfig;
 
 214   $form->{formname} = $form->{type} unless $form->{formname};
 
 217   foreach my $ref (@{ $form->{form_details} }) {
 
 218     $form->{rowcount} = ++$i;
 
 220     map { $form->{"${_}_$i"} = $ref->{$_} } keys %{$ref};
 
 222   for my $i (1 .. $form->{rowcount}) {
 
 224       $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100);
 
 226       $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
 
 228     my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
 
 230     my $decimalplaces = ($dec > 2) ? $dec : 2;
 
 232     # copy reqdate from deliverydate for invoice -> order conversion
 
 233     $form->{"reqdate_$i"} = $form->{"deliverydate_$i"} unless $form->{"reqdate_$i"};
 
 235     $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
 
 236     $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}, $decimalplaces);
 
 238     (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
 
 239     $dec_qty = length $dec_qty;
 
 240     $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
 
 243   $main::lxdebug->leave_sub();
 
 246 sub setup_do_action_bar {
 
 247   my @transfer_qty   = qw(kivi.SalesPurchase.delivery_order_check_transfer_qty);
 
 248   my @req_trans_desc = qw(kivi.SalesPurchase.check_transaction_description) x!!$::instance_conf->get_require_transaction_description_ps;
 
 249   my $is_customer    = $::form->{vc} eq 'customer';
 
 251   my $undo_date  = DateTime->today->subtract(days => $::instance_conf->get_undo_transfer_interval);
 
 252   my $insertdate = DateTime->from_kivitendo($::form->{insertdate});
 
 253   my $undo_transfer  = 0;
 
 254   if (ref $undo_date eq 'DateTime' && ref $insertdate eq 'DateTime') {
 
 255     $undo_transfer = $insertdate > $undo_date;
 
 257   for my $bar ($::request->layout->get('actionbar')) {
 
 261           submit    => [ '#form', { action => "update" } ],
 
 262           id        => 'update_button',
 
 263           accesskey => 'enter',
 
 269           submit   => [ '#form', { action => "save" } ],
 
 270           checks   => [ 'kivi.validate_form' ],
 
 271           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 275           submit   => [ '#form', { action => "save_as_new" } ],
 
 276           checks   => [ 'kivi.validate_form' ],
 
 277           disabled => !$::form->{id},
 
 280           t8('Mark as closed'),
 
 281           submit   => [ '#form', { action => "mark_closed" } ],
 
 282           checks   => [ 'kivi.validate_form' ],
 
 283           confirm  => t8('This will remove the delivery order from showing as open even if contents are not delivered. Proceed?'),
 
 284           disabled => !$::form->{id}    ? t8('This record has not been saved yet.')
 
 285                     : $::form->{closed} ? t8('This record has already been closed.')
 
 288       ], # end of combobox "Save"
 
 292         submit   => [ '#form', { action => "delete" } ],
 
 293         confirm  => t8('Do you really want to delete this object?'),
 
 294         disabled => !$::form->{id}                                                                              ? t8('This record has not been saved yet.')
 
 295                   : $::form->{delivered}                                                                        ? t8('This record has already been delivered.')
 
 296                   : ($::form->{vc} eq 'customer' && !$::instance_conf->get_sales_delivery_order_show_delete)    ? t8('Deleting this type of record has been disabled in the configuration.')
 
 297                   : ($::form->{vc} eq 'vendor'   && !$::instance_conf->get_purchase_delivery_order_show_delete) ? t8('Deleting this type of record has been disabled in the configuration.')
 
 304           submit   => [ '#form', { action => "transfer_out" } ],
 
 305           checks   => [ 'kivi.validate_form', @transfer_qty ],
 
 306           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 307           only_if  => $is_customer,
 
 310           t8('Transfer out via default'),
 
 311           submit   => [ '#form', { action => "transfer_out_default" } ],
 
 312           checks   => [ 'kivi.validate_form' ],
 
 313           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 314           only_if  => $is_customer && $::instance_conf->get_transfer_default,
 
 318           submit   => [ '#form', { action => "transfer_in" } ],
 
 319           checks   => [ 'kivi.validate_form', @transfer_qty ],
 
 320           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 321           only_if  => !$is_customer,
 
 324           t8('Transfer in via default'),
 
 325           submit   => [ '#form', { action => "transfer_in_default" } ],
 
 326           checks   => [ 'kivi.validate_form' ],
 
 327           disabled => $::form->{delivered} ? t8('This record has already been delivered.') : undef,
 
 328           only_if  => !$is_customer && $::instance_conf->get_transfer_default,
 
 332           submit   => [ '#form', { action => "delete_transfers" } ],
 
 333           checks   => [ 'kivi.validate_form' ],
 
 334           only_if  => $::form->{delivered},
 
 335           disabled => !$undo_transfer ? t8('Transfer date exceeds the maximum allowed interval.') : undef,
 
 337       ], # end of combobox "Transfer out"
 
 344         submit => [ '#form', { action => "invoice" } ],
 
 345         disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
 
 346         confirm  => $::form->{delivered}                                                                         ? undef
 
 347                   : ($::form->{vc} eq 'customer' && $::instance_conf->get_sales_delivery_order_check_stocked)    ? t8('This record has not been stocked out. Proceed?')
 
 348                   : ($::form->{vc} eq 'vendor'   && $::instance_conf->get_purchase_delivery_order_check_stocked) ? t8('This record has not been stocked in. Proceed?')
 
 353         action => [ t8('Export') ],
 
 356           call   => [ 'kivi.SalesPurchase.show_print_dialog' ],
 
 357           checks => [ 'kivi.validate_form' ],
 
 361           call   => [ 'kivi.SalesPurchase.show_email_dialog' ],
 
 362           checks => [ 'kivi.validate_form' ],
 
 363           disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
 
 365       ], # end of combobox "Export"
 
 368         action => [ t8('more') ],
 
 371           call     => [ 'set_history_window', $::form->{id} * 1, 'id' ],
 
 372           disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
 
 376           call     => [ 'follow_up_window' ],
 
 377           disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
 
 379       ], # end if combobox "more"
 
 382   $::request->layout->add_javascripts('kivi.Validator.js');
 
 385 sub setup_do_search_action_bar {
 
 388   for my $bar ($::request->layout->get('actionbar')) {
 
 392         submit    => [ '#form' ],
 
 393         accesskey => 'enter',
 
 394         checks    => [ 'kivi.validate_form' ],
 
 398   $::request->layout->add_javascripts('kivi.Validator.js');
 
 401 sub setup_do_orders_action_bar {
 
 404   for my $bar ($::request->layout->get('actionbar')) {
 
 408         submit    => [ '#form', { action => 'invoice_multi' } ],
 
 409         checks    => [ [ 'kivi.check_if_entries_selected', '#form tbody input[type=checkbox]' ] ],
 
 410         accesskey => 'enter',
 
 414         call   => [ 'kivi.SalesPurchase.show_print_dialog', 'js:kivi.MassDeliveryOrderPrint.submitMultiOrders' ],
 
 415         checks => [ [ 'kivi.check_if_entries_selected', '#form tbody input[type=checkbox]' ] ],
 
 422   $main::lxdebug->enter_sub();
 
 426   my $form     = $main::form;
 
 427   my %myconfig = %main::myconfig;
 
 429   my $class       = "SL::DB::" . ($form->{vc} eq 'customer' ? 'Customer' : 'Vendor');
 
 430   $form->{VC_OBJ} = $class->load_cached($form->{ $form->{vc} . '_id' });
 
 432   $form->{CONTACT_OBJ}   = $form->{cp_id} ? SL::DB::Contact->load_cached($form->{cp_id}) : undef;
 
 433   my $current_employee   = SL::DB::Manager::Employee->current;
 
 434   $form->{employee_id}   = $form->{old_employee_id} if $form->{old_employee_id};
 
 435   $form->{salesman_id}   = $form->{old_salesman_id} if $form->{old_salesman_id};
 
 436   $form->{employee_id} ||= $current_employee->id;
 
 437   $form->{salesman_id} ||= $current_employee->id;
 
 439   my $vc = $form->{vc} eq "customer" ? "customers" : "vendors";
 
 440   $form->get_lists("price_factors"  => "ALL_PRICE_FACTORS",
 
 441                    "business_types" => "ALL_BUSINESS_TYPES",
 
 443   $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all_sorted;
 
 444   $form->{ALL_LANGUAGES}   = SL::DB::Manager::Language->get_all_sorted;
 
 447   my @old_project_ids = uniq grep { $_ } map { $_ * 1 } ($form->{"globalproject_id"}, map { $form->{"project_id_$_"} } 1..$form->{"rowcount"});
 
 448   my @old_ids_cond    = @old_project_ids ? (id => \@old_project_ids) : ();
 
 450   if (($vc eq 'customers') && $::instance_conf->get_customer_projects_only_in_sales) {
 
 453         customer_id          => $::form->{customer_id},
 
 454         billable_customer_id => $::form->{customer_id},
 
 459       and => [ active => 1, @customer_cond ],
 
 463   $::form->{ALL_PROJECTS}          = SL::DB::Manager::Project->get_all_sorted(query => \@conditions);
 
 464   $::form->{ALL_EMPLOYEES}         = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{employee_id},  deleted => 0 ] ]);
 
 465   $::form->{ALL_SALESMEN}          = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{salesman_id},  deleted => 0 ] ]);
 
 466   $::form->{ALL_SHIPTO}            = SL::DB::Manager::Shipto->get_all_sorted(query => [
 
 467     or => [ trans_id  => $::form->{"$::form->{vc}_id"} * 1, and => [ shipto_id => $::form->{shipto_id} * 1, trans_id => undef ] ]
 
 469   $::form->{ALL_CONTACTS}          = SL::DB::Manager::Contact->get_all_sorted(query => [
 
 471       cp_cv_id => $::form->{"$::form->{vc}_id"} * 1,
 
 474         cp_id    => $::form->{cp_id} * 1
 
 479   my $dispatch_to_popup = '';
 
 480   if ($form->{resubmit} && ($form->{format} eq "html")) {
 
 481     $dispatch_to_popup  = "window.open('about:blank','Beleg'); document.do.target = 'Beleg';";
 
 482     $dispatch_to_popup .= "document.do.submit();";
 
 483   } elsif ($form->{resubmit} && $form->{action_print}) {
 
 484     # emulate click for resubmitting actions
 
 485     $dispatch_to_popup  = "kivi.SalesPurchase.show_print_dialog(); kivi.SalesPurchase.print_record();";
 
 487   $::request->{layout}->add_javascripts_inline("\$(function(){$dispatch_to_popup});");
 
 490   $form->{follow_up_trans_info} = $form->{donumber} .'('. $form->{VC_OBJ}->name .')' if $form->{VC_OBJ};
 
 492   $::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));
 
 494   setup_do_action_bar();
 
 497   # Fix für Bug 1082 Erwartet wird: 'abteilungsNAME--abteilungsID'
 
 498   # und Erweiterung für Bug 1760:
 
 499   # Das war leider nur ein Teil-Fix, da das Verhalten den 'Erneuern'-Knopf
 
 500   # nicht überlebt. Konsequent jetzt auf L umgestellt
 
 501   #   $ perldoc SL::Template::Plugin::L
 
 502   # Daher entsprechend nur die Anpassung in form_header
 
 503   # und in DO.pm gemacht. 4 Testfälle:
 
 504   # department_id speichern                 | i.O.
 
 505   # department_id lesen                     | i.O.
 
 506   # department leer überlebt erneuern       | i.O.
 
 507   # department nicht leer überlebt erneuern | i.O.
 
 508   # $main::lxdebug->message(0, 'ABTEILUNGS ID in form?' . $form->{department_id});
 
 509   print $form->parse_html_template('do/form_header');
 
 511   $main::lxdebug->leave_sub();
 
 515   $main::lxdebug->enter_sub();
 
 519   my $form     = $main::form;
 
 521   $form->{PRINT_OPTIONS}      = setup_sales_purchase_print_options();
 
 522   $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
 
 524   my $shipto_cvars       = SL::DB::Shipto->new->cvars_by_config;
 
 525   foreach my $var (@{ $shipto_cvars }) {
 
 526     my $name = "shiptocvar_" . $var->config->name;
 
 527     $var->value($form->{$name}) if exists $form->{$name};
 
 530   print $form->parse_html_template('do/form_footer',
 
 531     {transfer_default => ($::instance_conf->get_transfer_default),
 
 532      shipto_cvars     => $shipto_cvars});
 
 534   $main::lxdebug->leave_sub();
 
 537 sub update_delivery_order {
 
 538   $main::lxdebug->enter_sub();
 
 542   my $form     = $main::form;
 
 543   my %myconfig = %main::myconfig;
 
 545   set_headings($form->{"id"} ? "edit" : "add");
 
 547   $form->{insertdate} = SL::DB::DeliveryOrder->new(id => $form->{id})->load->itime_as_date if $form->{id};
 
 552   $payment_id = $form->{payment_id} if $form->{payment_id};
 
 554   my $vc = $form->{vc};
 
 555   if (($form->{"previous_${vc}_id"} || $form->{"${vc}_id"}) != $form->{"${vc}_id"}) {
 
 556     $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
 
 558     if ($vc eq 'customer') {
 
 559       IS->get_customer(\%myconfig, $form);
 
 560       $::form->{billing_address_id} = $::form->{default_billing_address_id};
 
 562       IR->get_vendor(\%myconfig, $form);
 
 566   $form->{discount} =  $form->{"$form->{vc}_discount"} if defined $form->{"$form->{vc}_discount"};
 
 567   # Problem: Wenn man ohne Erneuern einen Kunden/Lieferanten
 
 568   # wechselt, wird der entsprechende Kunden/ Lieferantenrabatt
 
 569   # nicht übernommen. Grundproblem: In Commit 82574e78
 
 570   # hab ich aus discount customer_discount und vendor_discount
 
 571   # gemacht und entsprechend an den Oberflächen richtig hin-
 
 572   # geschoben. Die damals bessere Lösung wäre gewesen:
 
 573   # In den Templates nur die hidden für form-discount wieder ein-
 
 574   # setzen dann wäre die Verrenkung jetzt nicht notwendig.
 
 575   # TODO: Ggf. Bugfix 1284, 1575 und 817 wieder zusammenführen
 
 576   # Testfälle: Kunden mit Rabatt 0 -> Rabatt 20 i.O.
 
 577   #            Kunde mit Rabatt 20 -> Rabatt 0  i.O.
 
 578   #            Kunde mit Rabatt 20 -> Rabatt 5,5 i.O.
 
 579   $form->{payment_id} = $payment_id if $form->{payment_id} eq "";
 
 581   my $i = $form->{rowcount};
 
 583   if (   ($form->{"partnumber_$i"} eq "")
 
 584       && ($form->{"description_$i"} eq "")
 
 585       && ($form->{"partsgroup_$i"}  eq "")) {
 
 592     if ($form->{type} eq 'purchase_delivery_order') {
 
 593       IR->retrieve_item(\%myconfig, $form);
 
 596       IS->retrieve_item(\%myconfig, $form);
 
 600     my $rows = scalar @{ $form->{item_list} };
 
 603       $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
 604       if( !$form->{"qty_$i"} ) {
 
 605         $form->{"qty_$i"} = 1;
 
 610         select_item(mode => $mode, pre_entered_qty => $form->{"qty_$i"});
 
 611         $::dispatcher->end_request;
 
 615         my $sellprice = $form->parse_amount(\%myconfig, $form->{"sellprice_$i"});
 
 617         map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} } keys %{ $form->{item_list}[0] };
 
 619         $form->{"marge_price_factor_$i"} = $form->{item_list}->[0]->{price_factor};
 
 622           $form->{"sellprice_$i"} = $sellprice;
 
 624           my $record        = _make_record();
 
 625           my $price_source  = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
 
 626           my $best_price    = $price_source->best_price;
 
 627           my $best_discount = $price_source->best_discount;
 
 630             $::form->{"sellprice_$i"}           = $best_price->price;
 
 631             $::form->{"active_price_source_$i"} = $best_price->source;
 
 633           if ($best_discount) {
 
 634             $::form->{"discount_$i"}               = $best_discount->discount;
 
 635             $::form->{"active_discount_source_$i"} = $best_discount->source;
 
 639         $form->{"sellprice_$i"}          = $form->format_amount(\%myconfig, $form->{"sellprice_$i"});
 
 640         $form->{"lastcost_$i"}           = $form->format_amount(\%myconfig, $form->{"lastcost_$i"});
 
 641         $form->{"qty_$i"}                = $form->format_amount(\%myconfig, $form->{"qty_$i"});
 
 642         $form->{"discount_$i"}           = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0);
 
 649       # ok, so this is a new part
 
 650       # ask if it is a part or service item
 
 652       if (   $form->{"partsgroup_$i"}
 
 653           && ($form->{"partsnumber_$i"} eq "")
 
 654           && ($form->{"description_$i"} eq "")) {
 
 656         $form->{"discount_$i"} = "";
 
 657         $form->{"not_discountable_$i"} = "";
 
 661         $form->{"id_$i"}   = 0;
 
 667   $main::lxdebug->leave_sub();
 
 671   $main::lxdebug->enter_sub();
 
 675   my $form     = $main::form;
 
 676   my %myconfig = %main::myconfig;
 
 677   my $locale   = $main::locale;
 
 679   $form->{vc} = $form->{type} eq 'purchase_delivery_order' ? 'vendor' : 'customer';
 
 681   $form->get_lists("projects"       => { "key" => "ALL_PROJECTS",
 
 683                    "business_types" => "ALL_BUSINESS_TYPES");
 
 684   $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]);
 
 685   $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all_sorted;
 
 686   $form->{title}             = $locale->text('Delivery Orders');
 
 688   setup_do_search_action_bar();
 
 692   print $form->parse_html_template('do/search');
 
 694   $main::lxdebug->leave_sub();
 
 698   $main::lxdebug->enter_sub();
 
 702   my $form     = $main::form;
 
 703   my %myconfig = %main::myconfig;
 
 704   my $locale   = $main::locale;
 
 705   my $cgi      = $::request->{cgi};
 
 707   $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.MassDeliveryOrderPrint kivi.SalesPurchase));
 
 708   ($form->{ $form->{vc} }, $form->{"$form->{vc}_id"}) = split(/--/, $form->{ $form->{vc} });
 
 710   report_generator_set_default_sort('transdate', 1);
 
 714   $form->{rowcount} = scalar @{ $form->{DO} };
 
 717     ids                     transdate               reqdate
 
 719     ordnumber               customernumber          cusordnumber
 
 720     name                    employee  salesman
 
 721     shipvia                 globalprojectnumber
 
 722     transaction_description department
 
 727   $form->{l_open}      = $form->{l_closed} = "Y" if ($form->{open}      && $form->{closed});
 
 728   $form->{l_delivered} = "Y"                     if ($form->{delivered} && $form->{notdelivered});
 
 730   $form->{title}       = $locale->text('Delivery Orders');
 
 732   my $attachment_basename = $form->{vc} eq 'vendor' ? $locale->text('purchase_delivery_order_list') : $locale->text('sales_delivery_order_list');
 
 734   my $report = SL::ReportGenerator->new(\%myconfig, $form);
 
 736   my @hidden_variables = map { "l_${_}" } @columns;
 
 737   push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber serialnumber cusordnumber
 
 738                                           transaction_description transdatefrom transdateto reqdatefrom reqdateto
 
 739                                           type vc employee_id salesman_id project_id parts_partnumber parts_description
 
 740                                           insertdatefrom insertdateto business_id all department_id);
 
 742   my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
 
 745     'ids'                     => { raw_header_data => SL::Presenter::Tag::checkbox_tag("", id => "multi_all", checkall => "[data-checkall=1]"), align => 'center' },
 
 746     'transdate'               => { 'text' => $locale->text('Delivery Order Date'), },
 
 747     'reqdate'                 => { 'text' => $locale->text('Reqdate'), },
 
 748     'id'                      => { 'text' => $locale->text('ID'), },
 
 749     'donumber'                => { 'text' => $locale->text('Delivery Order'), },
 
 750     'ordnumber'               => { 'text' => $locale->text('Order'), },
 
 751     'customernumber'          => { 'text' => $locale->text('Customer Number'), },
 
 752     'cusordnumber'            => { 'text' => $locale->text('Customer Order Number'), },
 
 753     'name'                    => { 'text' => $form->{vc} eq 'customer' ? $locale->text('Customer') : $locale->text('Vendor'), },
 
 754     'employee'                => { 'text' => $locale->text('Employee'), },
 
 755     'salesman'                => { 'text' => $locale->text('Salesman'), },
 
 756     'shipvia'                 => { 'text' => $locale->text('Ship via'), },
 
 757     'globalprojectnumber'     => { 'text' => $locale->text('Project Number'), },
 
 758     'transaction_description' => { 'text' => $locale->text('Transaction description'), },
 
 759     'open'                    => { 'text' => $locale->text('Open'), },
 
 760     'delivered'               => { 'text' => $locale->text('Delivered'), },
 
 761     'department'              => { 'text' => $locale->text('Department'), },
 
 762     'insertdate'              => { 'text' => $locale->text('Insert Date'), },
 
 765   foreach my $name (qw(id transdate reqdate donumber ordnumber name employee salesman shipvia transaction_description department insertdate)) {
 
 766     my $sortdir                 = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
 
 767     $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
 
 770   $form->{"l_type"} = "Y";
 
 771   map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
 
 773   $column_defs{ids}->{visible} = 'HTML';
 
 775   $report->set_columns(%column_defs);
 
 776   $report->set_column_order(@columns);
 
 778   $report->set_export_options('orders', @hidden_variables, qw(sort sortdir));
 
 780   $report->set_sort_indicator($form->{sort}, $form->{sortdir});
 
 783   if ($form->{customer}) {
 
 784     push @options, $locale->text('Customer') . " : $form->{customer}";
 
 786   if ($form->{vendor}) {
 
 787     push @options, $locale->text('Vendor') . " : $form->{vendor}";
 
 789   if ($form->{cp_name}) {
 
 790     push @options, $locale->text('Contact Person') . " : $form->{cp_name}";
 
 792   if ($form->{department_id}) {
 
 793     push @options, $locale->text('Department') . " : " . SL::DB::Department->new(id => $form->{department_id})->load->description;
 
 795   if ($form->{donumber}) {
 
 796     push @options, $locale->text('Delivery Order Number') . " : $form->{donumber}";
 
 798   if ($form->{ordnumber}) {
 
 799     push @options, $locale->text('Order Number') . " : $form->{ordnumber}";
 
 801   push @options, $locale->text('Serial Number') . " : $form->{serialnumber}" if $form->{serialnumber};
 
 802   if ($form->{business_id}) {
 
 803     my $vc_type_label = $form->{vc} eq 'customer' ? $locale->text('Customer type') : $locale->text('Vendor type');
 
 804     push @options, $vc_type_label . " : " . SL::DB::Business->new(id => $form->{business_id})->load->description;
 
 806   if ($form->{transaction_description}) {
 
 807     push @options, $locale->text('Transaction description') . " : $form->{transaction_description}";
 
 809   if ($form->{parts_description}) {
 
 810     push @options, $locale->text('Part Description') . " : $form->{parts_description}";
 
 812   if ($form->{parts_partnumber}) {
 
 813     push @options, $locale->text('Part Number') . " : $form->{parts_partnumber}";
 
 815   if ( $form->{transdatefrom} or $form->{transdateto} ) {
 
 816     push @options, $locale->text('Delivery Order Date');
 
 817     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1)     if $form->{transdatefrom};
 
 818     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{transdateto},   1)     if $form->{transdateto};
 
 820   if ( $form->{reqdatefrom} or $form->{reqdateto} ) {
 
 821     push @options, $locale->text('Reqdate');
 
 822     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1)       if $form->{reqdatefrom};
 
 823     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{reqdateto},   1)       if $form->{reqdateto};
 
 825   if ( $form->{insertdatefrom} or $form->{insertdateto} ) {
 
 826     push @options, $locale->text('Insert Date');
 
 827     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1)    if $form->{insertdatefrom};
 
 828     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{insertdateto},   1)    if $form->{insertdateto};
 
 831     push @options, $locale->text('Open');
 
 833   if ($form->{closed}) {
 
 834     push @options, $locale->text('Closed');
 
 836   if ($form->{delivered}) {
 
 837     push @options, $locale->text('Delivered');
 
 839   if ($form->{notdelivered}) {
 
 840     push @options, $locale->text('Not delivered');
 
 842   push @options, $locale->text('Quick Search') . " : $form->{all}" if $form->{all};
 
 844   my $pr = SL::DB::Manager::Printer->find_by(
 
 845       printer_description => $::locale->text("sales_delivery_order_printer"));
 
 847       $form->{printer_id} = $pr->id;
 
 850   my $print_options = SL::Helper::PrintOptions->get_print_options(
 
 852       hide_language_id => 1,
 
 858   $report->set_options('top_info_text'        => join("\n", @options),
 
 859                        'raw_top_info_text'    => $form->parse_html_template('do/orders_top'),
 
 860                        'raw_bottom_info_text' => $form->parse_html_template('do/orders_bottom', { print_options => $print_options }),
 
 861                        'output_format'        => 'HTML',
 
 862                        'title'                => $form->{title},
 
 863                        'attachment_basename'  => $attachment_basename . strftime('_%Y%m%d', localtime time),
 
 865   $report->set_options_from_form();
 
 866   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
 
 868   # add sort and escape callback, this one we use for the add sub
 
 869   $form->{callback} = $href .= "&sort=$form->{sort}";
 
 871   # escape callback for href
 
 872   my $callback = $form->escape($href);
 
 874   my $edit_url       = build_std_url('action=edit', 'type', 'vc');
 
 875   my $edit_order_url = ($::instance_conf->get_feature_experimental_order)
 
 876                      ? build_std_url('script=controller.pl', 'action=Order/edit', 'type=' . ($form->{type} eq 'sales_delivery_order' ? 'sales_order' : 'purchase_order'))
 
 877                      : build_std_url('script=oe.pl',         'action=edit',       'type=' . ($form->{type} eq 'sales_delivery_order' ? 'sales_order' : 'purchase_order'));
 
 881   foreach my $dord (@{ $form->{DO} }) {
 
 882     $dord->{open}      = $dord->{closed}    ? $locale->text('No')  : $locale->text('Yes');
 
 883     $dord->{delivered} = $dord->{delivered} ? $locale->text('Yes') : $locale->text('No');
 
 885     my $row = { map { $_ => { 'data' => $dord->{$_} } } @columns };
 
 887     my $ord_id = $dord->{id};
 
 889       'raw_data' =>   $cgi->hidden('-name' => "trans_id_${idx}", '-value' => $ord_id)
 
 890                     . $cgi->checkbox('-name' => "multi_id_${idx}",' id' => "multi_id_id_".$ord_id, '-value' => 1, 'data-checkall' => 1, '-label' => ''),
 
 891       'valign'   => 'center',
 
 895     $row->{donumber}->{link}  = SL::DB::DeliveryOrder::TypeData::get3($dord->{order_type}, "show_menu", "new_controller")
 
 896                               ? SL::Controller::DeliveryOrder->url_for(action => "edit", id => $dord->{id}, type => $dord->{order_type})
 
 897                               : $edit_url  . "&id=" . E($dord->{id})      . "&callback=${callback}";
 
 898     $row->{ordnumber}->{link} = $edit_order_url . "&id=" . E($dord->{oe_id})   . "&callback=${callback}" if $dord->{oe_id};
 
 899     $report->add_data($row);
 
 904   setup_do_orders_action_bar();
 
 906   $report->generate_with_headers();
 
 908   $main::lxdebug->leave_sub();
 
 912   $main::lxdebug->enter_sub();
 
 918   my $form     = $main::form;
 
 919   my %myconfig = %main::myconfig;
 
 920   my $locale   = $main::locale;
 
 922   $form->mtime_ischanged('delivery_orders');
 
 924   $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
 
 926   $form->isblank("transdate", $locale->text('Delivery Order Date missing!'));
 
 928   $form->{donumber} =~ s/^\s*//g;
 
 929   $form->{donumber} =~ s/\s*$//g;
 
 931   my $msg = ucfirst $form->{vc};
 
 932   $form->isblank($form->{vc} . "_id", $locale->text($msg . " missing!"));
 
 934   # $locale->text('Customer missing!');
 
 935   # $locale->text('Vendor missing!');
 
 937   remove_emptied_rows();
 
 940   # check for serial number if part needs one
 
 941   for my $i (1 .. $form->{rowcount} - 1) {
 
 942     next unless $form->{"has_sernumber_$i"};
 
 943     $form->isblank("serialnumber_$i",
 
 944                    $locale->text('Serial Number missing in Row') . " $i");
 
 946   # if the name changed get new values
 
 947   my $vc = $form->{vc};
 
 948   if (($form->{"previous_${vc}_id"} || $form->{"${vc}_id"}) != $form->{"${vc}_id"}) {
 
 949     $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
 
 951     if ($vc eq 'customer') {
 
 952       IS->get_customer(\%myconfig, $form);
 
 953       $::form->{billing_address_id} = $::form->{default_billing_address_id};
 
 955       IR->get_vendor(\%myconfig, $form);
 
 959     $::dispatcher->end_request;
 
 962   $form->{id} = 0 if $form->{saveasnew};
 
 963   # we rely on converted_from_orderitems, if the workflow is used
 
 964   # be sure that at least one position is linked to the original orderitem
 
 965   if ($form->{convert_from_oe_ids}) {
 
 967     for my $i (1 .. $form->{rowcount}) {
 
 968       if ($form->{"converted_from_orderitems_id_$i"}) {
 
 973     if (!$has_linked_pos) {
 
 974       $form->error($locale->text('Need at least one original position for the workflow Order to Delivery Order!'));
 
 979   if(!exists $form->{addition}) {
 
 980     $form->{snumbers} = qq|donumber_| . $form->{donumber};
 
 981     $form->{addition} = "SAVED";
 
 984   # /saving the history
 
 986   $form->{simple_save} = 1;
 
 987   if (!$params{no_redirect} && !$form->{print_and_save}) {
 
 988     delete @{$form}{ary_diff([keys %{ $form }], [qw(login id script type cursor_fokus)])};
 
 990     $::dispatcher->end_request;
 
 992   $main::lxdebug->leave_sub();
 
 996   $main::lxdebug->enter_sub();
 
1000   my $form     = $main::form;
 
1001   my %myconfig = %main::myconfig;
 
1002   my $locale   = $main::locale;
 
1004   if ($ret = DO->delete()) {
 
1005     # saving the history
 
1006     if(!exists $form->{addition}) {
 
1007       $form->{snumbers} = qq|donumber_| . $form->{donumber};
 
1008       $form->{addition} = "DELETED";
 
1009       $form->save_history;
 
1011     # /saving the history
 
1013     $form->info($locale->text('Delivery Order deleted!'));
 
1014     $::dispatcher->end_request;
 
1017   $form->error($locale->text('Cannot delete delivery order!') . $ret);
 
1019   $main::lxdebug->leave_sub();
 
1021 sub delete_transfers {
 
1022   $main::lxdebug->enter_sub();
 
1026   my $form     = $main::form;
 
1027   my %myconfig = %main::myconfig;
 
1028   my $locale   = $main::locale;
 
1031   die "Invalid form type" unless $form->{type} =~ m/^(sales|purchase)_delivery_order$/;
 
1033   if ($ret = DO->delete_transfers()) {
 
1034     # saving the history
 
1035     if(!exists $form->{addition}) {
 
1036       $form->{snumbers} = qq|donumber_| . $form->{donumber};
 
1037       $form->{addition} = "UNDO TRANSFER";
 
1038       $form->save_history;
 
1040     # /saving the history
 
1042     flash_later('info', $locale->text("Transfer undone."));
 
1044     $form->{callback} = 'do.pl?action=edit&type=' . $form->{type} . '&id=' . $form->escape($form->{id});
 
1048   $form->error($locale->text('Cannot undo delivery order transfer!') . $ret);
 
1050   $main::lxdebug->leave_sub();
 
1054   $main::lxdebug->enter_sub();
 
1056   my $form     = $main::form;
 
1057   my %myconfig = %main::myconfig;
 
1058   my $locale   = $main::locale;
 
1061   $form->mtime_ischanged('delivery_orders');
 
1063   $main::auth->assert($form->{type} eq 'purchase_delivery_order' ? 'vendor_invoice_edit' : 'invoice_edit');
 
1065   $form->get_employee();
 
1067   $form->{convert_from_do_ids} = $form->{id};
 
1068   # if we have a reqdate (Liefertermin), this is definetely the preferred
 
1069   # deliverydate for invoices
 
1070   $form->{deliverydate}        = $form->{reqdate} || $form->{transdate};
 
1071   $form->{transdate}           = $form->{invdate} = $form->current_date(\%myconfig);
 
1072   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
 
1073   $form->{defaultcurrency}     = $form->get_default_currency(\%myconfig);
 
1075   $form->{rowcount}--;
 
1077   delete @{$form}{qw(id closed delivered)};
 
1079   my ($script, $buysell);
 
1080   if ($form->{type} eq 'purchase_delivery_order') {
 
1081     $form->{title}  = $locale->text('Add Vendor Invoice');
 
1082     $form->{script} = 'ir.pl';
 
1087     $form->{title}  = $locale->text('Add Sales Invoice');
 
1088     $form->{script} = 'is.pl';
 
1093   for my $i (1 .. $form->{rowcount}) {
 
1094     map { $form->{"${_}_${i}"} = $form->parse_amount(\%myconfig, $form->{"${_}_${i}"}) if $form->{"${_}_${i}"} } qw(ship qty sellprice lastcost basefactor discount);
 
1096     # adds a customer/vendor discount, unless we have a workflow case
 
1097     # CAVEAT: has to be done, after the above parse_amount
 
1098     unless ($form->{"ordnumber"}) {
 
1099       if ($form->{discount}) { # Falls wir einen Lieferanten-/Kundenrabatt haben
 
1100         # und rabattfähig sind, dann
 
1101         unless ($form->{"not_discountable_$i"}) {
 
1102           $form->{"discount_$i"} = $form->{discount}*100; # ... nehmen wir diesen Rabatt
 
1106     $form->{"donumber_$i"} = $form->{donumber};
 
1107     $form->{"converted_from_delivery_order_items_id_$i"} = delete $form->{"delivery_order_items_id_$i"};
 
1110   $form->{type} = "invoice";
 
1113   $main::locale = Locale->new("$myconfig{countrycode}", "$script");
 
1114   $locale = $main::locale;
 
1116   require "bin/mozilla/$form->{script}";
 
1118   my $currency = $form->{currency};
 
1121   if ($form->{ordnumber}) {
 
1122     require SL::DB::Order;
 
1123     my $vc_id  = $form->{type} =~ /^sales/ ? 'customer_id' : 'vendor_id';
 
1124     if (my $order = SL::DB::Manager::Order->find_by(ordnumber => $form->{ordnumber}, $vc_id => $form->{"$vc_id"})) {
 
1126       $form->{orddate} = $order->transdate_as_date;
 
1127       $form->{$_}      = $order->$_ for qw(payment_id salesman_id taxzone_id quonumber taxincluded);
 
1128       $form->{taxincluded_changed_by_user} = 1;
 
1132   $form->{currency}     = $currency;
 
1133   $form->{exchangerate} = "";
 
1134   $form->{forex}        = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, $buysell);
 
1135   $form->{exchangerate} = $form->{forex} if ($form->{forex});
 
1140   for my $i (1 .. $form->{rowcount}) {
 
1141     $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
 
1143     my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
 
1145     my $decimalplaces = ($dec > 2) ? $dec : 2;
 
1147     # copy delivery date from reqdate for order -> invoice conversion
 
1148     $form->{"deliverydate_$i"} = $form->{"reqdate_$i"}
 
1149       unless $form->{"deliverydate_$i"};
 
1152     $form->{"sellprice_$i"} =
 
1153       $form->format_amount(\%myconfig, $form->{"sellprice_$i"},
 
1156     $form->{"lastcost_$i"} =
 
1157       $form->format_amount(\%myconfig, $form->{"lastcost_$i"},
 
1160     (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
 
1161     $dec_qty = length $dec_qty;
 
1163       $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
 
1169   $main::lxdebug->leave_sub();
 
1173   $main::lxdebug->enter_sub();
 
1175   my $form     = $main::form;
 
1176   my %myconfig = %main::myconfig;
 
1177   my $locale   = $main::locale;
 
1180   $main::auth->assert($form->{type} eq 'sales_delivery_order' ? 'invoice_edit' : 'vendor_invoice_edit');
 
1182   my @do_ids = map { $form->{"trans_id_$_"} } grep { $form->{"multi_id_$_"} } (1..$form->{rowcount});
 
1184   if (!scalar @do_ids) {
 
1185     $form->show_generic_error($locale->text('You have not selected any delivery order.'));
 
1188   map { delete $form->{$_} } grep { m/^(?:trans|multi)_id_\d+/ } keys %{ $form };
 
1190   if (!DO->retrieve('vc' => $form->{vc}, 'ids' => \@do_ids)) {
 
1191     $form->show_generic_error($form->{vc} eq 'customer' ?
 
1192                               $locale->text('You cannot create an invoice for delivery orders for different customers.') :
 
1193                               $locale->text('You cannot create an invoice for delivery orders from different vendors.'),
 
1194                               'back_button' => 1);
 
1197   my $source_type              = $form->{type};
 
1198   $form->{convert_from_do_ids} = join ' ', @do_ids;
 
1199   # bei der auswahl von mehreren Lieferscheinen fuer eine Rechnung, die einfach in donumber_array
 
1200   # zwischenspeichern (DO.pm) und als ' '-separierte Liste wieder zurueckschreiben
 
1201   # Hinweis: delete gibt den wert zurueck und loescht danach das element (nett und einfach)
 
1202   # $shell: perldoc perlunc; /delete EXPR
 
1203   $form->{donumber}            = delete $form->{donumber_array};
 
1204   $form->{ordnumber}           = delete $form->{ordnumber_array};
 
1205   $form->{cusordnumber}        = delete $form->{cusordnumber_array};
 
1206   $form->{deliverydate}        = $form->{transdate};
 
1207   $form->{transdate}           = $form->current_date(\%myconfig);
 
1208   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
 
1209   $form->{type}                = "invoice";
 
1210   $form->{closed}              = 0;
 
1211   $form->{defaultcurrency}     = $form->get_default_currency(\%myconfig);
 
1213   my ($script, $buysell);
 
1214   if ($source_type eq 'purchase_delivery_order') {
 
1215     $form->{title}  = $locale->text('Add Vendor Invoice');
 
1216     $form->{script} = 'ir.pl';
 
1221     $form->{title}  = $locale->text('Add Sales Invoice');
 
1222     $form->{script} = 'is.pl';
 
1227   map { delete $form->{$_} } qw(id subject message cc bcc printed emailed queued);
 
1229   # get vendor or customer discount
 
1231   my $saved_form = save_form();
 
1232   if ($form->{vc} eq 'vendor') {
 
1233     IR->get_vendor(\%myconfig, \%$form);
 
1234     $vc_discount = $form->{vendor_discount};
 
1236     IS->get_customer(\%myconfig, \%$form);
 
1237     $vc_discount = $form->{customer_discount};
 
1239   # use payment terms from customer or vendor
 
1240   restore_form($saved_form,0,qw(payment_id));
 
1242   $form->{rowcount} = 0;
 
1243   foreach my $ref (@{ $form->{form_details} }) {
 
1244     $form->{rowcount}++;
 
1245     $ref->{reqdate} ||= $ref->{dord_transdate}; # copy transdates into each invoice row
 
1246     map { $form->{"${_}_$form->{rowcount}"} = $ref->{$_} } keys %{ $ref };
 
1247     map { $form->{"${_}_$form->{rowcount}"} = $form->format_amount(\%myconfig, $ref->{$_}) } qw(qty sellprice lastcost);
 
1248     $form->{"converted_from_delivery_order_items_id_$form->{rowcount}"} = delete $form->{"delivery_order_items_id_$form->{rowcount}"};
 
1250     if ($vc_discount){ # falls wir einen Lieferanten/Kundenrabatt haben
 
1251       # und keinen anderen discount wert an $i ...
 
1252       $form->{"discount_$form->{rowcount}"} ||= $vc_discount; # ... nehmen wir diesen Rabatt
 
1255     $form->{"discount_$form->{rowcount}"}   = $form->{"discount_$form->{rowcount}"}  * 100; #s.a. Bug 1151
 
1256     # Anm.: Eine Änderung des discounts in der SL/DO.pm->retrieve (select (doi.discount * 100) as discount) ergibt in psql einen
 
1257     # Wert von 10.0000001490116. Ferner ist der Rabatt in der Rechnung dann bei 1.0 (?). Deswegen lasse ich das hier. jb 10.10.09
 
1259     $form->{"discount_$form->{rowcount}"} = $form->format_amount(\%myconfig, $form->{"discount_$form->{rowcount}"});
 
1261   delete $form->{form_details};
 
1263   $locale = Locale->new("$myconfig{countrycode}", "$script");
 
1265   require "bin/mozilla/$form->{script}";
 
1272   $main::lxdebug->leave_sub();
 
1276   $main::lxdebug->enter_sub();
 
1280   my $form     = $main::form;
 
1282   $form->{saveasnew} = 1;
 
1283   $form->{closed}    = 0;
 
1284   $form->{delivered} = 0;
 
1285   map { delete $form->{$_} } qw(printed emailed queued);
 
1286   delete @{ $form }{ grep { m/^stock_(?:in|out)_\d+/ } keys %{ $form } };
 
1287   $form->{"converted_from_delivery_order_items_id_$_"} = delete $form->{"delivery_order_items_id_$_"} for 1 .. $form->{"rowcount"};
 
1288   # Let kivitendo assign a new order number if the user hasn't changed the
 
1289   # previous one. If it has been changed manually then use it as-is.
 
1290   $form->{donumber} =~ s/^\s*//g;
 
1291   $form->{donumber} =~ s/\s*$//g;
 
1292   if ($form->{saved_donumber} && ($form->{saved_donumber} eq $form->{donumber})) {
 
1293     delete($form->{donumber});
 
1298   $main::lxdebug->leave_sub();
 
1301 sub calculate_stock_in_out {
 
1302   $main::lxdebug->enter_sub();
 
1304   my $form     = $main::form;
 
1308   if (!$form->{"id_${i}"}) {
 
1309     $main::lxdebug->leave_sub();
 
1313   my $all_units = AM->retrieve_all_units();
 
1315   my $in_out   = $form->{type} =~ /^sales/ ? 'out' : 'in';
 
1316   my $sinfo    = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_${i}"});
 
1318   my $do_qty   = AM->sum_with_unit($::form->{"qty_$i"}, $::form->{"unit_$i"});
 
1319   my $sum      = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $sinfo });
 
1320   my $matches  = $do_qty == $sum;
 
1322   my $amount_unit = $all_units->{$form->{"partunit_$i"}}->{base_unit};
 
1323   my $content     = $form->format_amount(\%::myconfig, AM->convert_unit($amount_unit, $form->{"unit_$i"}) * $sum * 1) . ' ' . $form->{"unit_$i"};
 
1325   $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="?">|;
 
1327   $main::lxdebug->leave_sub();
 
1332 sub get_basic_bin_wh_info {
 
1333   $main::lxdebug->enter_sub();
 
1335   my $stock_info = shift;
 
1337   my $form     = $main::form;
 
1339   foreach my $sinfo (@{ $stock_info }) {
 
1340     next unless ($sinfo->{bin_id});
 
1342     my $bin_info = WH->get_basic_bin_info('id' => $sinfo->{bin_id});
 
1343     map { $sinfo->{"${_}_description"} = $sinfo->{"${_}description"} = $bin_info->{"${_}_description"} } qw(bin warehouse);
 
1346   $main::lxdebug->leave_sub();
 
1349 sub stock_in_out_form {
 
1350   $main::lxdebug->enter_sub();
 
1352   my $form     = $main::form;
 
1354   if ($form->{in_out} eq 'out') {
 
1360   $main::lxdebug->leave_sub();
 
1363 sub redo_stock_info {
 
1364   $main::lxdebug->enter_sub();
 
1368   my $form     = $main::form;
 
1370   my @non_empty = grep { $_->{qty} } @{ $params{stock_info} };
 
1372   if ($params{add_empty_row}) {
 
1374       'warehouse_id' => scalar(@non_empty) ? $non_empty[-1]->{warehouse_id} : undef,
 
1375       'bin_id'       => scalar(@non_empty) ? $non_empty[-1]->{bin_id}       : undef,
 
1379   @{ $params{stock_info} } = @non_empty;
 
1381   $main::lxdebug->leave_sub();
 
1384 sub update_stock_in {
 
1385   $main::lxdebug->enter_sub();
 
1387   my $form     = $main::form;
 
1388   my %myconfig = %main::myconfig;
 
1390   my $stock_info = [];
 
1392   foreach my $i (1..$form->{rowcount}) {
 
1393     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1394     push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(warehouse_id bin_id chargenumber
 
1395                                                                    bestbefore qty unit delivery_order_items_stock_id) };
 
1398   display_stock_in_form($stock_info);
 
1400   $main::lxdebug->leave_sub();
 
1404   $main::lxdebug->enter_sub();
 
1406   my $form     = $main::form;
 
1408   my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
 
1410   display_stock_in_form($stock_info);
 
1412   $main::lxdebug->leave_sub();
 
1415 sub display_stock_in_form {
 
1416   $main::lxdebug->enter_sub();
 
1418   my $stock_info = shift;
 
1420   my $form     = $main::form;
 
1421   my %myconfig = %main::myconfig;
 
1422   my $locale   = $main::locale;
 
1424   $form->{title} = $locale->text('Stock');
 
1426   my $part_info  = IC->get_basic_part_info('id' => $form->{parts_id});
 
1428   # Standardlagerplatz für Standard-Auslagern verwenden, falls keiner für die Ware explizit definiert wurde
 
1429   if ($::instance_conf->get_transfer_default_use_master_default_bin) {
 
1430     $part_info->{warehouse_id} ||= $::instance_conf->get_warehouse_id;
 
1431     $part_info->{bin_id}       ||= $::instance_conf->get_bin_id;
 
1434   my $units      = AM->retrieve_units(\%myconfig, $form);
 
1435   # der zweite Parameter von unit_select_data gibt den default-Namen (selected) vor
 
1436   my $units_data = AM->unit_select_data($units, $form->{do_unit}, undef, $part_info->{unit});
 
1438   $form->get_lists('warehouses' => { 'key'    => 'WAREHOUSES',
 
1439                                      'bins'   => 'BINS' });
 
1441   redo_stock_info('stock_info' => $stock_info, 'add_empty_row' => !$form->{delivered});
 
1443   get_basic_bin_wh_info($stock_info);
 
1445   $form->header(no_layout => 1);
 
1446   print $form->parse_html_template('do/stock_in_form', { 'UNITS'      => $units_data,
 
1447                                                          'STOCK_INFO' => $stock_info,
 
1448                                                          'PART_INFO'  => $part_info, });
 
1450   $main::lxdebug->leave_sub();
 
1453 sub _stock_in_out_set_qty_display {
 
1454   my $stock_info       = shift;
 
1456   my $all_units        = AM->retrieve_all_units();
 
1457   my $sum              = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1458   my $amount_unit      = $all_units->{$form->{"partunit"}}->{base_unit};
 
1459   $form->{qty_display} = $form->format_amount(\%::myconfig, AM->convert_unit($amount_unit, $form->{"do_unit"}) * $sum * 1) . ' ' . $form->{"do_unit"};
 
1463   $main::lxdebug->enter_sub();
 
1465   my $form     = $main::form;
 
1466   my %myconfig = %main::myconfig;
 
1468   my $stock_info = [];
 
1470   foreach my $i (1..$form->{rowcount}) {
 
1471     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1473     next if ($form->{"qty_$i"} <= 0);
 
1475     push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(delivery_order_items_stock_id warehouse_id bin_id chargenumber bestbefore qty unit) };
 
1478   $form->{stock} = SL::YAML::Dump($stock_info);
 
1480   _stock_in_out_set_qty_display($stock_info);
 
1482   my $do_qty       = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
 
1483   my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1486   print $form->parse_html_template('do/set_stock_in_out', {
 
1487     qty_matches => $do_qty == $transfer_qty,
 
1490   $main::lxdebug->leave_sub();
 
1493 sub stock_out_form {
 
1494   $main::lxdebug->enter_sub();
 
1496   my $form     = $main::form;
 
1497   my %myconfig = %main::myconfig;
 
1498   my $locale   = $main::locale;
 
1500   $form->{title} = $locale->text('Release From Stock');
 
1502   my $part_info  = IC->get_basic_part_info('id' => $form->{parts_id});
 
1504   my $units      = AM->retrieve_units(\%myconfig, $form);
 
1505   my $units_data = AM->unit_select_data($units, undef, undef, $part_info->{unit});
 
1507   my @contents   = DO->get_item_availability('parts_id' => $form->{parts_id});
 
1509   my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
 
1511   if (!$form->{delivered}) {
 
1512     foreach my $row (@contents) {
 
1513       $row->{available_qty} = $form->format_amount(\%::myconfig, $row->{qty} * 1) . ' ' . $part_info->{unit};
 
1515       foreach my $sinfo (@{ $stock_info }) {
 
1516         next if (($row->{bin_id}       != $sinfo->{bin_id}) ||
 
1517                  ($row->{warehouse_id} != $sinfo->{warehouse_id}) ||
 
1518                  ($row->{chargenumber} ne $sinfo->{chargenumber}) ||
 
1519                  ($row->{bestbefore}   ne $sinfo->{bestbefore}));
 
1521         map { $row->{"stock_$_"} = $sinfo->{$_} } qw(qty unit error delivery_order_items_stock_id);
 
1526     get_basic_bin_wh_info($stock_info);
 
1528     foreach my $sinfo (@{ $stock_info }) {
 
1529       map { $sinfo->{"stock_$_"} = $sinfo->{$_} } qw(qty unit);
 
1533   $form->header(no_layout => 1);
 
1534   print $form->parse_html_template('do/stock_out_form', { 'UNITS'      => $units_data,
 
1535                                                           'WHCONTENTS' => $form->{delivered} ? $stock_info : \@contents,
 
1536                                                           'PART_INFO'  => $part_info, });
 
1538   $main::lxdebug->leave_sub();
 
1542   $main::lxdebug->enter_sub();
 
1544   my $form     = $main::form;
 
1545   my %myconfig = %main::myconfig;
 
1546   my $locale   = $main::locale;
 
1548   my $stock_info = [];
 
1550   foreach my $i (1 .. $form->{rowcount}) {
 
1551     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1553     next if ($form->{"qty_$i"} <= 0);
 
1555     push @{ $stock_info }, {
 
1556       'warehouse_id' => $form->{"warehouse_id_$i"},
 
1557       'bin_id'       => $form->{"bin_id_$i"},
 
1558       'chargenumber' => $form->{"chargenumber_$i"},
 
1559       'bestbefore'   => $form->{"bestbefore_$i"},
 
1560       'qty'          => $form->{"qty_$i"},
 
1561       'unit'         => $form->{"unit_$i"},
 
1563       'delivery_order_items_stock_id'  => $form->{"delivery_order_items_stock_id_$i"},
 
1567   my @errors     = DO->check_stock_availability('requests' => $stock_info,
 
1568                                                 'parts_id' => $form->{parts_id});
 
1570   $form->{stock} = SL::YAML::Dump($stock_info);
 
1573     $form->{ERRORS} = [];
 
1574     map { push @{ $form->{ERRORS} }, $locale->text('Error in row #1: The quantity you entered is bigger than the stocked quantity.', $_->{row}); } @errors;
 
1575     stock_in_out_form();
 
1578     _stock_in_out_set_qty_display($stock_info);
 
1580     my $do_qty       = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
 
1581     my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1584     print $form->parse_html_template('do/set_stock_in_out', {
 
1585       qty_matches => $do_qty == $transfer_qty,
 
1589   $main::lxdebug->leave_sub();
 
1593   $main::lxdebug->enter_sub();
 
1595   my $form     = $main::form;
 
1596   my %myconfig = %main::myconfig;
 
1597   my $locale   = $main::locale;
 
1599   if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
 
1600     $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred in.'));
 
1603   save(no_redirect => 1);
 
1605   my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_in_${_}"} } (1 .. $form->{rowcount});
 
1609     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1610     my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1613     $form->{ERRORS}   = [];
 
1615     foreach my $i (1 .. $form->{rowcount}) {
 
1616       next unless ($form->{"id_$i"} && $form->{"stock_in_$i"});
 
1618       my $row_sum_base_qty = 0;
 
1619       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1621       foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_in_$i"}) }) {
 
1622         $request->{parts_id}  = $form->{"id_$i"};
 
1623         $row_sum_base_qty    += $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
 
1625         $request->{project_id} = $form->{"project_id_$i"} || $form->{globalproject_id};
 
1627         push @all_requests, $request;
 
1630       next if (0 == $row_sum_base_qty);
 
1632       my $do_base_qty = $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1634 #      if ($do_base_qty != $row_sum_base_qty) {
 
1635 #        push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no stock at all or the full quantity of #2 #3.',
 
1636 #                                                 $i, $form->{"qty_$i"}, $form->{"unit_$i"});
 
1640     if (@{ $form->{ERRORS} }) {
 
1641       push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
 
1643       set_headings('edit');
 
1645       $main::lxdebug->leave_sub();
 
1647       $::dispatcher->end_request;
 
1651   DO->transfer_in_out('direction' => 'in',
 
1652                       'requests'  => \@all_requests);
 
1654   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1656   flash_later('info', $locale->text("Transfer successful"));
 
1657   $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id});
 
1660   $main::lxdebug->leave_sub();
 
1664   $main::lxdebug->enter_sub();
 
1666   my $form     = $main::form;
 
1667   my %myconfig = %main::myconfig;
 
1668   my $locale   = $main::locale;
 
1670   if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
 
1671     $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred out.'));
 
1674   save(no_redirect => 1);
 
1676   my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_out_${_}"} } (1 .. $form->{rowcount});
 
1680     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1681     my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1684     $form->{ERRORS}   = [];
 
1686     foreach my $i (1 .. $form->{rowcount}) {
 
1687       next unless ($form->{"id_$i"} && $form->{"stock_out_$i"});
 
1689       my $row_sum_base_qty = 0;
 
1690       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1692       foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_out_$i"}) }) {
 
1693         $request->{parts_id} = $form->{"id_$i"};
 
1694         $request->{base_qty} = $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
 
1695         $request->{project_id} = $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id};
 
1697         my $map_key          = join '--', ($form->{"id_$i"}, @{$request}{qw(warehouse_id bin_id chargenumber bestbefore)});
 
1699         $request_map{$map_key}                 ||= $request;
 
1700         $request_map{$map_key}->{sum_base_qty} ||= 0;
 
1701         $request_map{$map_key}->{sum_base_qty}  += $request->{base_qty};
 
1702         $row_sum_base_qty                       += $request->{base_qty};
 
1704         push @all_requests, $request;
 
1707       next if (0 == $row_sum_base_qty);
 
1709       my $do_base_qty = $form->{"qty_$i"} * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1711 #      if ($do_base_qty != $row_sum_base_qty) {
 
1712 #        push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no transfer at all or the full quantity of #2 #3.',
 
1713 #                                                 $i, $form->{"qty_$i"}, $form->{"unit_$i"});
 
1718       my @bin_ids      = map { $_->{bin_id} } values %request_map;
 
1719       my %bin_info_map = WH->get_basic_bin_info('id' => \@bin_ids);
 
1720       my @contents     = DO->get_item_availability('parts_id' => \@part_ids);
 
1722       foreach my $inv (@contents) {
 
1723         my $map_key = join '--', @{$inv}{qw(parts_id warehouse_id bin_id chargenumber bestbefore)};
 
1725         next unless ($request_map{$map_key});
 
1727         my $request    = $request_map{$map_key};
 
1728         $request->{ok} = $request->{sum_base_qty} <= $inv->{qty};
 
1731       foreach my $request (values %request_map) {
 
1732         next if ($request->{ok});
 
1734         my $pinfo = $part_info_map{$request->{parts_id}};
 
1735         my $binfo = $bin_info_map{$request->{bin_id}};
 
1737         if ($::instance_conf->get_show_bestbefore) {
 
1738             push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, #5, for the transfer of #6.",
 
1739                                                      $pinfo->{description},
 
1740                                                      $binfo->{warehouse_description},
 
1741                                                      $binfo->{bin_description},
 
1742                                                      $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
 
1743                                                      $request->{bestbefore} ? $locale->text('bestbefore #1', $request->{bestbefore}) : $locale->text('no bestbefore'),
 
1744                                                      $form->format_amount(\%::myconfig, $request->{sum_base_qty}) . ' ' . $pinfo->{unit});
 
1746             push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, for the transfer of #5.",
 
1747                                                      $pinfo->{description},
 
1748                                                      $binfo->{warehouse_description},
 
1749                                                      $binfo->{bin_description},
 
1750                                                      $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
 
1751                                                      $form->format_amount(\%::myconfig, $request->{sum_base_qty}) . ' ' . $pinfo->{unit});
 
1756     if (@{ $form->{ERRORS} }) {
 
1757       push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
 
1759       set_headings('edit');
 
1761       $main::lxdebug->leave_sub();
 
1763       $::dispatcher->end_request;
 
1766   DO->transfer_in_out('direction' => 'out',
 
1767                       'requests'  => \@all_requests);
 
1769   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1771   flash_later('info', $locale->text("Transfer successful"));
 
1772   $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id});
 
1775   $main::lxdebug->leave_sub();
 
1779   $main::lxdebug->enter_sub();
 
1781   my $form     = $main::form;
 
1783   DO->close_orders('ids' => [ $form->{id} ]);
 
1785   $form->{closed} = 1;
 
1789   $main::lxdebug->leave_sub();
 
1793   $::lxdebug->enter_sub;
 
1795   $::auth->assert('purchase_delivery_order_edit | sales_delivery_order_edit');
 
1798   retrieve_partunits();
 
1800   my $new_rowcount = $::form->{"rowcount"} * 1 + 1;
 
1801   $::form->{"project_id_${new_rowcount}"} = $::form->{"globalproject_id"};
 
1803   $::form->language_payment(\%::myconfig);
 
1805   Common::webdav_folder($::form);
 
1808   display_row(++$::form->{rowcount});
 
1811   $::lxdebug->leave_sub;
 
1815   call_sub($main::form->{yes_nextsub});
 
1819   call_sub($main::form->{no_nextsub});
 
1823   call_sub($main::form->{update_nextsub} || $main::form->{nextsub} || 'update_delivery_order');
 
1827   my $form     = $main::form;
 
1828   my $locale   = $main::locale;
 
1830   foreach my $action (qw(update print save transfer_out transfer_out_default sort
 
1831                          transfer_in transfer_in_default mark_closed save_as_new invoice delete)) {
 
1832     if ($form->{"action_${action}"}) {
 
1838   $form->error($locale->text('No action defined.'));
 
1841 sub transfer_out_default {
 
1842   $main::lxdebug->enter_sub();
 
1844   my $form     = $main::form;
 
1846   transfer_in_out_default('direction' => 'out');
 
1848   $main::lxdebug->leave_sub();
 
1851 sub transfer_in_default {
 
1852   $main::lxdebug->enter_sub();
 
1854   my $form     = $main::form;
 
1856   transfer_in_out_default('direction' => 'in');
 
1858   $main::lxdebug->leave_sub();
 
1861 # Falls das Standardlagerverfahren aktiv ist, wird
 
1862 # geprüft, ob alle Standardlagerplätze für die Auslager-
 
1863 # artikel vorhanden sind UND ob die Warenmenge ausreicht zum
 
1864 # Auslagern. Falls nicht wird entsprechend eine Fehlermeldung
 
1865 # generiert. Offen Chargennummer / bestbefore wird nicht berücksichtigt
 
1866 sub transfer_in_out_default {
 
1867   $main::lxdebug->enter_sub();
 
1869   my $form     = $main::form;
 
1870   my %myconfig = %main::myconfig;
 
1871   my $locale   = $main::locale;
 
1874   my (%missing_default_bins, %qty_parts, @all_requests, %part_info_map, $default_warehouse_id, $default_bin_id);
 
1876   Common::check_params(\%params, qw(direction));
 
1878   # entsprechende defaults holen, falls standardlagerplatz verwendet werden soll
 
1879   if ($::instance_conf->get_transfer_default_use_master_default_bin) {
 
1880     $default_warehouse_id = $::instance_conf->get_warehouse_id;
 
1881     $default_bin_id       = $::instance_conf->get_bin_id;
 
1885   my @part_ids = map { $form->{"id_${_}"} } (1 .. $form->{rowcount});
 
1887     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1888     %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1889     foreach my $i (1 .. $form->{rowcount}) {
 
1890       next unless ($form->{"id_$i"});
 
1891       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1892       my $qty =   $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1894       $form->show_generic_error($locale->text("Cannot transfer negative entries." )) if ($qty < 0);
 
1895       # if we do not want to transfer services and this part is a service, set qty to zero
 
1896       # ... and do not create a hash entry in %qty_parts below (will skip check for bins for the transfer == out case)
 
1897       # ... 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)
 
1899       $qty = 0 if (!$::instance_conf->get_transfer_default_services && $part_info_map{$form->{"id_$i"}}->{part_type} eq 'service');
 
1900       $qty_parts{$form->{"id_$i"}} += $qty;
 
1902         delete $qty_parts{$form->{"id_$i"}} unless $qty_parts{$form->{"id_$i"}};
 
1903         undef $form->{"stock_in_$i"};
 
1906       $part_info_map{$form->{"id_$i"}}{bin_id}       ||= $default_bin_id;
 
1907       $part_info_map{$form->{"id_$i"}}{warehouse_id} ||= $default_warehouse_id;
 
1909       push @all_requests, ($qty == 0) ? { } : {
 
1910                         'chargenumber' => '',  #?? die müsste entsprechend geholt werden
 
1911                         #'bestbefore' => undef, # TODO wird nicht berücksichtigt
 
1912                         'bin_id' => $part_info_map{$form->{"id_$i"}}{bin_id},
 
1914                         'parts_id' => $form->{"id_$i"},
 
1915                         'comment' => $locale->text("Default transfer delivery order"),
 
1916                         'unit' => $part_info_map{$form->{"id_$i"}}{unit},
 
1917                         'warehouse_id' => $part_info_map{$form->{"id_$i"}}{warehouse_id},
 
1918                         'oe_id' => $form->{id},
 
1919                         'project_id' => $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id}
 
1923     # jetzt wird erst überprüft, ob die Stückzahl entsprechend stimmt.
 
1924     # check if bin (transfer in and transfer out and qty (transfer out) is correct
 
1925     foreach my $key (keys %qty_parts) {
 
1927       $missing_default_bins{$key}{missing_bin} = 1 unless ($part_info_map{$key}{bin_id});
 
1928       next unless ($part_info_map{$key}{bin_id}); # abbruch
 
1930       if ($params{direction} eq 'out') {  # wird nur für ausgehende Mengen benötigt
 
1931         my ($max_qty, $error) = WH->get_max_qty_parts_bin(parts_id => $key, bin_id => $part_info_map{$key}{bin_id});
 
1933           # wir können nicht entscheiden, welche charge oder mhd (bestbefore) ausgewählt sein soll
 
1934           # deshalb rückmeldung nach oben geben, manuell auszulagern
 
1935           # TODO Bei nur einem Treffer mit Charge oder bestbefore wäre das noch möglich
 
1936           $missing_default_bins{$key}{chargenumber} = 1;
 
1938         if ($max_qty < $qty_parts{$key}){
 
1939           $missing_default_bins{$key}{missing_qty} = $max_qty - $qty_parts{$key};
 
1945   # Abfrage für Fehlerbehandlung (nur bei direction == out)
 
1946   if (scalar (keys %missing_default_bins)) {
 
1948     foreach my $fehler (keys %missing_default_bins) {
 
1950       my $ware = WH->get_part_description(parts_id => $fehler);
 
1951       if ($missing_default_bins{$fehler}{missing_bin}){
 
1952         $fehlertext .= "Kein Standardlagerplatz definiert bei $ware <br>";
 
1954       if ($missing_default_bins{$fehler}{missing_qty}) {  # missing_qty
 
1955         $fehlertext .= "Es fehlen " . $missing_default_bins{$fehler}{missing_qty}*-1 .
 
1956                        " von $ware auf dem Standard-Lagerplatz " . $part_info_map{$fehler}{bin} .   " zum Auslagern<br>";
 
1958       if ($missing_default_bins{$fehler}{chargenumber}){
 
1959         $fehlertext .= "Die Ware hat eine Chargennummer oder eine Mindesthaltbarkeit definiert.
 
1960                         Hier kann man nicht automatisch entscheiden.
 
1961                         Bitte diesen Lieferschein manuell auslagern.
 
1964       # auslagern soll immer gehen, auch wenn nicht genügend auf lager ist.
 
1965       # der lagerplatz ist hier extra konfigurierbar, bspw. Lager-Korrektur mit
 
1966       # Lagerplatz Lagerplatz-Korrektur
 
1967       my $default_warehouse_id_ignore_onhand = $::instance_conf->get_warehouse_id_ignore_onhand;
 
1968       my $default_bin_id_ignore_onhand       = $::instance_conf->get_bin_id_ignore_onhand;
 
1969       if ($::instance_conf->get_transfer_default_ignore_onhand && $default_bin_id_ignore_onhand) {
 
1970         # entsprechende defaults holen
 
1971         # falls chargenumber, bestbefore oder anzahl nicht stimmt, auf automatischen
 
1972         # lagerplatz wegbuchen!
 
1973         foreach (@all_requests) {
 
1974           if ($_->{parts_id} eq $fehler){
 
1975           $_->{bin_id}        = $default_bin_id_ignore_onhand;
 
1976           $_->{warehouse_id}  = $default_warehouse_id_ignore_onhand;
 
1980         #$main::lxdebug->message(0, 'Fehlertext: ' . $fehlertext);
 
1981         $form->show_generic_error($locale->text("Cannot transfer. <br> Reason:<br>#1", $fehlertext ));
 
1987   # hier der eigentliche fallunterschied für in oder out
 
1988   my $prefix   = $params{direction} eq 'in' ? 'in' : 'out';
 
1990   # dieser array_ref ist für DO->save da:
 
1991   # einmal die all_requests in YAML verwandeln, damit delivery_order_items_stock
 
1992   # gefüllt werden kann.
 
1993   # could be dumped to the form in the first loop,
 
1994   # but maybe bin_id and warehouse_id has changed to the "korrekturlager" with
 
1995   # allowed negative qty ($::instance_conf->get_warehouse_id_ignore_onhand) ...
 
1997   foreach (@all_requests){
 
1999     next unless scalar(%{ $_ });
 
2000     $form->{"stock_${prefix}_$i"} = SL::YAML::Dump([$_]);
 
2003   save(no_redirect => 1); # Wir können auslagern, deshalb beleg speichern
 
2004                           # und in delivery_order_items_stock speichern
 
2006   # ... and fill back the persistent dois_id for inventory fk
 
2007   undef (@all_requests);
 
2008   foreach my $i (1 .. $form->{rowcount}) {
 
2009     next unless ($form->{"id_$i"} && $form->{"stock_${prefix}_$i"});
 
2010     push @all_requests, @{ DO->unpack_stock_information('packed' => $form->{"stock_${prefix}_$i"}) };
 
2012   DO->transfer_in_out('direction' => $prefix,
 
2013                       'requests'  => \@all_requests);
 
2015   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
2017   $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'out';
 
2018   $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'in';
 
2024   $main::lxdebug->enter_sub();
 
2028   my $form     = $main::form;
 
2031   save(no_redirect => 1); # has to be done, at least for newly added positions
 
2033   # hashify partnumbers, positions. key is delivery_order_items_id
 
2034   for my $i (1 .. ($form->{rowcount}) ) {
 
2035     $temp_hash{$form->{"delivery_order_items_id_$i"}} = { runningnumber => $form->{"runningnumber_$i"}, partnumber => $form->{"partnumber_$i"} };
 
2036     if ($form->{id} && $form->{"discount_$i"}) {
 
2037       # prepare_order assumes a db value if there is a form->id and multiplies *100
 
2038       # We hope for new controller code (no more format_amount/parse_amount distinction)
 
2039       $form->{"discount_$i"} /=100;
 
2042   # naturally sort partnumbers and get a sorted array of doi_ids
 
2043   my @sorted_doi_ids =  sort { Sort::Naturally::ncmp($temp_hash{$a}->{"partnumber"}, $temp_hash{$b}->{"partnumber"}) }  keys %temp_hash;
 
2048   for (@sorted_doi_ids) {
 
2049     $form->{"runningnumber_$temp_hash{$_}->{runningnumber}"} = $new_number;
 
2052   # all parse_amounts changes are in form (i.e. , to .) therefore we need
 
2053   # another format_amount to change it back, for the next save ;-(
 
2054   # works great except for row discounts (see above comment)
 
2058     $main::lxdebug->leave_sub();
 
2070 do.pl - Script for all calls to delivery order
 
2078 Sorts all position with Natural Sort. Can be activated in form_footer.html like this
 
2079 C<E<lt>input class="submit" type="submit" name="action_sort" id="sort_button" value="[% 'Sort and Save' | $T8 %]"E<gt>>
 
2085 Sort and Save can be implemented as an optional button if configuration ca be set by client config.
 
2086 Example coding for database scripts and templates in (git show af2f24b8), check also
 
2087 autogeneration for rose (scripts/rose_auto_create_model.pl --h)