1 #=====================================================================
 
   4 # Based on SQL-Ledger Version 2.1.9
 
   5 # Web http://www.lx-office.org
 
   7 #=====================================================================
 
   8 # SQL-Ledger, Accounting
 
   9 # Copyright (c) 1998-2003
 
  11 #  Author: Dieter Simader
 
  12 #   Email: dsimader@sql-ledger.org
 
  13 #     Web: http://www.sql-ledger.org
 
  16 # This program is free software; you can redistribute it and/or modify
 
  17 # it under the terms of the GNU General Public License as published by
 
  18 # the Free Software Foundation; either version 2 of the License, or
 
  19 # (at your option) any later version.
 
  21 # This program is distributed in the hope that it will be useful,
 
  22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 
  23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
  24 # GNU General Public License for more details.
 
  25 # You should have received a copy of the GNU General Public License
 
  26 # along with this program; if not, write to the Free Software
 
  27 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
  29 #======================================================================
 
  32 #======================================================================
 
  35 use List::MoreUtils qw(uniq);
 
  36 use List::Util qw(max sum);
 
  37 use POSIX qw(strftime);
 
  40 use SL::DB::DeliveryOrder;
 
  44 use SL::MoreCommon qw(ary_diff restore_form save_form);
 
  45 use SL::ReportGenerator;
 
  47 use Sort::Naturally ();
 
  48 require "bin/mozilla/arap.pl";
 
  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   # get customer/vendor
 
 176   $form->all_vc(\%myconfig, $form->{vc}, ($form->{vc} eq 'customer') ? "AR" : "AP");
 
 178   # retrieve order/quotation
 
 179   my $editing = $form->{id};
 
 181   DO->retrieve('vc'  => $form->{vc},
 
 182                'ids' => $form->{id});
 
 184   $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes delivery_term_id currency));
 
 186   # get customer / vendor
 
 187   if ($form->{vc} eq 'vendor') {
 
 188     IR->get_vendor(\%myconfig, \%$form);
 
 189     $form->{discount} = $form->{vendor_discount};
 
 191     IS->get_customer(\%myconfig, \%$form);
 
 192     $form->{discount} = $form->{customer_discount};
 
 195   $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id delivery_term_id));
 
 196   $form->restore_vars(qw(currency)) if ($form->{id} || $form->{convert_from_oe_ids});
 
 197   $form->restore_vars(qw(taxincluded)) if $form->{id};
 
 198   $form->restore_vars(qw(salesman_id)) if $editing;
 
 200   if ($form->{"all_$form->{vc}"}) {
 
 201     unless ($form->{"$form->{vc}_id"}) {
 
 202       $form->{"$form->{vc}_id"} = $form->{"all_$form->{vc}"}->[0]->{id};
 
 206   ($form->{ $form->{vc} })  = split /--/, $form->{ $form->{vc} };
 
 207   $form->{"old$form->{vc}"} = qq|$form->{$form->{vc}}--$form->{"$form->{vc}_id"}|;
 
 209   $form->{employee} = "$form->{employee}--$form->{employee_id}";
 
 211   $main::lxdebug->leave_sub();
 
 215   $main::lxdebug->enter_sub();
 
 219   my $form     = $main::form;
 
 220   my %myconfig = %main::myconfig;
 
 222   $form->{formname} = $form->{type} unless $form->{formname};
 
 225   foreach my $ref (@{ $form->{form_details} }) {
 
 226     $form->{rowcount} = ++$i;
 
 228     map { $form->{"${_}_$i"} = $ref->{$_} } keys %{$ref};
 
 230   for my $i (1 .. $form->{rowcount}) {
 
 232       $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100);
 
 234       $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
 
 236     my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
 
 238     my $decimalplaces = ($dec > 2) ? $dec : 2;
 
 240     # copy reqdate from deliverydate for invoice -> order conversion
 
 241     $form->{"reqdate_$i"} = $form->{"deliverydate_$i"} unless $form->{"reqdate_$i"};
 
 243     $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
 
 244     $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}, $decimalplaces);
 
 246     (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
 
 247     $dec_qty = length $dec_qty;
 
 248     $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
 
 251   $main::lxdebug->leave_sub();
 
 255   $main::lxdebug->enter_sub();
 
 259   my $form     = $main::form;
 
 260   my %myconfig = %main::myconfig;
 
 262   $form->{employee_id} = $form->{old_employee_id} if $form->{old_employee_id};
 
 263   $form->{salesman_id} = $form->{old_salesman_id} if $form->{old_salesman_id};
 
 265   my $vc = $form->{vc} eq "customer" ? "customers" : "vendors";
 
 266   $form->get_lists($vc              => "ALL_VC",
 
 267                    "price_factors"  => "ALL_PRICE_FACTORS",
 
 268                    "business_types" => "ALL_BUSINESS_TYPES",
 
 270   $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all;
 
 273   my @old_project_ids = uniq grep { $_ } map { $_ * 1 } ($form->{"globalproject_id"}, map { $form->{"project_id_$_"} } 1..$form->{"rowcount"});
 
 274   my @old_ids_cond    = @old_project_ids ? (id => \@old_project_ids) : ();
 
 276   if (($vc eq 'customers') && $::instance_conf->get_customer_projects_only_in_sales) {
 
 279         customer_id          => $::form->{customer_id},
 
 280         billable_customer_id => $::form->{customer_id},
 
 285       and => [ active => 1, @customer_cond ],
 
 289   $::form->{ALL_PROJECTS}          = SL::DB::Manager::Project->get_all_sorted(query => \@conditions);
 
 290   $::form->{ALL_EMPLOYEES}         = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{employee_id},  deleted => 0 ] ]);
 
 291   $::form->{ALL_SALESMEN}          = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{salesman_id},  deleted => 0 ] ]);
 
 292   $::form->{ALL_SHIPTO}            = SL::DB::Manager::Shipto->get_all_sorted(query => [
 
 293     or => [ trans_id  => $::form->{"$::form->{vc}_id"} * 1, and => [ shipto_id => $::form->{shipto_id} * 1, trans_id => undef ] ]
 
 295   $::form->{ALL_CONTACTS}          = SL::DB::Manager::Contact->get_all_sorted(query => [
 
 297       cp_cv_id => $::form->{"$::form->{vc}_id"} * 1,
 
 300         cp_id    => $::form->{cp_id} * 1
 
 305   map { $_->{value} = "$_->{name}--$_->{id}"        } @{ $form->{ALL_VC} };
 
 307   $form->{SHOW_VC_DROP_DOWN} =  $myconfig{vclimit} > scalar @{ $form->{ALL_VC} };
 
 309   $form->{oldvcname}         =  $form->{"old$form->{vc}"};
 
 310   $form->{oldvcname}         =~ s/--.*//;
 
 312   my $dispatch_to_popup = '';
 
 313   if ($form->{resubmit} && ($form->{format} eq "html")) {
 
 314     $dispatch_to_popup  = "window.open('about:blank','Beleg'); document.do.target = 'Beleg';";
 
 315     $dispatch_to_popup .= "document.do.submit();";
 
 316   } elsif ($form->{resubmit}) {
 
 317     # emulate click for resubmitting actions
 
 318     $dispatch_to_popup  = "document.do.${_}.click(); " for grep { /^action_/ } keys %$form;
 
 320   $::request->{layout}->add_javascripts_inline("\$(function(){$dispatch_to_popup});");
 
 323   my $follow_up_vc                =  $form->{ $form->{vc} eq 'customer' ? 'customer' : 'vendor' };
 
 324   $follow_up_vc                   =~ s/--\d*\s*$//;
 
 326   $form->{follow_up_trans_info} = $form->{donumber} .'('. $follow_up_vc .')';
 
 328   $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.SalesPurchase ckeditor/ckeditor ckeditor/adapters/jquery kivi.io autocomplete_customer autocomplete_part));
 
 331   push @custom_hidden, map { "shiptocvar_" . $_->name } @{ SL::DB::Manager::CustomVariableConfig->get_all(where => [ module => 'ShipTo' ]) };
 
 333   $::form->{HIDDENS} = [ map { +{ name => $_, value => $::form->{$_} } } (@custom_hidden) ];
 
 336   # Fix für Bug 1082 Erwartet wird: 'abteilungsNAME--abteilungsID'
 
 337   # und Erweiterung für Bug 1760:
 
 338   # Das war leider nur ein Teil-Fix, da das Verhalten den 'Erneuern'-Knopf
 
 339   # nicht überlebt. Konsequent jetzt auf L umgestellt
 
 340   #   $ perldoc SL::Template::Plugin::L
 
 341   # Daher entsprechend nur die Anpassung in form_header
 
 342   # und in DO.pm gemacht. 4 Testfälle:
 
 343   # department_id speichern                 | i.O.
 
 344   # department_id lesen                     | i.O.
 
 345   # department leer überlebt erneuern       | i.O.
 
 346   # department nicht leer überlebt erneuern | i.O.
 
 347   # $main::lxdebug->message(0, 'ABTEILUNGS ID in form?' . $form->{department_id});
 
 348   print $form->parse_html_template('do/form_header');
 
 350   $main::lxdebug->leave_sub();
 
 354   $main::lxdebug->enter_sub();
 
 358   my $form     = $main::form;
 
 360   $form->{PRINT_OPTIONS} = print_options('inline' => 1);
 
 361   $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
 
 363   print $form->parse_html_template('do/form_footer',
 
 364     {transfer_default         => ($::instance_conf->get_transfer_default)});
 
 366   $main::lxdebug->leave_sub();
 
 369 sub update_delivery_order {
 
 370   $main::lxdebug->enter_sub();
 
 374   my $form     = $main::form;
 
 375   my %myconfig = %main::myconfig;
 
 377   set_headings($form->{"id"} ? "edit" : "add");
 
 379   $form->{insertdate} = SL::DB::DeliveryOrder->new(id => $form->{id})->load->itime_as_date if $form->{id};
 
 384   $payment_id = $form->{payment_id} if $form->{payment_id};
 
 386   check_name($form->{vc});
 
 387   $form->{discount} =  $form->{"$form->{vc}_discount"} if defined $form->{"$form->{vc}_discount"};
 
 388   # Problem: Wenn man ohne Erneuern einen Kunden/Lieferanten
 
 389   # wechselt, wird der entsprechende Kunden/ Lieferantenrabatt
 
 390   # nicht übernommen. Grundproblem: In Commit 82574e78
 
 391   # hab ich aus discount customer_discount und vendor_discount
 
 392   # gemacht und entsprechend an den Oberflächen richtig hin-
 
 393   # geschoben. Die damals bessere Lösung wäre gewesen:
 
 394   # In den Templates nur die hidden für form-discount wieder ein-
 
 395   # setzen dann wäre die Verrenkung jetzt nicht notwendig.
 
 396   # TODO: Ggf. Bugfix 1284, 1575 und 817 wieder zusammenführen
 
 397   # Testfälle: Kunden mit Rabatt 0 -> Rabatt 20 i.O.
 
 398   #            Kunde mit Rabatt 20 -> Rabatt 0  i.O.
 
 399   #            Kunde mit Rabatt 20 -> Rabatt 5,5 i.O.
 
 400   $form->{payment_id} = $payment_id if $form->{payment_id} eq "";
 
 402   my $i = $form->{rowcount};
 
 404   if (   ($form->{"partnumber_$i"} eq "")
 
 405       && ($form->{"description_$i"} eq "")
 
 406       && ($form->{"partsgroup_$i"}  eq "")) {
 
 413     if ($form->{type} eq 'purchase_delivery_order') {
 
 414       IR->retrieve_item(\%myconfig, $form);
 
 417       IS->retrieve_item(\%myconfig, $form);
 
 421     my $rows = scalar @{ $form->{item_list} };
 
 424       $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
 425       if( !$form->{"qty_$i"} ) {
 
 426         $form->{"qty_$i"} = 1;
 
 431         select_item(mode => $mode, pre_entered_qty => $form->{"qty_$i"});
 
 432         $::dispatcher->end_request;
 
 436         my $sellprice = $form->parse_amount(\%myconfig, $form->{"sellprice_$i"});
 
 438         map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} } keys %{ $form->{item_list}[0] };
 
 440         $form->{"marge_price_factor_$i"} = $form->{item_list}->[0]->{price_factor};
 
 443           $form->{"sellprice_$i"} = $sellprice;
 
 445           my $record        = _make_record();
 
 446           my $price_source  = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
 
 447           my $best_price    = $price_source->best_price;
 
 448           my $best_discount = $price_source->best_discount;
 
 451             $::form->{"sellprice_$i"}           = $best_price->price;
 
 452             $::form->{"active_price_source_$i"} = $best_price->source;
 
 454           if ($best_discount) {
 
 455             $::form->{"discount_$i"}               = $best_discount->discount;
 
 456             $::form->{"active_discount_source_$i"} = $best_discount->source;
 
 460         $form->{"sellprice_$i"}          = $form->format_amount(\%myconfig, $form->{"sellprice_$i"});
 
 461         $form->{"lastcost_$i"}           = $form->format_amount(\%myconfig, $form->{"lastcost_$i"});
 
 462         $form->{"qty_$i"}                = $form->format_amount(\%myconfig, $form->{"qty_$i"});
 
 463         $form->{"discount_$i"}           = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0);
 
 470       # ok, so this is a new part
 
 471       # ask if it is a part or service item
 
 473       if (   $form->{"partsgroup_$i"}
 
 474           && ($form->{"partsnumber_$i"} eq "")
 
 475           && ($form->{"description_$i"} eq "")) {
 
 477         $form->{"discount_$i"} = "";
 
 478         $form->{"not_discountable_$i"} = "";
 
 482         $form->{"id_$i"}   = 0;
 
 488   $main::lxdebug->leave_sub();
 
 492   $main::lxdebug->enter_sub();
 
 496   my $form     = $main::form;
 
 497   my %myconfig = %main::myconfig;
 
 498   my $locale   = $main::locale;
 
 500   $form->{vc} = $form->{type} eq 'purchase_delivery_order' ? 'vendor' : 'customer';
 
 502   $form->get_lists("projects"       => { "key" => "ALL_PROJECTS",
 
 504                    "$form->{vc}s"   => "ALL_VC",
 
 505                    "business_types" => "ALL_BUSINESS_TYPES");
 
 506   $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]);
 
 507   $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all;
 
 508   $form->{SHOW_VC_DROP_DOWN} =  $myconfig{vclimit} > scalar @{ $form->{ALL_VC} };
 
 509   $form->{title}             = $locale->text('Delivery Orders');
 
 513   print $form->parse_html_template('do/search');
 
 515   $main::lxdebug->leave_sub();
 
 519   $main::lxdebug->enter_sub();
 
 523   my $form     = $main::form;
 
 524   my %myconfig = %main::myconfig;
 
 525   my $locale   = $main::locale;
 
 526   my $cgi      = $::request->{cgi};
 
 528   ($form->{ $form->{vc} }, $form->{"$form->{vc}_id"}) = split(/--/, $form->{ $form->{vc} });
 
 530   report_generator_set_default_sort('transdate', 1);
 
 534   $form->{rowcount} = scalar @{ $form->{DO} };
 
 537     ids                     transdate               reqdate
 
 539     ordnumber               customernumber          cusordnumber
 
 540     name                    employee  salesman
 
 541     shipvia                 globalprojectnumber
 
 542     transaction_description department
 
 547   $form->{l_open}      = $form->{l_closed} = "Y" if ($form->{open}      && $form->{closed});
 
 548   $form->{l_delivered} = "Y"                     if ($form->{delivered} && $form->{notdelivered});
 
 550   $form->{title}       = $locale->text('Delivery Orders');
 
 552   my $attachment_basename = $form->{vc} eq 'vendor' ? $locale->text('purchase_delivery_order_list') : $locale->text('sales_delivery_order_list');
 
 554   my $report = SL::ReportGenerator->new(\%myconfig, $form);
 
 556   my @hidden_variables = map { "l_${_}" } @columns;
 
 557   push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber serialnumber cusordnumber
 
 558                                           transaction_description transdatefrom transdateto reqdatefrom reqdateto
 
 559                                           type vc employee_id salesman_id project_id parts_partnumber parts_description
 
 560                                           insertdatefrom insertdateto business_id);
 
 562   my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
 
 565     'ids'                     => { 'text' => '', },
 
 566     'transdate'               => { 'text' => $locale->text('Delivery Order Date'), },
 
 567     'reqdate'                 => { 'text' => $locale->text('Reqdate'), },
 
 568     'id'                      => { 'text' => $locale->text('ID'), },
 
 569     'donumber'                => { 'text' => $locale->text('Delivery Order'), },
 
 570     'ordnumber'               => { 'text' => $locale->text('Order'), },
 
 571     'customernumber'          => { 'text' => $locale->text('Customer Number'), },
 
 572     'cusordnumber'            => { 'text' => $locale->text('Customer Order Number'), },
 
 573     'name'                    => { 'text' => $form->{vc} eq 'customer' ? $locale->text('Customer') : $locale->text('Vendor'), },
 
 574     'employee'                => { 'text' => $locale->text('Employee'), },
 
 575     'salesman'                => { 'text' => $locale->text('Salesman'), },
 
 576     'shipvia'                 => { 'text' => $locale->text('Ship via'), },
 
 577     'globalprojectnumber'     => { 'text' => $locale->text('Project Number'), },
 
 578     'transaction_description' => { 'text' => $locale->text('Transaction description'), },
 
 579     'open'                    => { 'text' => $locale->text('Open'), },
 
 580     'delivered'               => { 'text' => $locale->text('Delivered'), },
 
 581     'department'              => { 'text' => $locale->text('Department'), },
 
 582     'insertdate'              => { 'text' => $locale->text('Insert Date'), },
 
 585   foreach my $name (qw(id transdate reqdate donumber ordnumber name employee salesman shipvia transaction_description department insertdate)) {
 
 586     my $sortdir                 = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
 
 587     $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
 
 590   $form->{"l_type"} = "Y";
 
 591   map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
 
 593   $column_defs{ids}->{visible} = 'HTML';
 
 595   $report->set_columns(%column_defs);
 
 596   $report->set_column_order(@columns);
 
 598   $report->set_export_options('orders', @hidden_variables, qw(sort sortdir));
 
 600   $report->set_sort_indicator($form->{sort}, $form->{sortdir});
 
 603   if ($form->{customer}) {
 
 604     push @options, $locale->text('Customer') . " : $form->{customer}";
 
 606   if ($form->{vendor}) {
 
 607     push @options, $locale->text('Vendor') . " : $form->{vendor}";
 
 609   if ($form->{cp_name}) {
 
 610     push @options, $locale->text('Contact Person') . " : $form->{cp_name}";
 
 612   if ($form->{department_id}) {
 
 613     push @options, $locale->text('Department') . " : " . SL::DB::Department->new(id => $form->{department_id})->load->description;
 
 615   if ($form->{donumber}) {
 
 616     push @options, $locale->text('Delivery Order Number') . " : $form->{donumber}";
 
 618   if ($form->{ordnumber}) {
 
 619     push @options, $locale->text('Order Number') . " : $form->{ordnumber}";
 
 621   push @options, $locale->text('Serial Number') . " : $form->{serialnumber}" if $form->{serialnumber};
 
 622   if ($form->{business_id}) {
 
 623     my $vc_type_label = $form->{vc} eq 'customer' ? $locale->text('Customer type') : $locale->text('Vendor type');
 
 624     push @options, $vc_type_label . " : " . SL::DB::Business->new(id => $form->{business_id})->load->description;
 
 626   if ($form->{transaction_description}) {
 
 627     push @options, $locale->text('Transaction description') . " : $form->{transaction_description}";
 
 629   if ($form->{parts_description}) {
 
 630     push @options, $locale->text('Part Description') . " : $form->{parts_description}";
 
 632   if ($form->{parts_partnumber}) {
 
 633     push @options, $locale->text('Part Number') . " : $form->{parts_partnumber}";
 
 635   if ( $form->{transdatefrom} or $form->{transdateto} ) {
 
 636     push @options, $locale->text('Delivery Order Date');
 
 637     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1)     if $form->{transdatefrom};
 
 638     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{transdateto},   1)     if $form->{transdateto};
 
 640   if ( $form->{reqdatefrom} or $form->{reqdateto} ) {
 
 641     push @options, $locale->text('Reqdate');
 
 642     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1)       if $form->{reqdatefrom};
 
 643     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{reqdateto},   1)       if $form->{reqdateto};
 
 645   if ( $form->{insertdatefrom} or $form->{insertdateto} ) {
 
 646     push @options, $locale->text('Insert Date');
 
 647     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1)    if $form->{insertdatefrom};
 
 648     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{insertdateto},   1)    if $form->{insertdateto};
 
 651     push @options, $locale->text('Open');
 
 653   if ($form->{closed}) {
 
 654     push @options, $locale->text('Closed');
 
 656   if ($form->{delivered}) {
 
 657     push @options, $locale->text('Delivered');
 
 659   if ($form->{notdelivered}) {
 
 660     push @options, $locale->text('Not delivered');
 
 663   $report->set_options('top_info_text'        => join("\n", @options),
 
 664                        'raw_top_info_text'    => $form->parse_html_template('do/orders_top'),
 
 665                        'raw_bottom_info_text' => $form->parse_html_template('do/orders_bottom'),
 
 666                        'output_format'        => 'HTML',
 
 667                        'title'                => $form->{title},
 
 668                        'attachment_basename'  => $attachment_basename . strftime('_%Y%m%d', localtime time),
 
 670   $report->set_options_from_form();
 
 671   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
 
 673   # add sort and escape callback, this one we use for the add sub
 
 674   $form->{callback} = $href .= "&sort=$form->{sort}";
 
 676   # escape callback for href
 
 677   my $callback = $form->escape($href);
 
 679   my $edit_url       = build_std_url('action=edit', 'type', 'vc');
 
 680   my $edit_order_url = build_std_url('script=oe.pl', 'type=' . ($form->{type} eq 'sales_delivery_order' ? 'sales_order' : 'purchase_order'), 'action=edit');
 
 684   foreach my $dord (@{ $form->{DO} }) {
 
 685     $dord->{open}      = $dord->{closed}    ? $locale->text('No')  : $locale->text('Yes');
 
 686     $dord->{delivered} = $dord->{delivered} ? $locale->text('Yes') : $locale->text('No');
 
 688     my $row = { map { $_ => { 'data' => $dord->{$_} } } @columns };
 
 691       'raw_data' =>   $cgi->hidden('-name' => "trans_id_${idx}", '-value' => $dord->{id})
 
 692                     . $cgi->checkbox('-name' => "multi_id_${idx}", '-value' => 1, '-label' => ''),
 
 693       'valign'   => 'center',
 
 697     $row->{donumber}->{link}  = $edit_url       . "&id=" . E($dord->{id})      . "&callback=${callback}";
 
 698     $row->{ordnumber}->{link} = $edit_order_url . "&id=" . E($dord->{oe_id})   . "&callback=${callback}" if $dord->{oe_id};
 
 699     $report->add_data($row);
 
 704   $report->generate_with_headers();
 
 706   $main::lxdebug->leave_sub();
 
 710   $main::lxdebug->enter_sub();
 
 716   my $form     = $main::form;
 
 717   my %myconfig = %main::myconfig;
 
 718   my $locale   = $main::locale;
 
 720   $form->mtime_ischanged('delivery_orders');
 
 722   $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
 
 724   $form->isblank("transdate", $locale->text('Delivery Order Date missing!'));
 
 726   $form->{donumber} =~ s/^\s*//g;
 
 727   $form->{donumber} =~ s/\s*$//g;
 
 729   my $msg = ucfirst $form->{vc};
 
 730   $form->isblank($form->{vc}, $locale->text($msg . " missing!"));
 
 732   # $locale->text('Customer missing!');
 
 733   # $locale->text('Vendor missing!');
 
 735   remove_emptied_rows();
 
 738   # if the name changed get new values
 
 739   if (check_name($form->{vc})) {
 
 741     $::dispatcher->end_request;
 
 744   $form->{id} = 0 if $form->{saveasnew};
 
 748   if(!exists $form->{addition}) {
 
 749     $form->{snumbers} = qq|donumber_| . $form->{donumber};
 
 750     $form->{addition} = "SAVED";
 
 753   # /saving the history
 
 755   $form->{simple_save} = 1;
 
 756   if (!$params{no_redirect} && !$form->{print_and_save}) {
 
 757     delete @{$form}{ary_diff([keys %{ $form }], [qw(login id script type cursor_fokus)])};
 
 759     $::dispatcher->end_request;
 
 761   $main::lxdebug->leave_sub();
 
 765   $main::lxdebug->enter_sub();
 
 769   my $form     = $main::form;
 
 770   my %myconfig = %main::myconfig;
 
 771   my $locale   = $main::locale;
 
 775     if(!exists $form->{addition}) {
 
 776       $form->{snumbers} = qq|donumber_| . $form->{donumber};
 
 777       $form->{addition} = "DELETED";
 
 780     # /saving the history
 
 782     $form->info($locale->text('Delivery Order deleted!'));
 
 783     $::dispatcher->end_request;
 
 786   $form->error($locale->text('Cannot delete delivery order!'));
 
 788   $main::lxdebug->leave_sub();
 
 792   $main::lxdebug->enter_sub();
 
 794   my $form     = $main::form;
 
 795   my %myconfig = %main::myconfig;
 
 796   my $locale   = $main::locale;
 
 799   $form->mtime_ischanged('delivery_orders');
 
 801   $main::auth->assert($form->{type} eq 'purchase_delivery_order' ? 'vendor_invoice_edit' : 'invoice_edit');
 
 803   $form->{convert_from_do_ids} = $form->{id};
 
 804   $form->{deliverydate}        = $form->{transdate};
 
 805   $form->{transdate}           = $form->{invdate} = $form->current_date(\%myconfig);
 
 806   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
 
 807   $form->{defaultcurrency}     = $form->get_default_currency(\%myconfig);
 
 811   delete @{$form}{qw(id closed delivered)};
 
 813   my ($script, $buysell);
 
 814   if ($form->{type} eq 'purchase_delivery_order') {
 
 815     $form->{title}  = $locale->text('Add Vendor Invoice');
 
 816     $form->{script} = 'ir.pl';
 
 821     $form->{title}  = $locale->text('Add Sales Invoice');
 
 822     $form->{script} = 'is.pl';
 
 827   for my $i (1 .. $form->{rowcount}) {
 
 828     map { $form->{"${_}_${i}"} = $form->parse_amount(\%myconfig, $form->{"${_}_${i}"}) if $form->{"${_}_${i}"} } qw(ship qty sellprice lastcost basefactor discount);
 
 830     # adds a customer/vendor discount, unless we have a workflow case
 
 831     # CAVEAT: has to be done, after the above parse_amount
 
 832     unless ($form->{"ordnumber"}) {
 
 833       if ($form->{discount}) { # Falls wir einen Lieferanten-/Kundenrabatt haben
 
 834         # und rabattfähig sind, dann
 
 835         unless ($form->{"not_discountable_$i"}) {
 
 836           $form->{"discount_$i"} = $form->{discount}*100; # ... nehmen wir diesen Rabatt
 
 840     $form->{"donumber_$i"} = $form->{donumber};
 
 841     $form->{"converted_from_delivery_order_items_id_$i"} = delete $form->{"delivery_order_items_id_$i"};
 
 844   $form->{type} = "invoice";
 
 847   $main::locale = Locale->new("$myconfig{countrycode}", "$script");
 
 848   $locale = $main::locale;
 
 850   require "bin/mozilla/$form->{script}";
 
 852   my $currency = $form->{currency};
 
 855   if ($form->{ordnumber}) {
 
 856     require SL::DB::Order;
 
 857     if (my $order = SL::DB::Manager::Order->find_by(ordnumber => $form->{ordnumber})) {
 
 859       $form->{orddate} = $order->transdate_as_date;
 
 860       $form->{$_}      = $order->$_ for qw(payment_id salesman_id taxzone_id quonumber);
 
 864   $form->{currency}     = $currency;
 
 865   $form->{exchangerate} = "";
 
 866   $form->{forex}        = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, $buysell);
 
 867   $form->{exchangerate} = $form->{forex} if ($form->{forex});
 
 872   for my $i (1 .. $form->{rowcount}) {
 
 873     $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
 
 875     my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
 
 877     my $decimalplaces = ($dec > 2) ? $dec : 2;
 
 879     # copy delivery date from reqdate for order -> invoice conversion
 
 880     $form->{"deliverydate_$i"} = $form->{"reqdate_$i"}
 
 881       unless $form->{"deliverydate_$i"};
 
 884     $form->{"sellprice_$i"} =
 
 885       $form->format_amount(\%myconfig, $form->{"sellprice_$i"},
 
 888     $form->{"lastcost_$i"} =
 
 889       $form->format_amount(\%myconfig, $form->{"lastcost_$i"},
 
 892     (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
 
 893     $dec_qty = length $dec_qty;
 
 895       $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
 
 901   $main::lxdebug->leave_sub();
 
 905   $main::lxdebug->enter_sub();
 
 907   my $form     = $main::form;
 
 908   my %myconfig = %main::myconfig;
 
 909   my $locale   = $main::locale;
 
 912   $main::auth->assert($form->{type} eq 'sales_delivery_order' ? 'invoice_edit' : 'vendor_invoice_edit');
 
 914   my @do_ids = map { $form->{"trans_id_$_"} } grep { $form->{"multi_id_$_"} } (1..$form->{rowcount});
 
 916   if (!scalar @do_ids) {
 
 917     $form->show_generic_error($locale->text('You have not selected any delivery order.'));
 
 920   map { delete $form->{$_} } grep { m/^(?:trans|multi)_id_\d+/ } keys %{ $form };
 
 922   if (!DO->retrieve('vc' => $form->{vc}, 'ids' => \@do_ids)) {
 
 923     $form->show_generic_error($form->{vc} eq 'customer' ?
 
 924                               $locale->text('You cannot create an invoice for delivery orders for different customers.') :
 
 925                               $locale->text('You cannot create an invoice for delivery orders from different vendors.'),
 
 929   my $source_type              = $form->{type};
 
 930   $form->{convert_from_do_ids} = join ' ', @do_ids;
 
 931   # bei der auswahl von mehreren Lieferscheinen fuer eine Rechnung, die einfach in donumber_array
 
 932   # zwischenspeichern (DO.pm) und als ' '-separierte Liste wieder zurueckschreiben
 
 933   # Hinweis: delete gibt den wert zurueck und loescht danach das element (nett und einfach)
 
 934   # $shell: perldoc perlunc; /delete EXPR
 
 935   $form->{donumber}            = delete $form->{donumber_array};
 
 936   $form->{ordnumber}           = delete $form->{ordnumber_array};
 
 937   $form->{cusordnumber}        = delete $form->{cusordnumber_array};
 
 938   $form->{deliverydate}        = $form->{transdate};
 
 939   $form->{transdate}           = $form->current_date(\%myconfig);
 
 940   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
 
 941   $form->{type}                = "invoice";
 
 943   $form->{defaultcurrency}     = $form->get_default_currency(\%myconfig);
 
 945   my ($script, $buysell);
 
 946   if ($source_type eq 'purchase_delivery_order') {
 
 947     $form->{title}  = $locale->text('Add Vendor Invoice');
 
 948     $form->{script} = 'ir.pl';
 
 953     $form->{title}  = $locale->text('Add Sales Invoice');
 
 954     $form->{script} = 'is.pl';
 
 959   map { delete $form->{$_} } qw(id subject message cc bcc printed emailed queued);
 
 961   # get vendor or customer discount
 
 963   my $saved_form = save_form();
 
 964   if ($form->{vc} eq 'vendor') {
 
 965     IR->get_vendor(\%myconfig, \%$form);
 
 966     $vc_discount = $form->{vendor_discount};
 
 968     IS->get_customer(\%myconfig, \%$form);
 
 969     $vc_discount = $form->{customer_discount};
 
 971   # use payment terms from customer or vendor
 
 972   restore_form($saved_form,0,qw(payment_id));
 
 974   $form->{rowcount} = 0;
 
 975   foreach my $ref (@{ $form->{form_details} }) {
 
 977     $ref->{reqdate} ||= $ref->{dord_transdate}; # copy transdates into each invoice row
 
 978     map { $form->{"${_}_$form->{rowcount}"} = $ref->{$_} } keys %{ $ref };
 
 979     map { $form->{"${_}_$form->{rowcount}"} = $form->format_amount(\%myconfig, $ref->{$_}) } qw(qty sellprice lastcost);
 
 980     $form->{"converted_from_delivery_order_items_id_$form->{rowcount}"} = delete $form->{"delivery_order_items_id_$form->{rowcount}"};
 
 982     if ($vc_discount){ # falls wir einen Lieferanten/Kundenrabatt haben
 
 983       # und keinen anderen discount wert an $i ...
 
 984       $form->{"discount_$form->{rowcount}"} ||= $vc_discount; # ... nehmen wir diesen Rabatt
 
 987     $form->{"discount_$form->{rowcount}"}   = $form->{"discount_$form->{rowcount}"}  * 100; #s.a. Bug 1151
 
 988     # Anm.: Eine Änderung des discounts in der SL/DO.pm->retrieve (select (doi.discount * 100) as discount) ergibt in psql einen
 
 989     # Wert von 10.0000001490116. Ferner ist der Rabatt in der Rechnung dann bei 1.0 (?). Deswegen lasse ich das hier. jb 10.10.09
 
 991     $form->{"discount_$form->{rowcount}"} = $form->format_amount(\%myconfig, $form->{"discount_$form->{rowcount}"});
 
 993   delete $form->{form_details};
 
 995   $locale = Locale->new("$myconfig{countrycode}", "$script");
 
 997   require "bin/mozilla/$form->{script}";
 
1004   $main::lxdebug->leave_sub();
 
1008   $main::lxdebug->enter_sub();
 
1012   my $form     = $main::form;
 
1014   $form->{saveasnew} = 1;
 
1015   $form->{closed}    = 0;
 
1016   $form->{delivered} = 0;
 
1017   map { delete $form->{$_} } qw(printed emailed queued);
 
1018   delete @{ $form }{ grep { m/^stock_(?:in|out)_\d+/ } keys %{ $form } };
 
1019   $form->{"converted_from_delivery_order_items_id_$_"} = delete $form->{"delivery_order_items_id_$_"} for 1 .. $form->{"rowcount"};
 
1020   # Let kivitendo assign a new order number if the user hasn't changed the
 
1021   # previous one. If it has been changed manually then use it as-is.
 
1022   $form->{donumber} =~ s/^\s*//g;
 
1023   $form->{donumber} =~ s/\s*$//g;
 
1024   if ($form->{saved_donumber} && ($form->{saved_donumber} eq $form->{donumber})) {
 
1025     delete($form->{donumber});
 
1030   $main::lxdebug->leave_sub();
 
1034   $main::lxdebug->enter_sub();
 
1038   $::form->mtime_ischanged('delivery_orders','mail');
 
1040   $::form->{print_and_save} = 1;
 
1042   my $saved_form = save_form();
 
1046   restore_form($saved_form, 0, qw(id ordnumber quonumber));
 
1050   $main::lxdebug->leave_sub();
 
1053 sub calculate_stock_in_out {
 
1054   $main::lxdebug->enter_sub();
 
1056   my $form     = $main::form;
 
1060   if (!$form->{"id_${i}"}) {
 
1061     $main::lxdebug->leave_sub();
 
1065   my $all_units = AM->retrieve_all_units();
 
1067   my $in_out   = $form->{type} =~ /^sales/ ? 'out' : 'in';
 
1068   my $sinfo    = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_${i}"});
 
1070   my $do_qty   = AM->sum_with_unit($::form->{"qty_$i"}, $::form->{"unit_$i"});
 
1071   my $sum      = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $sinfo });
 
1072   my $matches  = $do_qty == $sum;
 
1074   my $content  = $form->format_amount_units('amount'      => $sum * 1,
 
1075                                             'part_unit'   => $form->{"partunit_$i"},
 
1076                                             'amount_unit' => $all_units->{$form->{"partunit_$i"}}->{base_unit},
 
1077                                             'conv_units'  => 'convertible_not_smaller',
 
1079   $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="?">|;
 
1081   $main::lxdebug->leave_sub();
 
1086 sub get_basic_bin_wh_info {
 
1087   $main::lxdebug->enter_sub();
 
1089   my $stock_info = shift;
 
1091   my $form     = $main::form;
 
1093   foreach my $sinfo (@{ $stock_info }) {
 
1094     next unless ($sinfo->{bin_id});
 
1096     my $bin_info = WH->get_basic_bin_info('id' => $sinfo->{bin_id});
 
1097     map { $sinfo->{"${_}_description"} = $sinfo->{"${_}description"} = $bin_info->{"${_}_description"} } qw(bin warehouse);
 
1100   $main::lxdebug->leave_sub();
 
1103 sub stock_in_out_form {
 
1104   $main::lxdebug->enter_sub();
 
1106   my $form     = $main::form;
 
1108   if ($form->{in_out} eq 'out') {
 
1114   $main::lxdebug->leave_sub();
 
1117 sub redo_stock_info {
 
1118   $main::lxdebug->enter_sub();
 
1122   my $form     = $main::form;
 
1124   my @non_empty = grep { $_->{qty} } @{ $params{stock_info} };
 
1126   if ($params{add_empty_row}) {
 
1128       'warehouse_id' => scalar(@non_empty) ? $non_empty[-1]->{warehouse_id} : undef,
 
1129       'bin_id'       => scalar(@non_empty) ? $non_empty[-1]->{bin_id}       : undef,
 
1133   @{ $params{stock_info} } = @non_empty;
 
1135   $main::lxdebug->leave_sub();
 
1138 sub update_stock_in {
 
1139   $main::lxdebug->enter_sub();
 
1141   my $form     = $main::form;
 
1142   my %myconfig = %main::myconfig;
 
1144   my $stock_info = [];
 
1146   foreach my $i (1..$form->{rowcount}) {
 
1147     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1148     push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(warehouse_id bin_id chargenumber
 
1149                                                                    bestbefore qty unit delivery_order_items_stock_id) };
 
1152   display_stock_in_form($stock_info);
 
1154   $main::lxdebug->leave_sub();
 
1158   $main::lxdebug->enter_sub();
 
1160   my $form     = $main::form;
 
1162   my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
 
1164   display_stock_in_form($stock_info);
 
1166   $main::lxdebug->leave_sub();
 
1169 sub display_stock_in_form {
 
1170   $main::lxdebug->enter_sub();
 
1172   my $stock_info = shift;
 
1174   my $form     = $main::form;
 
1175   my %myconfig = %main::myconfig;
 
1176   my $locale   = $main::locale;
 
1178   $form->{title} = $locale->text('Stock');
 
1180   my $part_info  = IC->get_basic_part_info('id' => $form->{parts_id});
 
1182   # Standardlagerplatz für Standard-Auslagern verwenden, falls keiner für die Ware explizit definiert wurde
 
1183   if ($::instance_conf->get_transfer_default_use_master_default_bin) {
 
1184     $part_info->{warehouse_id} ||= $::instance_conf->get_warehouse_id;
 
1185     $part_info->{bin_id}       ||= $::instance_conf->get_bin_id;
 
1188   my $units      = AM->retrieve_units(\%myconfig, $form);
 
1189   # der zweite Parameter von unit_select_data gibt den default-Namen (selected) vor
 
1190   my $units_data = AM->unit_select_data($units, $form->{do_unit}, undef, $part_info->{unit});
 
1192   $form->get_lists('warehouses' => { 'key'    => 'WAREHOUSES',
 
1193                                      'bins'   => 'BINS' });
 
1195   redo_stock_info('stock_info' => $stock_info, 'add_empty_row' => !$form->{delivered});
 
1197   get_basic_bin_wh_info($stock_info);
 
1199   $form->header(no_layout => 1);
 
1200   print $form->parse_html_template('do/stock_in_form', { 'UNITS'      => $units_data,
 
1201                                                          'STOCK_INFO' => $stock_info,
 
1202                                                          'PART_INFO'  => $part_info, });
 
1204   $main::lxdebug->leave_sub();
 
1207 sub _stock_in_out_set_qty_display {
 
1208   my $stock_info       = shift;
 
1210   my $all_units        = AM->retrieve_all_units();
 
1211   my $sum              = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1212   $form->{qty_display} = $form->format_amount_units(amount      => $sum * 1,
 
1213                                                     part_unit   => $form->{partunit},
 
1214                                                     amount_unit => $all_units->{ $form->{partunit} }->{base_unit},
 
1215                                                     conv_units  => 'convertible_not_smaller',
 
1220   $main::lxdebug->enter_sub();
 
1222   my $form     = $main::form;
 
1223   my %myconfig = %main::myconfig;
 
1225   my $stock_info = [];
 
1227   foreach my $i (1..$form->{rowcount}) {
 
1228     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1230     next if ($form->{"qty_$i"} <= 0);
 
1232     push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(delivery_order_items_stock_id warehouse_id bin_id chargenumber bestbefore qty unit) };
 
1235   $form->{stock} = YAML::Dump($stock_info);
 
1237   _stock_in_out_set_qty_display($stock_info);
 
1239   my $do_qty       = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
 
1240   my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1243   print $form->parse_html_template('do/set_stock_in_out', {
 
1244     qty_matches => $do_qty == $transfer_qty,
 
1247   $main::lxdebug->leave_sub();
 
1250 sub stock_out_form {
 
1251   $main::lxdebug->enter_sub();
 
1253   my $form     = $main::form;
 
1254   my %myconfig = %main::myconfig;
 
1255   my $locale   = $main::locale;
 
1257   $form->{title} = $locale->text('Release From Stock');
 
1259   my $part_info  = IC->get_basic_part_info('id' => $form->{parts_id});
 
1261   my $units      = AM->retrieve_units(\%myconfig, $form);
 
1262   my $units_data = AM->unit_select_data($units, undef, undef, $part_info->{unit});
 
1264   my @contents   = DO->get_item_availability('parts_id' => $form->{parts_id});
 
1266   my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
 
1268   if (!$form->{delivered}) {
 
1269     foreach my $row (@contents) {
 
1270       $row->{available_qty} = $form->format_amount_units('amount'      => $row->{qty} * 1,
 
1271                                                          'part_unit'   => $part_info->{unit},
 
1272                                                          'conv_units'  => 'convertible_not_smaller',
 
1275       foreach my $sinfo (@{ $stock_info }) {
 
1276         next if (($row->{bin_id}       != $sinfo->{bin_id}) ||
 
1277                  ($row->{warehouse_id} != $sinfo->{warehouse_id}) ||
 
1278                  ($row->{chargenumber} ne $sinfo->{chargenumber}) ||
 
1279                  ($row->{bestbefore}   ne $sinfo->{bestbefore}));
 
1281         map { $row->{"stock_$_"} = $sinfo->{$_} } qw(qty unit error delivery_order_items_stock_id);
 
1286     get_basic_bin_wh_info($stock_info);
 
1288     foreach my $sinfo (@{ $stock_info }) {
 
1289       map { $sinfo->{"stock_$_"} = $sinfo->{$_} } qw(qty unit);
 
1293   $form->header(no_layout => 1);
 
1294   print $form->parse_html_template('do/stock_out_form', { 'UNITS'      => $units_data,
 
1295                                                           'WHCONTENTS' => $form->{delivered} ? $stock_info : \@contents,
 
1296                                                           'PART_INFO'  => $part_info, });
 
1298   $main::lxdebug->leave_sub();
 
1302   $main::lxdebug->enter_sub();
 
1304   my $form     = $main::form;
 
1305   my %myconfig = %main::myconfig;
 
1306   my $locale   = $main::locale;
 
1308   my $stock_info = [];
 
1310   foreach my $i (1 .. $form->{rowcount}) {
 
1311     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
 
1313     next if ($form->{"qty_$i"} <= 0);
 
1315     push @{ $stock_info }, {
 
1316       'warehouse_id' => $form->{"warehouse_id_$i"},
 
1317       'bin_id'       => $form->{"bin_id_$i"},
 
1318       'chargenumber' => $form->{"chargenumber_$i"},
 
1319       'bestbefore'   => $form->{"bestbefore_$i"},
 
1320       'qty'          => $form->{"qty_$i"},
 
1321       'unit'         => $form->{"unit_$i"},
 
1323       'delivery_order_items_stock_id'  => $form->{"delivery_order_items_stock_id_$i"},
 
1327   my @errors     = DO->check_stock_availability('requests' => $stock_info,
 
1328                                                 'parts_id' => $form->{parts_id});
 
1330   $form->{stock} = YAML::Dump($stock_info);
 
1333     $form->{ERRORS} = [];
 
1334     map { push @{ $form->{ERRORS} }, $locale->text('Error in row #1: The quantity you entered is bigger than the stocked quantity.', $_->{row}); } @errors;
 
1335     stock_in_out_form();
 
1338     _stock_in_out_set_qty_display($stock_info);
 
1340     my $do_qty       = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
 
1341     my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
 
1344     print $form->parse_html_template('do/set_stock_in_out', {
 
1345       qty_matches => $do_qty == $transfer_qty,
 
1349   $main::lxdebug->leave_sub();
 
1353   $main::lxdebug->enter_sub();
 
1355   my $form     = $main::form;
 
1356   my %myconfig = %main::myconfig;
 
1357   my $locale   = $main::locale;
 
1359   if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
 
1360     $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred in.'));
 
1363   save(no_redirect => 1);
 
1365   my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_in_${_}"} } (1 .. $form->{rowcount});
 
1369     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1370     my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1373     $form->{ERRORS}   = [];
 
1375     foreach my $i (1 .. $form->{rowcount}) {
 
1376       next unless ($form->{"id_$i"} && $form->{"stock_in_$i"});
 
1378       my $row_sum_base_qty = 0;
 
1379       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1381       foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_in_$i"}) }) {
 
1382         $request->{parts_id}  = $form->{"id_$i"};
 
1383         $row_sum_base_qty    += $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
 
1385         $request->{project_id} = $form->{"project_id_$i"} || $form->{globalproject_id};
 
1387         push @all_requests, $request;
 
1390       next if (0 == $row_sum_base_qty);
 
1392       my $do_base_qty = $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1394 #      if ($do_base_qty != $row_sum_base_qty) {
 
1395 #        push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no stock at all or the full quantity of #2 #3.',
 
1396 #                                                 $i, $form->{"qty_$i"}, $form->{"unit_$i"});
 
1400     if (@{ $form->{ERRORS} }) {
 
1401       push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
 
1403       set_headings('edit');
 
1405       $main::lxdebug->leave_sub();
 
1407       $::dispatcher->end_request;
 
1411   DO->transfer_in_out('direction' => 'in',
 
1412                       'requests'  => \@all_requests);
 
1414   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1416   $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id});
 
1419   $main::lxdebug->leave_sub();
 
1423   $main::lxdebug->enter_sub();
 
1425   my $form     = $main::form;
 
1426   my %myconfig = %main::myconfig;
 
1427   my $locale   = $main::locale;
 
1429   if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
 
1430     $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred out.'));
 
1433   save(no_redirect => 1);
 
1435   my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_out_${_}"} } (1 .. $form->{rowcount});
 
1439     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1440     my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1443     $form->{ERRORS}   = [];
 
1445     foreach my $i (1 .. $form->{rowcount}) {
 
1446       next unless ($form->{"id_$i"} && $form->{"stock_out_$i"});
 
1448       my $row_sum_base_qty = 0;
 
1449       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1451       foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_out_$i"}) }) {
 
1452         $request->{parts_id} = $form->{"id_$i"};
 
1453         $request->{base_qty} = $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
 
1454         $request->{project_id} = $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id};
 
1456         my $map_key          = join '--', ($form->{"id_$i"}, @{$request}{qw(warehouse_id bin_id chargenumber bestbefore)});
 
1458         $request_map{$map_key}                 ||= $request;
 
1459         $request_map{$map_key}->{sum_base_qty} ||= 0;
 
1460         $request_map{$map_key}->{sum_base_qty}  += $request->{base_qty};
 
1461         $row_sum_base_qty                       += $request->{base_qty};
 
1463         push @all_requests, $request;
 
1466       next if (0 == $row_sum_base_qty);
 
1468       my $do_base_qty = $form->{"qty_$i"} * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1470 #      if ($do_base_qty != $row_sum_base_qty) {
 
1471 #        push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no transfer at all or the full quantity of #2 #3.',
 
1472 #                                                 $i, $form->{"qty_$i"}, $form->{"unit_$i"});
 
1477       my @bin_ids      = map { $_->{bin_id} } values %request_map;
 
1478       my %bin_info_map = WH->get_basic_bin_info('id' => \@bin_ids);
 
1479       my @contents     = DO->get_item_availability('parts_id' => \@part_ids);
 
1481       foreach my $inv (@contents) {
 
1482         my $map_key = join '--', @{$inv}{qw(parts_id warehouse_id bin_id chargenumber bestbefore)};
 
1484         next unless ($request_map{$map_key});
 
1486         my $request    = $request_map{$map_key};
 
1487         $request->{ok} = $request->{sum_base_qty} <= $inv->{qty};
 
1490       foreach my $request (values %request_map) {
 
1491         next if ($request->{ok});
 
1493         my $pinfo = $part_info_map{$request->{parts_id}};
 
1494         my $binfo = $bin_info_map{$request->{bin_id}};
 
1496         if ($::instance_conf->get_show_bestbefore) {
 
1497             push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, #5, for the transfer of #6.",
 
1498                                                      $pinfo->{description},
 
1499                                                      $binfo->{warehouse_description},
 
1500                                                      $binfo->{bin_description},
 
1501                                                      $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
 
1502                                                      $request->{bestbefore} ? $locale->text('bestbefore #1', $request->{bestbefore}) : $locale->text('no bestbefore'),
 
1503                                                      $form->format_amount_units('amount'      => $request->{sum_base_qty},
 
1504                                                                                 'part_unit'   => $pinfo->{unit},
 
1505                                                                                 'conv_units'  => 'convertible_not_smaller'));
 
1507             push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, for the transfer of #5.",
 
1508                                                      $pinfo->{description},
 
1509                                                      $binfo->{warehouse_description},
 
1510                                                      $binfo->{bin_description},
 
1511                                                      $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
 
1512                                                      $form->format_amount_units('amount'      => $request->{sum_base_qty},
 
1513                                                                                 'part_unit'   => $pinfo->{unit},
 
1514                                                                                 'conv_units'  => 'convertible_not_smaller'));
 
1519     if (@{ $form->{ERRORS} }) {
 
1520       push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
 
1522       set_headings('edit');
 
1524       $main::lxdebug->leave_sub();
 
1526       $::dispatcher->end_request;
 
1529   DO->transfer_in_out('direction' => 'out',
 
1530                       'requests'  => \@all_requests);
 
1532   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1534   $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id});
 
1537   $main::lxdebug->leave_sub();
 
1541   $main::lxdebug->enter_sub();
 
1543   my $form     = $main::form;
 
1545   DO->close_orders('ids' => [ $form->{id} ]);
 
1547   $form->{closed} = 1;
 
1551   $main::lxdebug->leave_sub();
 
1555   $::lxdebug->enter_sub;
 
1557   $::auth->assert('purchase_delivery_order_edit | sales_delivery_order_edit');
 
1560   retrieve_partunits();
 
1562   my $new_rowcount = $::form->{"rowcount"} * 1 + 1;
 
1563   $::form->{"project_id_${new_rowcount}"} = $::form->{"globalproject_id"};
 
1565   $::form->language_payment(\%::myconfig);
 
1567   Common::webdav_folder($::form);
 
1570   display_row(++$::form->{rowcount});
 
1573   $::lxdebug->leave_sub;
 
1577   call_sub($main::form->{yes_nextsub});
 
1581   call_sub($main::form->{no_nextsub});
 
1585   call_sub($main::form->{update_nextsub} || $main::form->{nextsub} || 'update_delivery_order');
 
1589   my $form     = $main::form;
 
1590   my $locale   = $main::locale;
 
1592   foreach my $action (qw(update ship_to print e_mail save transfer_out transfer_out_default sort
 
1593                          transfer_in transfer_in_default mark_closed save_as_new invoice delete)) {
 
1594     if ($form->{"action_${action}"}) {
 
1600   $form->error($locale->text('No action defined.'));
 
1603 sub transfer_out_default {
 
1604   $main::lxdebug->enter_sub();
 
1606   my $form     = $main::form;
 
1608   transfer_in_out_default('direction' => 'out');
 
1610   $main::lxdebug->leave_sub();
 
1613 sub transfer_in_default {
 
1614   $main::lxdebug->enter_sub();
 
1616   my $form     = $main::form;
 
1618   transfer_in_out_default('direction' => 'in');
 
1620   $main::lxdebug->leave_sub();
 
1623 # Falls das Standardlagerverfahren aktiv ist, wird
 
1624 # geprüft, ob alle Standardlagerplätze für die Auslager-
 
1625 # artikel vorhanden sind UND ob die Warenmenge ausreicht zum
 
1626 # Auslagern. Falls nicht wird entsprechend eine Fehlermeldung
 
1627 # generiert. Offen Chargennummer / bestbefore wird nicht berücksichtigt
 
1628 sub transfer_in_out_default {
 
1629   $main::lxdebug->enter_sub();
 
1631   my $form     = $main::form;
 
1632   my %myconfig = %main::myconfig;
 
1633   my $locale   = $main::locale;
 
1636   my (%missing_default_bins, %qty_parts, @all_requests, %part_info_map, $default_warehouse_id, $default_bin_id);
 
1638   Common::check_params(\%params, qw(direction));
 
1640   # entsprechende defaults holen, falls standardlagerplatz verwendet werden soll
 
1641   if ($::instance_conf->get_transfer_default_use_master_default_bin) {
 
1642     $default_warehouse_id = $::instance_conf->get_warehouse_id;
 
1643     $default_bin_id       = $::instance_conf->get_bin_id;
 
1647   my @part_ids = map { $form->{"id_${_}"} } (1 .. $form->{rowcount});
 
1649     my $units         = AM->retrieve_units(\%myconfig, $form);
 
1650     %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
 
1651     foreach my $i (1 .. $form->{rowcount}) {
 
1652       next unless ($form->{"id_$i"});
 
1653       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
 
1654       my $qty =   $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
 
1656       $form->show_generic_error($locale->text("Cannot transfer negative entries." )) if ($qty < 0);
 
1657       # if we do not want to transfer services and this part is a service, set qty to zero
 
1658       # ... and do not create a hash entry in %qty_parts below (will skip check for bins for the transfer == out case)
 
1659       # ... 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)
 
1661       $qty = 0 if (!$::instance_conf->get_transfer_default_services && !defined($part_info_map{$form->{"id_$i"}}->{inventory_accno_id}) && !$part_info_map{$form->{"id_$i"}}->{assembly});
 
1662       $qty_parts{$form->{"id_$i"}} += $qty;
 
1664         delete $qty_parts{$form->{"id_$i"}} unless $qty_parts{$form->{"id_$i"}};
 
1665         undef $form->{"stock_in_$i"};
 
1668       $part_info_map{$form->{"id_$i"}}{bin_id}       ||= $default_bin_id;
 
1669       $part_info_map{$form->{"id_$i"}}{warehouse_id} ||= $default_warehouse_id;
 
1671       push @all_requests, ($qty == 0) ? { } : {
 
1672                         'chargenumber' => '',  #?? die müsste entsprechend geholt werden
 
1673                         #'bestbefore' => undef, # TODO wird nicht berücksichtigt
 
1674                         'bin_id' => $part_info_map{$form->{"id_$i"}}{bin_id},
 
1676                         'parts_id' => $form->{"id_$i"},
 
1677                         'comment' => $locale->text("Default transfer delivery order"),
 
1678                         'unit' => $part_info_map{$form->{"id_$i"}}{unit},
 
1679                         'warehouse_id' => $part_info_map{$form->{"id_$i"}}{warehouse_id},
 
1680                         'oe_id' => $form->{id},
 
1681                         'project_id' => $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id}
 
1685     # jetzt wird erst überprüft, ob die Stückzahl entsprechend stimmt.
 
1686     # check if bin (transfer in and transfer out and qty (transfer out) is correct
 
1687     foreach my $key (keys %qty_parts) {
 
1689       $missing_default_bins{$key}{missing_bin} = 1 unless ($part_info_map{$key}{bin_id});
 
1690       next unless ($part_info_map{$key}{bin_id}); # abbruch
 
1692       if ($params{direction} eq 'out') {  # wird nur für ausgehende Mengen benötigt
 
1693         my ($max_qty, $error) = WH->get_max_qty_parts_bin(parts_id => $key, bin_id => $part_info_map{$key}{bin_id});
 
1695           # wir können nicht entscheiden, welche charge oder mhd (bestbefore) ausgewählt sein soll
 
1696           # deshalb rückmeldung nach oben geben, manuell auszulagern
 
1697           # TODO Bei nur einem Treffer mit Charge oder bestbefore wäre das noch möglich
 
1698           $missing_default_bins{$key}{chargenumber} = 1;
 
1700         if ($max_qty < $qty_parts{$key}){
 
1701           $missing_default_bins{$key}{missing_qty} = $max_qty - $qty_parts{$key};
 
1707   # Abfrage für Fehlerbehandlung (nur bei direction == out)
 
1708   if (scalar (keys %missing_default_bins)) {
 
1710     foreach my $fehler (keys %missing_default_bins) {
 
1712       my $ware = WH->get_part_description(parts_id => $fehler);
 
1713       if ($missing_default_bins{$fehler}{missing_bin}){
 
1714         $fehlertext .= "Kein Standardlagerplatz definiert bei $ware <br>";
 
1716       if ($missing_default_bins{$fehler}{missing_qty}) {  # missing_qty
 
1717         $fehlertext .= "Es fehlen " . $missing_default_bins{$fehler}{missing_qty}*-1 .
 
1718                        " von $ware auf dem Standard-Lagerplatz " . $part_info_map{$fehler}{bin} .   " zum Auslagern<br>";
 
1720       if ($missing_default_bins{$fehler}{chargenumber}){
 
1721         $fehlertext .= "Die Ware hat eine Chargennummer oder eine Mindesthaltbarkeit definiert.
 
1722                         Hier kann man nicht automatisch entscheiden.
 
1723                         Bitte diesen Lieferschein manuell auslagern.
 
1726       # auslagern soll immer gehen, auch wenn nicht genügend auf lager ist.
 
1727       # der lagerplatz ist hier extra konfigurierbar, bspw. Lager-Korrektur mit
 
1728       # Lagerplatz Lagerplatz-Korrektur
 
1729       my $default_warehouse_id_ignore_onhand = $::instance_conf->get_warehouse_id_ignore_onhand;
 
1730       my $default_bin_id_ignore_onhand       = $::instance_conf->get_bin_id_ignore_onhand;
 
1731       if ($::instance_conf->get_transfer_default_ignore_onhand && $default_bin_id_ignore_onhand) {
 
1732         # entsprechende defaults holen
 
1733         # falls chargenumber, bestbefore oder anzahl nicht stimmt, auf automatischen
 
1734         # lagerplatz wegbuchen!
 
1735         foreach (@all_requests) {
 
1736           if ($_->{parts_id} eq $fehler){
 
1737           $_->{bin_id}        = $default_bin_id_ignore_onhand;
 
1738           $_->{warehouse_id}  = $default_warehouse_id_ignore_onhand;
 
1742         #$main::lxdebug->message(0, 'Fehlertext: ' . $fehlertext);
 
1743         $form->show_generic_error($locale->text("Cannot transfer. <br> Reason:<br>#1", $fehlertext ));
 
1749   # hier der eigentliche fallunterschied für in oder out
 
1750   my $prefix   = $params{direction} eq 'in' ? 'in' : 'out';
 
1752   # dieser array_ref ist für DO->save da:
 
1753   # einmal die all_requests in YAML verwandeln, damit delivery_order_items_stock
 
1754   # gefüllt werden kann.
 
1755   # could be dumped to the form in the first loop,
 
1756   # but maybe bin_id and warehouse_id has changed to the "korrekturlager" with
 
1757   # allowed negative qty ($::instance_conf->get_warehouse_id_ignore_onhand) ...
 
1759   foreach (@all_requests){
 
1761     next unless scalar(%{ $_ });
 
1762     $form->{"stock_${prefix}_$i"} = YAML::Dump([$_]);
 
1765   save(no_redirect => 1); # Wir können auslagern, deshalb beleg speichern
 
1766                           # und in delivery_order_items_stock speichern
 
1768   # ... and fill back the persistent dois_id for inventory fk
 
1769   undef (@all_requests);
 
1770   foreach my $i (1 .. $form->{rowcount}) {
 
1771     next unless ($form->{"id_$i"} && $form->{"stock_${prefix}_$i"});
 
1772     push @all_requests, @{ DO->unpack_stock_information('packed' => $form->{"stock_${prefix}_$i"}) };
 
1774   DO->transfer_in_out('direction' => $prefix,
 
1775                       'requests'  => \@all_requests);
 
1777   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
 
1779   $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'out';
 
1780   $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'in';
 
1786   $main::lxdebug->enter_sub();
 
1790   my $form     = $main::form;
 
1793   save(no_redirect => 1); # has to be done, at least for newly added positions
 
1795   # hashify partnumbers, positions. key is delivery_order_items_id
 
1796   for my $i (1 .. ($form->{rowcount}) ) {
 
1797     $temp_hash{$form->{"delivery_order_items_id_$i"}} = { runningnumber => $form->{"runningnumber_$i"}, partnumber => $form->{"partnumber_$i"} };
 
1798     if ($form->{id} && $form->{"discount_$i"}) {
 
1799       # prepare_order assumes a db value if there is a form->id and multiplies *100
 
1800       # We hope for new controller code (no more format_amount/parse_amount distinction)
 
1801       $form->{"discount_$i"} /=100;
 
1804   # naturally sort partnumbers and get a sorted array of doi_ids
 
1805   my @sorted_doi_ids =  sort { Sort::Naturally::ncmp($temp_hash{$a}->{"partnumber"}, $temp_hash{$b}->{"partnumber"}) }  keys %temp_hash;
 
1810   for (@sorted_doi_ids) {
 
1811     $form->{"runningnumber_$temp_hash{$_}->{runningnumber}"} = $new_number;
 
1814   # all parse_amounts changes are in form (i.e. , to .) therefore we need
 
1815   # another format_amount to change it back, for the next save ;-(
 
1816   # works great except for row discounts (see above comment)
 
1820     $main::lxdebug->leave_sub();
 
1832 do.pl - Script for all calls to delivery order
 
1841 Sorts all position with Natural Sort. Can be activated in form_footer.html like this
 
1842 C<E<lt>input class="submit" type="submit" name="action_sort" id="sort_button" value="[% 'Sort and Save' | $T8 %]"E<gt>>
 
1848 Sort and Save can be implemented as an optional button if configuration ca be set by client config.
 
1849 Example coding for database scripts and templates in (git show af2f24b8), check also
 
1850 autogeneration for rose (scripts/rose_auto_create_model.pl --h)