Lieferschein: Übernahme einiger Felder aus Auftrag gefixt
[kivitendo-erp.git] / bin / mozilla / do.pl
1 #=====================================================================
2 # LX-Office ERP
3 # Copyright (C) 2004
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
6 #
7 #=====================================================================
8 # SQL-Ledger, Accounting
9 # Copyright (c) 1998-2003
10 #
11 #  Author: Dieter Simader
12 #   Email: dsimader@sql-ledger.org
13 #     Web: http://www.sql-ledger.org
14 #
15 #
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.
20 #
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., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #======================================================================
29 #
30 # Delivery orders
31 #======================================================================
32
33 use List::MoreUtils qw(uniq);
34 use List::Util qw(max sum);
35 use POSIX qw(strftime);
36 use YAML;
37
38 use SL::DB::DeliveryOrder;
39 use SL::DO;
40 use SL::IR;
41 use SL::IS;
42 use SL::MoreCommon qw(ary_diff);
43 use SL::ReportGenerator;
44 use SL::WH;
45 use Sort::Naturally ();
46 require "bin/mozilla/arap.pl";
47 require "bin/mozilla/common.pl";
48 require "bin/mozilla/invoice_io.pl";
49 require "bin/mozilla/io.pl";
50 require "bin/mozilla/reportgenerator.pl";
51
52 use strict;
53
54 1;
55
56 # end of main
57
58 sub check_do_access {
59   $main::auth->assert($main::form->{type} . '_edit');
60 }
61
62 sub set_headings {
63   $main::lxdebug->enter_sub();
64
65   check_do_access();
66
67   my ($action) = @_;
68
69   my $form     = $main::form;
70   my $locale   = $main::locale;
71
72   if ($form->{type} eq 'purchase_delivery_order') {
73     $form->{vc}    = 'vendor';
74     $form->{title} = $action eq "edit" ? $locale->text('Edit Purchase Delivery Order') : $locale->text('Add Purchase Delivery Order');
75   } else {
76     $form->{vc}    = 'customer';
77     $form->{title} = $action eq "edit" ? $locale->text('Edit Sales Delivery Order') : $locale->text('Add Sales Delivery Order');
78   }
79
80   $form->{heading} = $locale->text('Delivery Order');
81
82   $main::lxdebug->leave_sub();
83 }
84
85 sub add {
86   $main::lxdebug->enter_sub();
87
88   check_do_access();
89
90   if (($::form->{type} =~ /purchase/) && !$::instance_conf->get_allow_new_purchase_invoice) {
91     $::form->show_generic_error($::locale->text("You do not have the permissions to access this function."));
92   }
93
94   my $form     = $main::form;
95
96   set_headings("add");
97
98   $form->{callback} = build_std_url('action=add', 'type', 'vc') unless ($form->{callback});
99
100   order_links();
101   prepare_order();
102   display_form();
103
104   $main::lxdebug->leave_sub();
105 }
106
107 sub edit {
108   $main::lxdebug->enter_sub();
109
110   check_do_access();
111
112   my $form     = $main::form;
113
114   # show history button
115   $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
116   #/show hhistory button
117
118   $form->{simple_save} = 0;
119
120   set_headings("edit");
121
122   # editing without stuff to edit? try adding it first
123   if ($form->{rowcount} && !$form->{print_and_save}) {
124 #     map { $id++ if $form->{"multi_id_$_"} } (1 .. $form->{rowcount});
125 #     if (!$id) {
126
127       # reset rowcount
128       undef $form->{rowcount};
129       add();
130       $main::lxdebug->leave_sub();
131       return;
132 #     }
133   } elsif (!$form->{id}) {
134     add();
135     $main::lxdebug->leave_sub();
136     return;
137   }
138
139   my ($language_id, $printer_id);
140   if ($form->{print_and_save}) {
141     $form->{action}   = "dispatcher";
142     $form->{action_print} = "1";
143     $form->{resubmit} = 1;
144     $language_id      = $form->{language_id};
145     $printer_id       = $form->{printer_id};
146   }
147
148   set_headings("edit");
149
150   order_links();
151   prepare_order();
152
153   if ($form->{print_and_save}) {
154     $form->{language_id} = $language_id;
155     $form->{printer_id}  = $printer_id;
156   }
157
158   display_form();
159
160   $main::lxdebug->leave_sub();
161 }
162
163 sub order_links {
164   $main::lxdebug->enter_sub();
165
166   check_do_access();
167
168   my $form     = $main::form;
169   my %myconfig = %main::myconfig;
170
171   # get customer/vendor
172   $form->all_vc(\%myconfig, $form->{vc}, ($form->{vc} eq 'customer') ? "AR" : "AP");
173
174   # retrieve order/quotation
175   $form->{webdav}   = $::instance_conf->get_webdav;
176
177   my $editing = $form->{id};
178
179   DO->retrieve('vc'  => $form->{vc},
180                'ids' => $form->{id});
181
182   $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes delivery_term_id currency));
183
184   # get customer / vendor
185   if ($form->{vc} eq 'vendor') {
186     IR->get_vendor(\%myconfig, \%$form);
187     $form->{discount} = $form->{vendor_discount};
188   } else {
189     IS->get_customer(\%myconfig, \%$form);
190     $form->{discount} = $form->{customer_discount};
191   }
192
193   $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id delivery_term_id));
194   $form->restore_vars(qw(currency)) if ($form->{id} || $form->{convert_from_oe_ids});
195   $form->restore_vars(qw(taxincluded)) if $form->{id};
196   $form->restore_vars(qw(salesman_id)) if $editing;
197
198   if ($form->{"all_$form->{vc}"}) {
199     unless ($form->{"$form->{vc}_id"}) {
200       $form->{"$form->{vc}_id"} = $form->{"all_$form->{vc}"}->[0]->{id};
201     }
202   }
203
204   ($form->{ $form->{vc} })  = split /--/, $form->{ $form->{vc} };
205   $form->{"old$form->{vc}"} = qq|$form->{$form->{vc}}--$form->{"$form->{vc}_id"}|;
206
207   $form->{employee} = "$form->{employee}--$form->{employee_id}";
208
209   $main::lxdebug->leave_sub();
210 }
211
212 sub prepare_order {
213   $main::lxdebug->enter_sub();
214
215   check_do_access();
216
217   my $form     = $main::form;
218   my %myconfig = %main::myconfig;
219
220   $form->{formname} = $form->{type} unless $form->{formname};
221
222   my $i = 0;
223   foreach my $ref (@{ $form->{form_details} }) {
224     $form->{rowcount} = ++$i;
225
226     map { $form->{"${_}_$i"} = $ref->{$_} } keys %{$ref};
227   }
228   for my $i (1 .. $form->{rowcount}) {
229     if ($form->{id}) {
230       $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100);
231     } else {
232       $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
233     }
234     my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
235     $dec           = length $dec;
236     my $decimalplaces = ($dec > 2) ? $dec : 2;
237
238     # copy reqdate from deliverydate for invoice -> order conversion
239     $form->{"reqdate_$i"} = $form->{"deliverydate_$i"} unless $form->{"reqdate_$i"};
240
241     $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
242     $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}, $decimalplaces);
243
244     (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
245     $dec_qty = length $dec_qty;
246     $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
247   }
248
249   $main::lxdebug->leave_sub();
250 }
251
252 sub form_header {
253   $main::lxdebug->enter_sub();
254
255   check_do_access();
256
257   my $form     = $main::form;
258   my %myconfig = %main::myconfig;
259
260   $form->{employee_id} = $form->{old_employee_id} if $form->{old_employee_id};
261   $form->{salesman_id} = $form->{old_salesman_id} if $form->{old_salesman_id};
262
263   my $vc = $form->{vc} eq "customer" ? "customers" : "vendors";
264   $form->get_lists($vc              => "ALL_VC",
265                    "price_factors"  => "ALL_PRICE_FACTORS",
266                    "departments"    => "ALL_DEPARTMENTS",
267                    "business_types" => "ALL_BUSINESS_TYPES",
268     );
269
270   # Projects
271   my @old_project_ids = uniq grep { $_ } map { $_ * 1 } ($form->{"globalproject_id"}, map { $form->{"project_id_$_"} } 1..$form->{"rowcount"});
272   my @old_ids_cond    = @old_project_ids ? (id => \@old_project_ids) : ();
273   my @customer_cond;
274   if (($vc eq 'customers') && $::instance_conf->get_customer_projects_only_in_sales) {
275     @customer_cond = (
276       or => [
277         customer_id          => $::form->{customer_id},
278         billable_customer_id => $::form->{customer_id},
279       ]);
280   }
281   my @conditions = (
282     or => [
283       and => [ active => 1, @customer_cond ],
284       @old_ids_cond,
285     ]);
286
287   $::form->{ALL_PROJECTS}          = SL::DB::Manager::Project->get_all_sorted(query => \@conditions);
288   $::form->{ALL_EMPLOYEES}         = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{employee_id},  deleted => 0 ] ]);
289   $::form->{ALL_SALESMEN}          = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{salesman_id},  deleted => 0 ] ]);
290   $::form->{ALL_SHIPTO}            = SL::DB::Manager::Shipto->get_all_sorted(query => [
291     or => [ trans_id  => $::form->{"$::form->{vc}_id"} * 1, and => [ shipto_id => $::form->{shipto_id} * 1, trans_id => undef ] ]
292   ]);
293   $::form->{ALL_CONTACTS}          = SL::DB::Manager::Contact->get_all_sorted(query => [
294     or => [
295       cp_cv_id => $::form->{"$::form->{vc}_id"} * 1,
296       and      => [
297         cp_cv_id => undef,
298         cp_id    => $::form->{cp_id} * 1
299       ]
300     ]
301   ]);
302
303   map { $_->{value} = "$_->{description}--$_->{id}" } @{ $form->{ALL_DEPARTMENTS} };
304   map { $_->{value} = "$_->{name}--$_->{id}"        } @{ $form->{ALL_VC} };
305
306   $form->{SHOW_VC_DROP_DOWN} =  $myconfig{vclimit} > scalar @{ $form->{ALL_VC} };
307
308   $form->{oldvcname}         =  $form->{"old$form->{vc}"};
309   $form->{oldvcname}         =~ s/--.*//;
310
311   my $dispatch_to_popup = '';
312   if ($form->{resubmit} && ($form->{format} eq "html")) {
313     $dispatch_to_popup  = "window.open('about:blank','Beleg'); document.do.target = 'Beleg';";
314     $dispatch_to_popup .= "document.do.submit();";
315   } elsif ($form->{resubmit}) {
316     # emulate click for resubmitting actions
317     $dispatch_to_popup  = "document.do.${_}.click(); " for grep { /^action_/ } keys %$form;
318   }
319   $::request->{layout}->add_javascripts_inline("\$(function(){$dispatch_to_popup});");
320
321
322   my $follow_up_vc                =  $form->{ $form->{vc} eq 'customer' ? 'customer' : 'vendor' };
323   $follow_up_vc                   =~ s/--\d*\s*$//;
324
325   $form->{follow_up_trans_info} = $form->{donumber} .'('. $follow_up_vc .')';
326
327   $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.SalesPurchase ckeditor/ckeditor ckeditor/adapters/jquery kivi.io autocomplete_customer autocomplete_part));
328
329   $form->header();
330   # Fix für Bug 1082 Erwartet wird: 'abteilungsNAME--abteilungsID'
331   # und Erweiterung für Bug 1760:
332   # Das war leider nur ein Teil-Fix, da das Verhalten den 'Erneuern'-Knopf
333   # nicht überlebt. Konsequent jetzt auf L umgestellt
334   #   $ perldoc SL::Template::Plugin::L
335   # Daher entsprechend nur die Anpassung in form_header
336   # und in DO.pm gemacht. 4 Testfälle:
337   # department_id speichern                 | i.O.
338   # department_id lesen                     | i.O.
339   # department leer überlebt erneuern       | i.O.
340   # department nicht leer überlebt erneuern | i.O.
341   # $main::lxdebug->message(0, 'ABTEILUNGS ID in form?' . $form->{department_id});
342   print $form->parse_html_template('do/form_header');
343
344   $main::lxdebug->leave_sub();
345 }
346
347 sub form_footer {
348   $main::lxdebug->enter_sub();
349
350   check_do_access();
351
352   my $form     = $main::form;
353
354   $form->{PRINT_OPTIONS} = print_options('inline' => 1);
355   $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
356
357   print $form->parse_html_template('do/form_footer',
358     {transfer_default         => ($::instance_conf->get_transfer_default)});
359
360   $main::lxdebug->leave_sub();
361 }
362
363 sub update_delivery_order {
364   $main::lxdebug->enter_sub();
365
366   check_do_access();
367
368   my $form     = $main::form;
369   my %myconfig = %main::myconfig;
370
371   set_headings($form->{"id"} ? "edit" : "add");
372
373   $form->{insertdate} = SL::DB::DeliveryOrder->new(id => $form->{id})->load->itime_as_date if $form->{id};
374
375   $form->{update} = 1;
376
377   my $payment_id;
378   $payment_id = $form->{payment_id} if $form->{payment_id};
379
380   check_name($form->{vc});
381   $form->{discount} =  $form->{"$form->{vc}_discount"} if defined $form->{"$form->{vc}_discount"};
382   # Problem: Wenn man ohne Erneuern einen Kunden/Lieferanten
383   # wechselt, wird der entsprechende Kunden/ Lieferantenrabatt
384   # nicht übernommen. Grundproblem: In Commit 82574e78
385   # hab ich aus discount customer_discount und vendor_discount
386   # gemacht und entsprechend an den Oberflächen richtig hin-
387   # geschoben. Die damals bessere Lösung wäre gewesen:
388   # In den Templates nur die hidden für form-discount wieder ein-
389   # setzen dann wäre die Verrenkung jetzt nicht notwendig.
390   # TODO: Ggf. Bugfix 1284, 1575 und 817 wieder zusammenführen
391   # Testfälle: Kunden mit Rabatt 0 -> Rabatt 20 i.O.
392   #            Kunde mit Rabatt 20 -> Rabatt 0  i.O.
393   #            Kunde mit Rabatt 20 -> Rabatt 5,5 i.O.
394   $form->{payment_id} = $payment_id if $form->{payment_id} eq "";
395
396   my $i = $form->{rowcount};
397
398   if (   ($form->{"partnumber_$i"} eq "")
399       && ($form->{"description_$i"} eq "")
400       && ($form->{"partsgroup_$i"}  eq "")) {
401
402     check_form();
403
404   } else {
405
406     my $mode;
407     if ($form->{type} eq 'purchase_delivery_order') {
408       IR->retrieve_item(\%myconfig, $form);
409       $mode = 'IR';
410     } else {
411       IS->retrieve_item(\%myconfig, $form);
412       $mode = 'IS';
413     }
414
415     my $rows = scalar @{ $form->{item_list} };
416
417     if ($rows) {
418       $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
419       if( !$form->{"qty_$i"} ) {
420         $form->{"qty_$i"} = 1;
421       }
422
423       if ($rows > 1) {
424
425         select_item(mode => $mode, pre_entered_qty => $form->{"qty_$i"});
426         ::end_of_request();
427
428       } else {
429
430         my $sellprice = $form->parse_amount(\%myconfig, $form->{"sellprice_$i"});
431
432         map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} } keys %{ $form->{item_list}[0] };
433
434         $form->{"marge_price_factor_$i"} = $form->{item_list}->[0]->{price_factor};
435
436         if ($sellprice) {
437           $form->{"sellprice_$i"} = $sellprice;
438         } else {
439           my $record        = _make_record();
440           my $price_source  = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
441           my $best_price    = $price_source->best_price;
442           my $best_discount = $price_source->best_discount;
443
444           if ($best_price) {
445             $::form->{"sellprice_$i"}           = $best_price->price;
446             $::form->{"active_price_source_$i"} = $best_price->source;
447           }
448           if ($best_discount) {
449             $::form->{"discount_$i"}               = $best_discount->discount;
450             $::form->{"active_discount_source_$i"} = $best_discount->source;
451           }
452         }
453
454         $form->{"sellprice_$i"}          = $form->format_amount(\%myconfig, $form->{"sellprice_$i"});
455         $form->{"lastcost_$i"}           = $form->format_amount(\%myconfig, $form->{"lastcost_$i"});
456         $form->{"qty_$i"}                = $form->format_amount(\%myconfig, $form->{"qty_$i"});
457         $form->{"discount_$i"}           = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0);
458       }
459
460       display_form();
461
462     } else {
463
464       # ok, so this is a new part
465       # ask if it is a part or service item
466
467       if (   $form->{"partsgroup_$i"}
468           && ($form->{"partsnumber_$i"} eq "")
469           && ($form->{"description_$i"} eq "")) {
470         $form->{rowcount}--;
471         $form->{"discount_$i"} = "";
472         $form->{"not_discountable_$i"} = "";
473         display_form();
474
475       } else {
476         $form->{"id_$i"}   = 0;
477         new_item();
478       }
479     }
480   }
481
482   $main::lxdebug->leave_sub();
483 }
484
485 sub search {
486   $main::lxdebug->enter_sub();
487
488   check_do_access();
489
490   my $form     = $main::form;
491   my %myconfig = %main::myconfig;
492   my $locale   = $main::locale;
493
494   $form->{vc} = $form->{type} eq 'purchase_delivery_order' ? 'vendor' : 'customer';
495
496   $form->get_lists("projects"       => { "key" => "ALL_PROJECTS",
497                                          "all" => 1 },
498                    "departments"    => "ALL_DEPARTMENTS",
499                    "$form->{vc}s"   => "ALL_VC",
500                    "business_types" => "ALL_BUSINESS_TYPES");
501   $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]);
502
503   $form->{SHOW_VC_DROP_DOWN} =  $myconfig{vclimit} > scalar @{ $form->{ALL_VC} };
504   $form->{title}             = $locale->text('Delivery Orders');
505
506   $form->header();
507
508   print $form->parse_html_template('do/search');
509
510   $main::lxdebug->leave_sub();
511 }
512
513 sub orders {
514   $main::lxdebug->enter_sub();
515
516   check_do_access();
517
518   my $form     = $main::form;
519   my %myconfig = %main::myconfig;
520   my $locale   = $main::locale;
521   my $cgi      = $::request->{cgi};
522
523   $form->{department_id} = (split /--/, $form->{department})[-1];
524   ($form->{ $form->{vc} }, $form->{"$form->{vc}_id"}) = split(/--/, $form->{ $form->{vc} });
525
526   report_generator_set_default_sort('transdate', 1);
527
528   DO->transactions();
529
530   $form->{rowcount} = scalar @{ $form->{DO} };
531
532   my @columns = qw(
533     ids                     transdate               reqdate
534     id                      donumber
535     ordnumber               customernumber          cusordnumber
536     name                    employee  salesman
537     shipvia                 globalprojectnumber
538     transaction_description department
539     open                    delivered
540     insertdate
541   );
542
543   $form->{l_open}      = $form->{l_closed} = "Y" if ($form->{open}      && $form->{closed});
544   $form->{l_delivered} = "Y"                     if ($form->{delivered} && $form->{notdelivered});
545
546   $form->{title}       = $locale->text('Delivery Orders');
547
548   my $attachment_basename = $form->{vc} eq 'vendor' ? $locale->text('purchase_delivery_order_list') : $locale->text('sales_delivery_order_list');
549
550   my $report = SL::ReportGenerator->new(\%myconfig, $form);
551
552   my @hidden_variables = map { "l_${_}" } @columns;
553   push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber serialnumber cusordnumber
554                                           transaction_description transdatefrom transdateto reqdatefrom reqdateto
555                                           type vc employee_id salesman_id project_id
556                                           insertdatefrom insertdateto business_id);
557
558   my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
559
560   my %column_defs = (
561     'ids'                     => { 'text' => '', },
562     'transdate'               => { 'text' => $locale->text('Delivery Order Date'), },
563     'reqdate'                 => { 'text' => $locale->text('Reqdate'), },
564     'id'                      => { 'text' => $locale->text('ID'), },
565     'donumber'                => { 'text' => $locale->text('Delivery Order'), },
566     'ordnumber'               => { 'text' => $locale->text('Order'), },
567     'customernumber'          => { 'text' => $locale->text('Customer Number'), },
568     'cusordnumber'            => { 'text' => $locale->text('Customer Order Number'), },
569     'name'                    => { 'text' => $form->{vc} eq 'customer' ? $locale->text('Customer') : $locale->text('Vendor'), },
570     'employee'                => { 'text' => $locale->text('Employee'), },
571     'salesman'                => { 'text' => $locale->text('Salesman'), },
572     'shipvia'                 => { 'text' => $locale->text('Ship via'), },
573     'globalprojectnumber'     => { 'text' => $locale->text('Project Number'), },
574     'transaction_description' => { 'text' => $locale->text('Transaction description'), },
575     'open'                    => { 'text' => $locale->text('Open'), },
576     'delivered'               => { 'text' => $locale->text('Delivered'), },
577     'department'              => { 'text' => $locale->text('Department'), },
578     'insertdate'              => { 'text' => $locale->text('Insert Date'), },
579   );
580
581   foreach my $name (qw(id transdate reqdate donumber ordnumber name employee salesman shipvia transaction_description department insertdate)) {
582     my $sortdir                 = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
583     $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
584   }
585
586   $form->{"l_type"} = "Y";
587   map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
588
589   $column_defs{ids}->{visible} = 'HTML';
590
591   $report->set_columns(%column_defs);
592   $report->set_column_order(@columns);
593
594   $report->set_export_options('orders', @hidden_variables, qw(sort sortdir));
595
596   $report->set_sort_indicator($form->{sort}, $form->{sortdir});
597
598   my @options;
599   if ($form->{customer}) {
600     push @options, $locale->text('Customer') . " : $form->{customer}";
601   }
602   if ($form->{vendor}) {
603     push @options, $locale->text('Vendor') . " : $form->{vendor}";
604   }
605   if ($form->{cp_name}) {
606     push @options, $locale->text('Contact Person') . " : $form->{cp_name}";
607   }
608   if ($form->{department}) {
609     my ($department) = split /--/, $form->{department};
610     push @options, $locale->text('Department') . " : $department";
611   }
612   if ($form->{donumber}) {
613     push @options, $locale->text('Delivery Order Number') . " : $form->{donumber}";
614   }
615   if ($form->{ordnumber}) {
616     push @options, $locale->text('Order Number') . " : $form->{ordnumber}";
617   }
618   push @options, $locale->text('Serial Number') . " : $form->{serialnumber}" if $form->{serialnumber};
619   if ($form->{business_id}) {
620     my $vc_type_label = $form->{vc} eq 'customer' ? $locale->text('Customer type') : $locale->text('Vendor type');
621     push @options, $vc_type_label . " : " . SL::DB::Business->new(id => $form->{business_id})->load->description;
622   }
623   if ($form->{transaction_description}) {
624     push @options, $locale->text('Transaction description') . " : $form->{transaction_description}";
625   }
626   if ( $form->{transdatefrom} or $form->{transdateto} ) {
627     push @options, $locale->text('Delivery Order Date');
628     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1)     if $form->{transdatefrom};
629     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{transdateto},   1)     if $form->{transdateto};
630   };
631   if ( $form->{reqdatefrom} or $form->{reqdateto} ) {
632     push @options, $locale->text('Reqdate');
633     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1)       if $form->{reqdatefrom};
634     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{reqdateto},   1)       if $form->{reqdateto};
635   };
636   if ( $form->{insertdatefrom} or $form->{insertdateto} ) {
637     push @options, $locale->text('Insert Date');
638     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1)    if $form->{insertdatefrom};
639     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{insertdateto},   1)    if $form->{insertdateto};
640   };
641   if ($form->{open}) {
642     push @options, $locale->text('Open');
643   }
644   if ($form->{closed}) {
645     push @options, $locale->text('Closed');
646   }
647   if ($form->{delivered}) {
648     push @options, $locale->text('Delivered');
649   }
650   if ($form->{notdelivered}) {
651     push @options, $locale->text('Not delivered');
652   }
653
654   $report->set_options('top_info_text'        => join("\n", @options),
655                        'raw_top_info_text'    => $form->parse_html_template('do/orders_top'),
656                        'raw_bottom_info_text' => $form->parse_html_template('do/orders_bottom'),
657                        'output_format'        => 'HTML',
658                        'title'                => $form->{title},
659                        'attachment_basename'  => $attachment_basename . strftime('_%Y%m%d', localtime time),
660     );
661   $report->set_options_from_form();
662   $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
663
664   # add sort and escape callback, this one we use for the add sub
665   $form->{callback} = $href .= "&sort=$form->{sort}";
666
667   # escape callback for href
668   my $callback = $form->escape($href);
669
670   my $edit_url       = build_std_url('action=edit', 'type', 'vc');
671   my $edit_order_url = build_std_url('script=oe.pl', 'type=' . ($form->{type} eq 'sales_delivery_order' ? 'sales_order' : 'purchase_order'), 'action=edit');
672
673   my $idx            = 1;
674
675   foreach my $dord (@{ $form->{DO} }) {
676     $dord->{open}      = $dord->{closed}    ? $locale->text('No')  : $locale->text('Yes');
677     $dord->{delivered} = $dord->{delivered} ? $locale->text('Yes') : $locale->text('No');
678
679     my $row = { map { $_ => { 'data' => $dord->{$_} } } @columns };
680
681     $row->{ids}  = {
682       'raw_data' =>   $cgi->hidden('-name' => "trans_id_${idx}", '-value' => $dord->{id})
683                     . $cgi->checkbox('-name' => "multi_id_${idx}", '-value' => 1, '-label' => ''),
684       'valign'   => 'center',
685       'align'    => 'center',
686     };
687
688     $row->{donumber}->{link}  = $edit_url       . "&id=" . E($dord->{id})      . "&callback=${callback}";
689     $row->{ordnumber}->{link} = $edit_order_url . "&id=" . E($dord->{oe_id})   . "&callback=${callback}" if $dord->{oe_id};
690     $report->add_data($row);
691
692     $idx++;
693   }
694
695   $report->generate_with_headers();
696
697   $main::lxdebug->leave_sub();
698 }
699
700 sub save {
701   $main::lxdebug->enter_sub();
702
703   my (%params) = @_;
704
705   check_do_access();
706
707   my $form     = $main::form;
708   my %myconfig = %main::myconfig;
709   my $locale   = $main::locale;
710
711   $form->mtime_ischanged('delivery_orders');
712
713   $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
714
715   $form->isblank("transdate", $locale->text('Delivery Order Date missing!'));
716
717   $form->{donumber} =~ s/^\s*//g;
718   $form->{donumber} =~ s/\s*$//g;
719
720   my $msg = ucfirst $form->{vc};
721   $form->isblank($form->{vc}, $locale->text($msg . " missing!"));
722
723   # $locale->text('Customer missing!');
724   # $locale->text('Vendor missing!');
725
726   remove_emptied_rows();
727   validate_items();
728
729   # if the name changed get new values
730   if (check_name($form->{vc})) {
731     update();
732     ::end_of_request();
733   }
734
735   $form->{id} = 0 if $form->{saveasnew};
736
737   DO->save();
738   # saving the history
739   if(!exists $form->{addition}) {
740     $form->{snumbers} = qq|donumber_| . $form->{donumber};
741     $form->{addition} = "SAVED";
742     $form->save_history;
743   }
744   # /saving the history
745
746   $form->{simple_save} = 1;
747   if (!$params{no_redirect} && !$form->{print_and_save}) {
748     delete @{$form}{ary_diff([keys %{ $form }], [qw(login id script type cursor_fokus)])};
749     edit();
750     ::end_of_request();
751   }
752   $main::lxdebug->leave_sub();
753 }
754
755 sub delete {
756   $main::lxdebug->enter_sub();
757
758   check_do_access();
759
760   my $form     = $main::form;
761   my %myconfig = %main::myconfig;
762   my $locale   = $main::locale;
763
764   if (DO->delete()) {
765     # saving the history
766     if(!exists $form->{addition}) {
767       $form->{snumbers} = qq|donumber_| . $form->{donumber};
768       $form->{addition} = "DELETED";
769       $form->save_history;
770     }
771     # /saving the history
772
773     $form->info($locale->text('Delivery Order deleted!'));
774     ::end_of_request();
775   }
776
777   $form->error($locale->text('Cannot delete delivery order!'));
778
779   $main::lxdebug->leave_sub();
780 }
781
782 sub invoice {
783   $main::lxdebug->enter_sub();
784
785   my $form     = $main::form;
786   my %myconfig = %main::myconfig;
787   my $locale   = $main::locale;
788
789   check_do_access();
790   $form->mtime_ischanged('delivery_orders');
791
792   $main::auth->assert($form->{type} eq 'purchase_delivery_order' ? 'vendor_invoice_edit' : 'invoice_edit');
793
794   $form->{convert_from_do_ids} = $form->{id};
795   $form->{deliverydate}        = $form->{transdate};
796   $form->{transdate}           = $form->{invdate} = $form->current_date(\%myconfig);
797   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
798   $form->{defaultcurrency}     = $form->get_default_currency(\%myconfig);
799
800   $form->{rowcount}--;
801
802   delete @{$form}{qw(id closed delivered)};
803
804   my ($script, $buysell);
805   if ($form->{type} eq 'purchase_delivery_order') {
806     $form->{title}  = $locale->text('Add Vendor Invoice');
807     $form->{script} = 'ir.pl';
808     $script         = "ir";
809     $buysell        = 'sell';
810
811   } else {
812     $form->{title}  = $locale->text('Add Sales Invoice');
813     $form->{script} = 'is.pl';
814     $script         = "is";
815     $buysell        = 'buy';
816   }
817
818   for my $i (1 .. $form->{rowcount}) {
819     # für bug 1284
820     unless ($form->{"ordnumber"}) {
821       if ($form->{discount}) { # Falls wir einen Lieferanten-/Kundenrabatt haben
822         # und rabattfähig sind, dann
823         unless ($form->{"not_discountable_$i"}) {
824           $form->{"discount_$i"} = $form->{discount}*100; # ... nehmen wir diesen Rabatt
825         }
826       }
827     }
828     map { $form->{"${_}_${i}"} = $form->parse_amount(\%myconfig, $form->{"${_}_${i}"}) if $form->{"${_}_${i}"} } qw(ship qty sellprice lastcost basefactor);
829     $form->{"donumber_$i"} = $form->{donumber};
830     $form->{"converted_from_delivery_order_items_id_$i"} = delete $form->{"delivery_order_items_id_$i"};
831   }
832
833   $form->{type} = "invoice";
834
835   # locale messages
836   $main::locale = Locale->new("$myconfig{countrycode}", "$script");
837   $locale = $main::locale;
838
839   require "bin/mozilla/$form->{script}";
840
841   my $currency = $form->{currency};
842   invoice_links();
843
844   if ($form->{ordnumber}) {
845     require SL::DB::Order;
846     if (my $order = SL::DB::Manager::Order->find_by(ordnumber => $form->{ordnumber})) {
847       $order->load;
848       $form->{orddate} = $order->transdate_as_date;
849       $form->{$_}      = $order->$_ for qw(payment_id salesman_id taxzone_id quonumber);
850     }
851   }
852
853   $form->{currency}     = $currency;
854   $form->{exchangerate} = "";
855   $form->{forex}        = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, $buysell);
856   $form->{exchangerate} = $form->{forex} if ($form->{forex});
857
858   prepare_invoice();
859
860   # format amounts
861   for my $i (1 .. $form->{rowcount}) {
862     $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
863
864     my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
865     $dec           = length $dec;
866     my $decimalplaces = ($dec > 2) ? $dec : 2;
867
868     # copy delivery date from reqdate for order -> invoice conversion
869     $form->{"deliverydate_$i"} = $form->{"reqdate_$i"}
870       unless $form->{"deliverydate_$i"};
871
872
873     $form->{"sellprice_$i"} =
874       $form->format_amount(\%myconfig, $form->{"sellprice_$i"},
875                            $decimalplaces);
876
877     $form->{"lastcost_$i"} =
878       $form->format_amount(\%myconfig, $form->{"lastcost_$i"},
879                            $decimalplaces);
880
881     (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
882     $dec_qty = length $dec_qty;
883     $form->{"qty_$i"} =
884       $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
885
886   }
887
888   display_form();
889
890   $main::lxdebug->leave_sub();
891 }
892
893 sub invoice_multi {
894   $main::lxdebug->enter_sub();
895
896   my $form     = $main::form;
897   my %myconfig = %main::myconfig;
898   my $locale   = $main::locale;
899
900   check_do_access();
901   $main::auth->assert($form->{type} eq 'sales_delivery_order' ? 'invoice_edit' : 'vendor_invoice_edit');
902
903   my @do_ids = map { $form->{"trans_id_$_"} } grep { $form->{"multi_id_$_"} } (1..$form->{rowcount});
904
905   if (!scalar @do_ids) {
906     $form->show_generic_error($locale->text('You have not selected any delivery order.'), 'back_button' => 1);
907   }
908
909   map { delete $form->{$_} } grep { m/^(?:trans|multi)_id_\d+/ } keys %{ $form };
910
911   if (!DO->retrieve('vc' => $form->{vc}, 'ids' => \@do_ids)) {
912     $form->show_generic_error($form->{vc} eq 'customer' ?
913                               $locale->text('You cannot create an invoice for delivery orders for different customers.') :
914                               $locale->text('You cannot create an invoice for delivery orders from different vendors.'),
915                               'back_button' => 1);
916   }
917
918   my $source_type              = $form->{type};
919   $form->{convert_from_do_ids} = join ' ', @do_ids;
920   # bei der auswahl von mehreren Lieferscheinen fuer eine Rechnung, die einfach in donumber_array
921   # zwischenspeichern (DO.pm) und als ' '-separierte Liste wieder zurueckschreiben
922   # Hinweis: delete gibt den wert zurueck und loescht danach das element (nett und einfach)
923   # $shell: perldoc perlunc; /delete EXPR
924   $form->{donumber}            = delete $form->{donumber_array};
925   $form->{ordnumber}           = delete $form->{ordnumber_array};
926   $form->{cusordnumber}        = delete $form->{cusordnumber_array};
927   $form->{deliverydate}        = $form->{transdate};
928   $form->{transdate}           = $form->current_date(\%myconfig);
929   $form->{duedate}             = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
930   $form->{type}                = "invoice";
931   $form->{closed}              = 0;
932   $form->{defaultcurrency}     = $form->get_default_currency(\%myconfig);
933
934   my ($script, $buysell);
935   if ($source_type eq 'purchase_delivery_order') {
936     $form->{title}  = $locale->text('Add Vendor Invoice');
937     $form->{script} = 'ir.pl';
938     $script         = "ir";
939     $buysell        = 'sell';
940
941   } else {
942     $form->{title}  = $locale->text('Add Sales Invoice');
943     $form->{script} = 'is.pl';
944     $script         = "is";
945     $buysell        = 'buy';
946   }
947
948   map { delete $form->{$_} } qw(id subject message cc bcc printed emailed queued);
949
950   # get vendor or customer discount
951   my $vc_discount;
952   my $saved_form = save_form();
953   if ($form->{vc} eq 'vendor') {
954     IR->get_vendor(\%myconfig, \%$form);
955     $vc_discount = $form->{vendor_discount};
956   } else {
957     IS->get_customer(\%myconfig, \%$form);
958     $vc_discount = $form->{customer_discount};
959   }
960   restore_form($saved_form);
961
962   $form->{rowcount} = 0;
963   foreach my $ref (@{ $form->{form_details} }) {
964     $form->{rowcount}++;
965     $ref->{reqdate} ||= $ref->{dord_transdate}; # copy transdates into each invoice row
966     map { $form->{"${_}_$form->{rowcount}"} = $ref->{$_} } keys %{ $ref };
967     map { $form->{"${_}_$form->{rowcount}"} = $form->format_amount(\%myconfig, $ref->{$_}) } qw(qty sellprice lastcost);
968     $form->{"converted_from_delivery_order_items_id_$form->{rowcount}"} = delete $form->{"delivery_order_items_id_$form->{rowcount}"};
969
970     if ($vc_discount){ # falls wir einen Lieferanten/Kundenrabatt haben
971       # und keinen anderen discount wert an $i ...
972       $form->{"discount_$form->{rowcount}"} ||= $vc_discount; # ... nehmen wir diesen Rabatt
973     }
974
975     $form->{"discount_$form->{rowcount}"}   = $form->{"discount_$form->{rowcount}"}  * 100; #s.a. Bug 1151
976     # Anm.: Eine Änderung des discounts in der SL/DO.pm->retrieve (select (doi.discount * 100) as discount) ergibt in psql einen
977     # Wert von 10.0000001490116. Ferner ist der Rabatt in der Rechnung dann bei 1.0 (?). Deswegen lasse ich das hier. jb 10.10.09
978
979     $form->{"discount_$form->{rowcount}"} = $form->format_amount(\%myconfig, $form->{"discount_$form->{rowcount}"});
980   }
981   delete $form->{form_details};
982
983   $locale = Locale->new("$myconfig{countrycode}", "$script");
984
985   require "bin/mozilla/$form->{script}";
986
987   invoice_links();
988   prepare_invoice();
989
990   display_form();
991
992   $main::lxdebug->leave_sub();
993 }
994
995 sub save_as_new {
996   $main::lxdebug->enter_sub();
997
998   check_do_access();
999
1000   my $form     = $main::form;
1001
1002   $form->{saveasnew} = 1;
1003   $form->{closed}    = 0;
1004   $form->{delivered} = 0;
1005   map { delete $form->{$_} } qw(printed emailed queued);
1006   delete @{ $form }{ grep { m/^stock_(?:in|out)_\d+/ } keys %{ $form } };
1007   $form->{"converted_from_delivery_order_items_id_$_"} = delete $form->{"delivery_order_items_id_$_"} for 1 .. $form->{"rowcount"};
1008   # Let kivitendo assign a new order number if the user hasn't changed the
1009   # previous one. If it has been changed manually then use it as-is.
1010   $form->{donumber} =~ s/^\s*//g;
1011   $form->{donumber} =~ s/\s*$//g;
1012   if ($form->{saved_donumber} && ($form->{saved_donumber} eq $form->{donumber})) {
1013     delete($form->{donumber});
1014   }
1015
1016   save();
1017
1018   $main::lxdebug->leave_sub();
1019 }
1020
1021 sub e_mail {
1022   $main::lxdebug->enter_sub();
1023
1024   check_do_access();
1025
1026   $::form->mtime_ischanged('delivery_orders','mail');
1027
1028   $::form->{print_and_save} = 1;
1029
1030   my $saved_form = save_form();
1031
1032   save();
1033
1034   restore_form($saved_form, 0, qw(id ordnumber quonumber));
1035
1036   edit_e_mail();
1037
1038   $main::lxdebug->leave_sub();
1039 }
1040
1041 sub calculate_stock_in_out {
1042   $main::lxdebug->enter_sub();
1043
1044   my $form     = $main::form;
1045
1046   my $i = shift;
1047
1048   if (!$form->{"id_${i}"}) {
1049     $main::lxdebug->leave_sub();
1050     return '';
1051   }
1052
1053   my $all_units = AM->retrieve_all_units();
1054
1055   my $in_out   = $form->{type} =~ /^sales/ ? 'out' : 'in';
1056   my $sinfo    = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_${i}"});
1057
1058   my $do_qty   = AM->sum_with_unit($::form->{"qty_$i"}, $::form->{"unit_$i"});
1059   my $sum      = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $sinfo });
1060   my $matches  = $do_qty == $sum;
1061
1062   my $content  = $form->format_amount_units('amount'      => $sum * 1,
1063                                             'part_unit'   => $form->{"partunit_$i"},
1064                                             'amount_unit' => $all_units->{$form->{"partunit_$i"}}->{base_unit},
1065                                             'conv_units'  => 'convertible_not_smaller',
1066                                             'max_places'  => 2);
1067   $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="?">|;
1068
1069   $main::lxdebug->leave_sub();
1070
1071   return $content;
1072 }
1073
1074 sub get_basic_bin_wh_info {
1075   $main::lxdebug->enter_sub();
1076
1077   my $stock_info = shift;
1078
1079   my $form     = $main::form;
1080
1081   foreach my $sinfo (@{ $stock_info }) {
1082     next unless ($sinfo->{bin_id});
1083
1084     my $bin_info = WH->get_basic_bin_info('id' => $sinfo->{bin_id});
1085     map { $sinfo->{"${_}_description"} = $sinfo->{"${_}description"} = $bin_info->{"${_}_description"} } qw(bin warehouse);
1086   }
1087
1088   $main::lxdebug->leave_sub();
1089 }
1090
1091 sub stock_in_out_form {
1092   $main::lxdebug->enter_sub();
1093
1094   my $form     = $main::form;
1095
1096   if ($form->{in_out} eq 'out') {
1097     stock_out_form();
1098   } else {
1099     stock_in_form();
1100   }
1101
1102   $main::lxdebug->leave_sub();
1103 }
1104
1105 sub redo_stock_info {
1106   $main::lxdebug->enter_sub();
1107
1108   my %params    = @_;
1109
1110   my $form     = $main::form;
1111
1112   my @non_empty = grep { $_->{qty} } @{ $params{stock_info} };
1113
1114   if ($params{add_empty_row}) {
1115     push @non_empty, {
1116       'warehouse_id' => scalar(@non_empty) ? $non_empty[-1]->{warehouse_id} : undef,
1117       'bin_id'       => scalar(@non_empty) ? $non_empty[-1]->{bin_id}       : undef,
1118     };
1119   }
1120
1121   @{ $params{stock_info} } = @non_empty;
1122
1123   $main::lxdebug->leave_sub();
1124 }
1125
1126 sub update_stock_in {
1127   $main::lxdebug->enter_sub();
1128
1129   my $form     = $main::form;
1130   my %myconfig = %main::myconfig;
1131
1132   my $stock_info = [];
1133
1134   foreach my $i (1..$form->{rowcount}) {
1135     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
1136     push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(warehouse_id bin_id chargenumber
1137                                                                    bestbefore qty unit delivery_order_items_stock_id) };
1138   }
1139
1140   display_stock_in_form($stock_info);
1141
1142   $main::lxdebug->leave_sub();
1143 }
1144
1145 sub stock_in_form {
1146   $main::lxdebug->enter_sub();
1147
1148   my $form     = $main::form;
1149
1150   my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
1151
1152   display_stock_in_form($stock_info);
1153
1154   $main::lxdebug->leave_sub();
1155 }
1156
1157 sub display_stock_in_form {
1158   $main::lxdebug->enter_sub();
1159
1160   my $stock_info = shift;
1161
1162   my $form     = $main::form;
1163   my %myconfig = %main::myconfig;
1164   my $locale   = $main::locale;
1165
1166   $form->{title} = $locale->text('Stock');
1167
1168   my $part_info  = IC->get_basic_part_info('id' => $form->{parts_id});
1169
1170   # Standardlagerplatz für Standard-Auslagern verwenden, falls keiner für die Ware explizit definiert wurde
1171   if ($::instance_conf->get_transfer_default_use_master_default_bin) {
1172     $part_info->{warehouse_id} ||= $::instance_conf->get_warehouse_id;
1173     $part_info->{bin_id}       ||= $::instance_conf->get_bin_id;
1174   }
1175
1176   my $units      = AM->retrieve_units(\%myconfig, $form);
1177   # der zweite Parameter von unit_select_data gibt den default-Namen (selected) vor
1178   my $units_data = AM->unit_select_data($units, $form->{do_unit}, undef, $part_info->{unit});
1179
1180   $form->get_lists('warehouses' => { 'key'    => 'WAREHOUSES',
1181                                      'bins'   => 'BINS' });
1182
1183   redo_stock_info('stock_info' => $stock_info, 'add_empty_row' => !$form->{delivered});
1184
1185   get_basic_bin_wh_info($stock_info);
1186
1187   $form->header(no_layout => 1);
1188   print $form->parse_html_template('do/stock_in_form', { 'UNITS'      => $units_data,
1189                                                          'STOCK_INFO' => $stock_info,
1190                                                          'PART_INFO'  => $part_info, });
1191
1192   $main::lxdebug->leave_sub();
1193 }
1194
1195 sub _stock_in_out_set_qty_display {
1196   my $stock_info       = shift;
1197   my $form             = $::form;
1198   my $all_units        = AM->retrieve_all_units();
1199   my $sum              = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
1200   $form->{qty_display} = $form->format_amount_units(amount      => $sum * 1,
1201                                                     part_unit   => $form->{partunit},
1202                                                     amount_unit => $all_units->{ $form->{partunit} }->{base_unit},
1203                                                     conv_units  => 'convertible_not_smaller',
1204                                                     max_places  => 2);
1205 }
1206
1207 sub set_stock_in {
1208   $main::lxdebug->enter_sub();
1209
1210   my $form     = $main::form;
1211   my %myconfig = %main::myconfig;
1212
1213   my $stock_info = [];
1214
1215   foreach my $i (1..$form->{rowcount}) {
1216     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
1217
1218     next if ($form->{"qty_$i"} <= 0);
1219
1220     push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(delivery_order_items_stock_id warehouse_id bin_id chargenumber bestbefore qty unit) };
1221   }
1222
1223   $form->{stock} = YAML::Dump($stock_info);
1224
1225   _stock_in_out_set_qty_display($stock_info);
1226
1227   my $do_qty       = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
1228   my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
1229
1230   $form->header();
1231   print $form->parse_html_template('do/set_stock_in_out', {
1232     qty_matches => $do_qty == $transfer_qty,
1233   });
1234
1235   $main::lxdebug->leave_sub();
1236 }
1237
1238 sub stock_out_form {
1239   $main::lxdebug->enter_sub();
1240
1241   my $form     = $main::form;
1242   my %myconfig = %main::myconfig;
1243   my $locale   = $main::locale;
1244
1245   $form->{title} = $locale->text('Release From Stock');
1246
1247   my $part_info  = IC->get_basic_part_info('id' => $form->{parts_id});
1248
1249   my $units      = AM->retrieve_units(\%myconfig, $form);
1250   my $units_data = AM->unit_select_data($units, undef, undef, $part_info->{unit});
1251
1252   my @contents   = DO->get_item_availability('parts_id' => $form->{parts_id});
1253
1254   my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
1255
1256   if (!$form->{delivered}) {
1257     foreach my $row (@contents) {
1258       $row->{available_qty} = $form->format_amount_units('amount'      => $row->{qty} * 1,
1259                                                          'part_unit'   => $part_info->{unit},
1260                                                          'conv_units'  => 'convertible_not_smaller',
1261                                                          'max_places'  => 2);
1262
1263       foreach my $sinfo (@{ $stock_info }) {
1264         next if (($row->{bin_id}       != $sinfo->{bin_id}) ||
1265                  ($row->{warehouse_id} != $sinfo->{warehouse_id}) ||
1266                  ($row->{chargenumber} ne $sinfo->{chargenumber}) ||
1267                  ($row->{bestbefore}   ne $sinfo->{bestbefore}));
1268
1269         map { $row->{"stock_$_"} = $sinfo->{$_} } qw(qty unit error delivery_order_items_stock_id);
1270       }
1271     }
1272
1273   } else {
1274     get_basic_bin_wh_info($stock_info);
1275
1276     foreach my $sinfo (@{ $stock_info }) {
1277       map { $sinfo->{"stock_$_"} = $sinfo->{$_} } qw(qty unit);
1278     }
1279   }
1280
1281   $form->header(no_layout => 1);
1282   print $form->parse_html_template('do/stock_out_form', { 'UNITS'      => $units_data,
1283                                                           'WHCONTENTS' => $form->{delivered} ? $stock_info : \@contents,
1284                                                           'PART_INFO'  => $part_info, });
1285
1286   $main::lxdebug->leave_sub();
1287 }
1288
1289 sub set_stock_out {
1290   $main::lxdebug->enter_sub();
1291
1292   my $form     = $main::form;
1293   my %myconfig = %main::myconfig;
1294   my $locale   = $main::locale;
1295
1296   my $stock_info = [];
1297
1298   foreach my $i (1 .. $form->{rowcount}) {
1299     $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
1300
1301     next if ($form->{"qty_$i"} <= 0);
1302
1303     push @{ $stock_info }, {
1304       'warehouse_id' => $form->{"warehouse_id_$i"},
1305       'bin_id'       => $form->{"bin_id_$i"},
1306       'chargenumber' => $form->{"chargenumber_$i"},
1307       'bestbefore'   => $form->{"bestbefore_$i"},
1308       'qty'          => $form->{"qty_$i"},
1309       'unit'         => $form->{"unit_$i"},
1310       'row'          => $i,
1311       'delivery_order_items_stock_id'  => $form->{"delivery_order_items_stock_id_$i"},
1312     };
1313   }
1314
1315   my @errors     = DO->check_stock_availability('requests' => $stock_info,
1316                                                 'parts_id' => $form->{parts_id});
1317
1318   $form->{stock} = YAML::Dump($stock_info);
1319
1320   if (@errors) {
1321     $form->{ERRORS} = [];
1322     map { push @{ $form->{ERRORS} }, $locale->text('Error in row #1: The quantity you entered is bigger than the stocked quantity.', $_->{row}); } @errors;
1323     stock_in_out_form();
1324
1325   } else {
1326     _stock_in_out_set_qty_display($stock_info);
1327
1328     my $do_qty       = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
1329     my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
1330
1331     $form->header();
1332     print $form->parse_html_template('do/set_stock_in_out', {
1333       qty_matches => $do_qty == $transfer_qty,
1334     });
1335   }
1336
1337   $main::lxdebug->leave_sub();
1338 }
1339
1340 sub transfer_in {
1341   $main::lxdebug->enter_sub();
1342
1343   my $form     = $main::form;
1344   my %myconfig = %main::myconfig;
1345   my $locale   = $main::locale;
1346
1347   if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
1348     $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred in.'), 'back_button' => 1);
1349   }
1350
1351   save(no_redirect => 1);
1352
1353   my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_in_${_}"} } (1 .. $form->{rowcount});
1354   my @all_requests;
1355
1356   if (@part_ids) {
1357     my $units         = AM->retrieve_units(\%myconfig, $form);
1358     my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
1359     my %request_map;
1360
1361     $form->{ERRORS}   = [];
1362
1363     foreach my $i (1 .. $form->{rowcount}) {
1364       next unless ($form->{"id_$i"} && $form->{"stock_in_$i"});
1365
1366       my $row_sum_base_qty = 0;
1367       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
1368
1369       foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_in_$i"}) }) {
1370         $request->{parts_id}  = $form->{"id_$i"};
1371         $row_sum_base_qty    += $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
1372
1373         $request->{project_id} = $form->{"project_id_$i"} || $form->{globalproject_id};
1374
1375         push @all_requests, $request;
1376       }
1377
1378       next if (0 == $row_sum_base_qty);
1379
1380       my $do_base_qty = $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
1381
1382 #      if ($do_base_qty != $row_sum_base_qty) {
1383 #        push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no stock at all or the full quantity of #2 #3.',
1384 #                                                 $i, $form->{"qty_$i"}, $form->{"unit_$i"});
1385 #      }
1386     }
1387
1388     if (@{ $form->{ERRORS} }) {
1389       push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
1390
1391       set_headings('edit');
1392       update();
1393       $main::lxdebug->leave_sub();
1394
1395       ::end_of_request();
1396     }
1397   }
1398
1399   DO->transfer_in_out('direction' => 'in',
1400                       'requests'  => \@all_requests);
1401
1402   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
1403
1404   $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id});
1405   $form->redirect;
1406
1407   $main::lxdebug->leave_sub();
1408 }
1409
1410 sub transfer_out {
1411   $main::lxdebug->enter_sub();
1412
1413   my $form     = $main::form;
1414   my %myconfig = %main::myconfig;
1415   my $locale   = $main::locale;
1416
1417   if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
1418     $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred out.'), 'back_button' => 1);
1419   }
1420
1421   save(no_redirect => 1);
1422
1423   my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_out_${_}"} } (1 .. $form->{rowcount});
1424   my @all_requests;
1425
1426   if (@part_ids) {
1427     my $units         = AM->retrieve_units(\%myconfig, $form);
1428     my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
1429     my %request_map;
1430
1431     $form->{ERRORS}   = [];
1432
1433     foreach my $i (1 .. $form->{rowcount}) {
1434       next unless ($form->{"id_$i"} && $form->{"stock_out_$i"});
1435
1436       my $row_sum_base_qty = 0;
1437       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
1438
1439       foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_out_$i"}) }) {
1440         $request->{parts_id} = $form->{"id_$i"};
1441         $request->{base_qty} = $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
1442         $request->{project_id} = $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id};
1443
1444         my $map_key          = join '--', ($form->{"id_$i"}, @{$request}{qw(warehouse_id bin_id chargenumber bestbefore)});
1445
1446         $request_map{$map_key}                 ||= $request;
1447         $request_map{$map_key}->{sum_base_qty} ||= 0;
1448         $request_map{$map_key}->{sum_base_qty}  += $request->{base_qty};
1449         $row_sum_base_qty                       += $request->{base_qty};
1450
1451         push @all_requests, $request;
1452       }
1453
1454       next if (0 == $row_sum_base_qty);
1455
1456       my $do_base_qty = $form->{"qty_$i"} * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
1457
1458 #      if ($do_base_qty != $row_sum_base_qty) {
1459 #        push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no transfer at all or the full quantity of #2 #3.',
1460 #                                                 $i, $form->{"qty_$i"}, $form->{"unit_$i"});
1461 #      }
1462     }
1463
1464     if (%request_map) {
1465       my @bin_ids      = map { $_->{bin_id} } values %request_map;
1466       my %bin_info_map = WH->get_basic_bin_info('id' => \@bin_ids);
1467       my @contents     = DO->get_item_availability('parts_id' => \@part_ids);
1468
1469       foreach my $inv (@contents) {
1470         my $map_key = join '--', @{$inv}{qw(parts_id warehouse_id bin_id chargenumber bestbefore)};
1471
1472         next unless ($request_map{$map_key});
1473
1474         my $request    = $request_map{$map_key};
1475         $request->{ok} = $request->{sum_base_qty} <= $inv->{qty};
1476       }
1477
1478       foreach my $request (values %request_map) {
1479         next if ($request->{ok});
1480
1481         my $pinfo = $part_info_map{$request->{parts_id}};
1482         my $binfo = $bin_info_map{$request->{bin_id}};
1483
1484         if ($::instance_conf->get_show_bestbefore) {
1485             push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, #5, for the transfer of #6.",
1486                                                      $pinfo->{description},
1487                                                      $binfo->{warehouse_description},
1488                                                      $binfo->{bin_description},
1489                                                      $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
1490                                                      $request->{bestbefore} ? $locale->text('bestbefore #1', $request->{bestbefore}) : $locale->text('no bestbefore'),
1491                                                      $form->format_amount_units('amount'      => $request->{sum_base_qty},
1492                                                                                 'part_unit'   => $pinfo->{unit},
1493                                                                                 'conv_units'  => 'convertible_not_smaller'));
1494         } else {
1495             push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, for the transfer of #5.",
1496                                                      $pinfo->{description},
1497                                                      $binfo->{warehouse_description},
1498                                                      $binfo->{bin_description},
1499                                                      $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
1500                                                      $form->format_amount_units('amount'      => $request->{sum_base_qty},
1501                                                                                 'part_unit'   => $pinfo->{unit},
1502                                                                                 'conv_units'  => 'convertible_not_smaller'));
1503         }
1504       }
1505     }
1506
1507     if (@{ $form->{ERRORS} }) {
1508       push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
1509
1510       set_headings('edit');
1511       update();
1512       $main::lxdebug->leave_sub();
1513
1514       ::end_of_request();
1515     }
1516   }
1517   DO->transfer_in_out('direction' => 'out',
1518                       'requests'  => \@all_requests);
1519
1520   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
1521
1522   $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id});
1523   $form->redirect;
1524
1525   $main::lxdebug->leave_sub();
1526 }
1527
1528 sub mark_closed {
1529   $main::lxdebug->enter_sub();
1530
1531   my $form     = $main::form;
1532
1533   DO->close_orders('ids' => [ $form->{id} ]);
1534
1535   $form->{closed} = 1;
1536
1537   update();
1538
1539   $main::lxdebug->leave_sub();
1540 }
1541
1542
1543 sub yes {
1544   call_sub($main::form->{yes_nextsub});
1545 }
1546
1547 sub no {
1548   call_sub($main::form->{no_nextsub});
1549 }
1550
1551 sub update {
1552   call_sub($main::form->{update_nextsub} || $main::form->{nextsub} || 'update_delivery_order');
1553 }
1554
1555 sub dispatcher {
1556   my $form     = $main::form;
1557   my $locale   = $main::locale;
1558
1559   foreach my $action (qw(update ship_to print e_mail save transfer_out transfer_out_default sort
1560                          transfer_in transfer_in_default mark_closed save_as_new invoice delete)) {
1561     if ($form->{"action_${action}"}) {
1562       call_sub($action);
1563       return;
1564     }
1565   }
1566
1567   $form->error($locale->text('No action defined.'));
1568 }
1569
1570 sub transfer_out_default {
1571   $main::lxdebug->enter_sub();
1572
1573   my $form     = $main::form;
1574
1575   transfer_in_out_default('direction' => 'out');
1576
1577   $main::lxdebug->leave_sub();
1578 }
1579
1580 sub transfer_in_default {
1581   $main::lxdebug->enter_sub();
1582
1583   my $form     = $main::form;
1584
1585   transfer_in_out_default('direction' => 'in');
1586
1587   $main::lxdebug->leave_sub();
1588 }
1589
1590 # Falls das Standardlagerverfahren aktiv ist, wird
1591 # geprüft, ob alle Standardlagerplätze für die Auslager-
1592 # artikel vorhanden sind UND ob die Warenmenge ausreicht zum
1593 # Auslagern. Falls nicht wird entsprechend eine Fehlermeldung
1594 # generiert. Offen Chargennummer / bestbefore wird nicht berücksichtigt
1595 sub transfer_in_out_default {
1596   $main::lxdebug->enter_sub();
1597
1598   my $form     = $main::form;
1599   my %myconfig = %main::myconfig;
1600   my $locale   = $main::locale;
1601   my %params   = @_;
1602
1603   my (%missing_default_bins, %qty_parts, @all_requests, %part_info_map, $default_warehouse_id, $default_bin_id);
1604
1605   Common::check_params(\%params, qw(direction));
1606
1607   # entsprechende defaults holen, falls standardlagerplatz verwendet werden soll
1608   if ($::instance_conf->get_transfer_default_use_master_default_bin) {
1609     $default_warehouse_id = $::instance_conf->get_warehouse_id;
1610     $default_bin_id       = $::instance_conf->get_bin_id;
1611   }
1612
1613
1614   my @part_ids = map { $form->{"id_${_}"} } (1 .. $form->{rowcount});
1615   if (@part_ids) {
1616     my $units         = AM->retrieve_units(\%myconfig, $form);
1617     %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
1618     foreach my $i (1 .. $form->{rowcount}) {
1619       next unless ($form->{"id_$i"});
1620       my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
1621       my $qty =   $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
1622
1623       $form->show_generic_error($locale->text("Cannot transfer negative entries." ), 'back_button' => 1) if ($qty < 0);
1624       # if we do not want to transfer services and this part is a service, set qty to zero
1625       # ... and do not create a hash entry in %qty_parts below (will skip check for bins for the transfer == out case)
1626       # ... 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)
1627
1628       $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});
1629       $qty_parts{$form->{"id_$i"}} += $qty;
1630       if ($qty == 0) {
1631         delete $qty_parts{$form->{"id_$i"}} unless $qty_parts{$form->{"id_$i"}};
1632         undef $form->{"stock_in_$i"};
1633       }
1634
1635       $part_info_map{$form->{"id_$i"}}{bin_id}       ||= $default_bin_id;
1636       $part_info_map{$form->{"id_$i"}}{warehouse_id} ||= $default_warehouse_id;
1637
1638       push @all_requests, ($qty == 0) ? { } : {
1639                         'chargenumber' => '',  #?? die müsste entsprechend geholt werden
1640                         #'bestbefore' => undef, # TODO wird nicht berücksichtigt
1641                         'bin_id' => $part_info_map{$form->{"id_$i"}}{bin_id},
1642                         'qty' => $qty,
1643                         'parts_id' => $form->{"id_$i"},
1644                         'comment' => $locale->text("Default transfer delivery order"),
1645                         'unit' => $part_info_map{$form->{"id_$i"}}{unit},
1646                         'warehouse_id' => $part_info_map{$form->{"id_$i"}}{warehouse_id},
1647                         'oe_id' => $form->{id},
1648                         'project_id' => $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id}
1649                       };
1650     }
1651
1652     # jetzt wird erst überprüft, ob die Stückzahl entsprechend stimmt.
1653     # check if bin (transfer in and transfer out and qty (transfer out) is correct
1654     foreach my $key (keys %qty_parts) {
1655
1656       $missing_default_bins{$key}{missing_bin} = 1 unless ($part_info_map{$key}{bin_id});
1657       next unless ($part_info_map{$key}{bin_id}); # abbruch
1658
1659       if ($params{direction} eq 'out') {  # wird nur für ausgehende Mengen benötigt
1660         my ($max_qty, $error) = WH->get_max_qty_parts_bin(parts_id => $key, bin_id => $part_info_map{$key}{bin_id});
1661         if ($error == 1) {
1662           # wir können nicht entscheiden, welche charge oder mhd (bestbefore) ausgewählt sein soll
1663           # deshalb rückmeldung nach oben geben, manuell auszulagern
1664           # TODO Bei nur einem Treffer mit Charge oder bestbefore wäre das noch möglich
1665           $missing_default_bins{$key}{chargenumber} = 1;
1666         }
1667         if ($max_qty < $qty_parts{$key}){
1668           $missing_default_bins{$key}{missing_qty} = $max_qty - $qty_parts{$key};
1669         }
1670       }
1671     }
1672   } # if @parts_id
1673
1674   # Abfrage für Fehlerbehandlung (nur bei direction == out)
1675   if (scalar (keys %missing_default_bins)) {
1676     my $fehlertext;
1677     foreach my $fehler (keys %missing_default_bins) {
1678
1679       my $ware = WH->get_part_description(parts_id => $fehler);
1680       if ($missing_default_bins{$fehler}{missing_bin}){
1681         $fehlertext .= "Kein Standardlagerplatz definiert bei $ware <br>";
1682       }
1683       if ($missing_default_bins{$fehler}{missing_qty}) {  # missing_qty
1684         $fehlertext .= "Es fehlen " . $missing_default_bins{$fehler}{missing_qty}*-1 .
1685                        " von $ware auf dem Standard-Lagerplatz " . $part_info_map{$fehler}{bin} .   " zum Auslagern<br>";
1686       }
1687       if ($missing_default_bins{$fehler}{chargenumber}){
1688         $fehlertext .= "Die Ware hat eine Chargennummer oder eine Mindesthaltbarkeit definiert.
1689                         Hier kann man nicht automatisch entscheiden.
1690                         Bitte diesen Lieferschein manuell auslagern.
1691                         Bei: $ware";
1692       }
1693       # auslagern soll immer gehen, auch wenn nicht genügend auf lager ist.
1694       # der lagerplatz ist hier extra konfigurierbar, bspw. Lager-Korrektur mit
1695       # Lagerplatz Lagerplatz-Korrektur
1696       my $default_warehouse_id_ignore_onhand = $::instance_conf->get_warehouse_id_ignore_onhand;
1697       my $default_bin_id_ignore_onhand       = $::instance_conf->get_bin_id_ignore_onhand;
1698       if ($::instance_conf->get_transfer_default_ignore_onhand && $default_bin_id_ignore_onhand) {
1699         # entsprechende defaults holen
1700         # falls chargenumber, bestbefore oder anzahl nicht stimmt, auf automatischen
1701         # lagerplatz wegbuchen!
1702         foreach (@all_requests) {
1703           if ($_->{parts_id} eq $fehler){
1704           $_->{bin_id}        = $default_bin_id_ignore_onhand;
1705           $_->{warehouse_id}  = $default_warehouse_id_ignore_onhand;
1706           }
1707         }
1708       } else {
1709         #$main::lxdebug->message(0, 'Fehlertext: ' . $fehlertext);
1710         $form->show_generic_error($locale->text("Cannot transfer. <br> Reason:<br>#1", $fehlertext ), 'back_button' => 1);
1711       }
1712     }
1713   }
1714
1715
1716   # hier der eigentliche fallunterschied für in oder out
1717   my $prefix   = $params{direction} eq 'in' ? 'in' : 'out';
1718
1719   # dieser array_ref ist für DO->save da:
1720   # einmal die all_requests in YAML verwandeln, damit delivery_order_items_stock
1721   # gefüllt werden kann.
1722   my $i = 0;
1723   foreach (@all_requests){
1724     $i++;
1725     next unless scalar(%{ $_ });
1726     $form->{"stock_${prefix}_$i"} = YAML::Dump([$_]);
1727   }
1728
1729   save(no_redirect => 1); # Wir können auslagern, deshalb beleg speichern
1730                           # und in delivery_order_items_stock speichern
1731   DO->transfer_in_out('direction' => $prefix,
1732                       'requests'  => \@all_requests);
1733
1734   SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
1735
1736   $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'out';
1737   $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'in';
1738   $form->redirect;
1739
1740 }
1741
1742 sub sort {
1743   $main::lxdebug->enter_sub();
1744
1745   check_do_access();
1746
1747   my $form     = $main::form;
1748   my %temp_hash;
1749
1750   croak ("Delivery Order needs to be saved") unless $form->{id};
1751
1752   # hashify partnumbers, positions. key is delivery_order_items_id
1753   for my $i (1 .. ($form->{rowcount}) ) {
1754     $temp_hash{$form->{"delivery_order_items_id_$i"}} = { runningnumber => $form->{"runningnumber_$i"}, partnumber => $form->{"partnumber_$i"} };
1755   }
1756   # naturally sort partnumbers and get a sorted array of doi_ids
1757   my @sorted_doi_ids =  sort { Sort::Naturally::ncmp($temp_hash{$a}->{"partnumber"}, $temp_hash{$b}->{"partnumber"}) }  keys %temp_hash;
1758
1759
1760   my $new_number = 1;
1761
1762   for (@sorted_doi_ids) {
1763     $form->{"runningnumber_$temp_hash{$_}->{runningnumber}"} = $new_number;
1764     $new_number++;
1765   }
1766     $main::lxdebug->leave_sub();
1767     save();
1768 }
1769
1770 __END__
1771
1772 =pod
1773
1774 =encoding utf8
1775
1776 =head1 NAME
1777
1778 do.pl - Script for all calls to delivery order
1779
1780
1781 =head1 FUNCTIONS
1782
1783 =over 2
1784
1785 =item C<sort>
1786
1787 Sorts all position with Natural Sort. Can be activated in form_footer.html like this
1788 C<E<lt>input class="submit" type="submit" name="action_sort" id="sort_button" value="[% 'Sort and Save' | $T8 %]"E<gt>>
1789
1790 =back
1791
1792 =head1 TODO
1793
1794 Sort and Save can be implemented as an optional button if configuration ca be set by client config.
1795 Example coding for database scripts and templates in (git show af2f24b8), check also
1796 autogeneration for rose (scripts/rose_auto_create_model.pl --h)