1 #=====================================================================
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
7 #=====================================================================
8 # SQL-Ledger, Accounting
9 # Copyright (c) 1998-2003
11 # Author: Dieter Simader
12 # Email: dsimader@sql-ledger.org
13 # Web: http://www.sql-ledger.org
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
29 #======================================================================
32 #======================================================================
35 use List::MoreUtils qw(uniq);
36 use List::Util qw(max sum);
37 use POSIX qw(strftime);
39 use SL::Controller::DeliveryOrder;
40 use SL::DB::DeliveryOrder;
41 use SL::DB::DeliveryOrder::TypeData qw(:types validate_type);
42 use SL::Helper::UserPreferences::DisplayPreferences;
46 use SL::MoreCommon qw(ary_diff restore_form save_form);
47 use SL::ReportGenerator;
50 use Sort::Naturally ();
51 require "bin/mozilla/common.pl";
52 require "bin/mozilla/io.pl";
53 require "bin/mozilla/reportgenerator.pl";
61 sub check_do_access_for_edit {
62 validate_type($::form->{type});
64 my $right = SL::DB::DeliveryOrder::TypeData::get3($::form->{type}, "rights", "edit");
65 $main::auth->assert($right);
69 validate_type($::form->{type});
71 my $right = SL::DB::DeliveryOrder::TypeData::get3($::form->{type}, "rights", "view");
72 $main::auth->assert($right);
76 $main::lxdebug->enter_sub();
82 my $form = $main::form;
83 my $locale = $main::locale;
85 if ($form->{type} eq 'purchase_delivery_order') {
86 $form->{vc} = 'vendor';
87 $form->{title} = $action eq "edit" ? $locale->text('Edit Purchase Delivery Order') : $locale->text('Add Purchase Delivery Order');
89 $form->{vc} = 'customer';
90 $form->{title} = $action eq "edit" ? $locale->text('Edit Sales Delivery Order') : $locale->text('Add Sales Delivery Order');
93 $form->{heading} = $locale->text('Delivery Order');
95 $main::lxdebug->leave_sub();
99 $main::lxdebug->enter_sub();
101 check_do_access_for_edit();
103 if (($::form->{type} =~ /purchase/) && !$::instance_conf->get_allow_new_purchase_invoice) {
104 $::form->show_generic_error($::locale->text("You do not have the permissions to access this function."));
107 my $form = $main::form;
111 $form->{show_details} = $::myconfig{show_form_details};
112 $form->{callback} = build_std_url('action=add', 'type', 'vc') unless ($form->{callback});
114 order_links(is_new => 1);
118 $main::lxdebug->leave_sub();
122 $main::lxdebug->enter_sub();
126 my $form = $main::form;
128 $form->{show_details} = $::myconfig{show_form_details};
130 # show history button
131 $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
132 #/show hhistory button
134 $form->{simple_save} = 0;
136 set_headings("edit");
138 # editing without stuff to edit? try adding it first
139 if ($form->{rowcount} && !$form->{print_and_save}) {
140 # map { $id++ if $form->{"multi_id_$_"} } (1 .. $form->{rowcount});
144 undef $form->{rowcount};
146 $main::lxdebug->leave_sub();
149 } elsif (!$form->{id}) {
151 $main::lxdebug->leave_sub();
155 my ($language_id, $printer_id);
156 if ($form->{print_and_save}) {
157 $form->{action} = "dispatcher";
158 $form->{action_print} = "1";
159 $form->{resubmit} = 1;
160 $language_id = $form->{language_id};
161 $printer_id = $form->{printer_id};
164 set_headings("edit");
169 if ($form->{print_and_save}) {
170 $form->{language_id} = $language_id;
171 $form->{printer_id} = $printer_id;
176 $main::lxdebug->leave_sub();
180 $main::lxdebug->enter_sub();
185 my $form = $main::form;
186 my %myconfig = %main::myconfig;
188 # retrieve order/quotation
189 my $editing = $form->{id};
191 DO->retrieve('vc' => $form->{vc},
192 'ids' => $form->{id});
194 $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes delivery_term_id currency));
196 # get customer / vendor
197 if ($form->{vc} eq 'vendor') {
198 IR->get_vendor(\%myconfig, \%$form);
199 $form->{discount} = $form->{vendor_discount};
201 IS->get_customer(\%myconfig, \%$form);
202 $form->{discount} = $form->{customer_discount};
203 $form->{billing_address_id} = $form->{default_billing_address_id} if $params{is_new};
206 $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id delivery_term_id));
207 $form->restore_vars(qw(currency)) if ($form->{id} || $form->{convert_from_oe_ids});
208 $form->restore_vars(qw(taxincluded)) if $form->{id};
209 $form->restore_vars(qw(salesman_id)) if $editing;
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();
254 sub setup_do_action_bar {
255 my @transfer_qty = qw(kivi.SalesPurchase.delivery_order_check_transfer_qty);
256 my @req_trans_desc = qw(kivi.SalesPurchase.check_transaction_description) x!!$::instance_conf->get_require_transaction_description_ps;
257 my $is_customer = $::form->{vc} eq 'customer';
259 my $undo_date = DateTime->today->subtract(days => $::instance_conf->get_undo_transfer_interval);
260 my $insertdate = DateTime->from_kivitendo($::form->{insertdate});
261 my $undo_transfer = 0;
262 if (ref $undo_date eq 'DateTime' && ref $insertdate eq 'DateTime') {
263 $undo_transfer = $insertdate > $undo_date;
266 my $may_edit_create = $::auth->assert(SL::DB::DeliveryOrder::TypeData::get3($::form->{type}, "rights", "edit"), 1);
268 for my $bar ($::request->layout->get('actionbar')) {
272 submit => [ '#form', { action => "update" } ],
273 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.') : undef,
274 id => 'update_button',
275 accesskey => 'enter',
281 submit => [ '#form', { action => "save" } ],
282 checks => [ 'kivi.validate_form' ],
283 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
284 : $::form->{delivered} ? t8('This record has already been delivered.')
289 submit => [ '#form', { action => "save_as_new" } ],
290 checks => [ 'kivi.validate_form' ],
291 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
295 t8('Mark as closed'),
296 submit => [ '#form', { action => "mark_closed" } ],
297 checks => [ 'kivi.validate_form' ],
298 confirm => t8('This will remove the delivery order from showing as open even if contents are not delivered. Proceed?'),
299 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
300 : !$::form->{id} ? t8('This record has not been saved yet.')
301 : $::form->{closed} ? t8('This record has already been closed.')
304 ], # end of combobox "Save"
308 submit => [ '#form', { action => "delete" } ],
309 confirm => t8('Do you really want to delete this object?'),
310 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
311 : !$::form->{id} ? t8('This record has not been saved yet.')
312 : $::form->{delivered} ? t8('This record has already been delivered.')
313 : ($::form->{vc} eq 'customer' && !$::instance_conf->get_sales_delivery_order_show_delete) ? t8('Deleting this type of record has been disabled in the configuration.')
314 : ($::form->{vc} eq 'vendor' && !$::instance_conf->get_purchase_delivery_order_show_delete) ? t8('Deleting this type of record has been disabled in the configuration.')
321 submit => [ '#form', { action => "transfer_out" } ],
322 checks => [ 'kivi.validate_form', @transfer_qty ],
323 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
324 : $::form->{delivered} ? t8('This record has already been delivered.')
326 only_if => $is_customer,
329 t8('Transfer out via default'),
330 submit => [ '#form', { action => "transfer_out_default" } ],
331 checks => [ 'kivi.validate_form' ],
332 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
333 : $::form->{delivered} ? t8('This record has already been delivered.')
335 only_if => $is_customer && $::instance_conf->get_transfer_default,
339 submit => [ '#form', { action => "transfer_in" } ],
340 checks => [ 'kivi.validate_form', @transfer_qty ],
341 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
342 : $::form->{delivered} ? t8('This record has already been delivered.')
344 only_if => !$is_customer,
347 t8('Transfer in via default'),
348 submit => [ '#form', { action => "transfer_in_default" } ],
349 checks => [ 'kivi.validate_form' ],
350 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
351 : $::form->{delivered} ? t8('This record has already been delivered.')
353 only_if => !$is_customer && $::instance_conf->get_transfer_default,
357 submit => [ '#form', { action => "delete_transfers" } ],
358 checks => [ 'kivi.validate_form' ],
359 only_if => $::form->{delivered},
360 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
361 : !$undo_transfer ? t8('Transfer date exceeds the maximum allowed interval.')
364 ], # end of combobox "Transfer out"
371 submit => [ '#form', { action => "invoice" } ],
372 disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
373 confirm => $::form->{delivered} ? undef
374 : ($::form->{vc} eq 'customer' && $::instance_conf->get_sales_delivery_order_check_stocked) ? t8('This record has not been stocked out. Proceed?')
375 : ($::form->{vc} eq 'vendor' && $::instance_conf->get_purchase_delivery_order_check_stocked) ? t8('This record has not been stocked in. Proceed?')
380 action => [ t8('Export') ],
383 call => [ 'kivi.SalesPurchase.show_print_dialog' ],
384 checks => [ 'kivi.validate_form' ],
385 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.') : undef,
389 call => [ 'kivi.SalesPurchase.show_email_dialog' ],
390 checks => [ 'kivi.validate_form' ],
391 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
392 : !$::form->{id} ? t8('This record has not been saved yet.')
395 ], # end of combobox "Export"
398 action => [ t8('more') ],
401 call => [ 'set_history_window', $::form->{id} * 1, 'id' ],
402 disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
406 call => [ 'follow_up_window' ],
407 disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
409 ], # end if combobox "more"
412 $::request->layout->add_javascripts('kivi.Validator.js');
415 sub setup_do_search_action_bar {
418 for my $bar ($::request->layout->get('actionbar')) {
422 submit => [ '#form' ],
423 accesskey => 'enter',
424 checks => [ 'kivi.validate_form' ],
428 $::request->layout->add_javascripts('kivi.Validator.js');
431 sub setup_do_orders_action_bar {
434 for my $bar ($::request->layout->get('actionbar')) {
438 submit => [ '#form', { action => 'invoice_multi' } ],
439 checks => [ [ 'kivi.check_if_entries_selected', '#form tbody input[type=checkbox]' ] ],
440 accesskey => 'enter',
444 call => [ 'kivi.SalesPurchase.show_print_dialog', 'js:kivi.MassDeliveryOrderPrint.submitMultiOrders' ],
445 checks => [ [ 'kivi.check_if_entries_selected', '#form tbody input[type=checkbox]' ] ],
452 $main::lxdebug->enter_sub();
456 my $form = $main::form;
457 my %myconfig = %main::myconfig;
459 my $class = "SL::DB::" . ($form->{vc} eq 'customer' ? 'Customer' : 'Vendor');
460 $form->{VC_OBJ} = $class->load_cached($form->{ $form->{vc} . '_id' });
462 $form->{CONTACT_OBJ} = $form->{cp_id} ? SL::DB::Contact->load_cached($form->{cp_id}) : undef;
463 my $current_employee = SL::DB::Manager::Employee->current;
464 $form->{employee_id} = $form->{old_employee_id} if $form->{old_employee_id};
465 $form->{salesman_id} = $form->{old_salesman_id} if $form->{old_salesman_id};
466 $form->{employee_id} ||= $current_employee->id;
467 $form->{salesman_id} ||= $current_employee->id;
469 my $vc = $form->{vc} eq "customer" ? "customers" : "vendors";
470 $form->get_lists("price_factors" => "ALL_PRICE_FACTORS",
471 "business_types" => "ALL_BUSINESS_TYPES",
473 $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all_sorted;
474 $form->{ALL_LANGUAGES} = SL::DB::Manager::Language->get_all_sorted;
477 my @old_project_ids = uniq grep { $_ } map { $_ * 1 } ($form->{"globalproject_id"}, map { $form->{"project_id_$_"} } 1..$form->{"rowcount"});
478 my @old_ids_cond = @old_project_ids ? (id => \@old_project_ids) : ();
480 if (($vc eq 'customers') && $::instance_conf->get_customer_projects_only_in_sales) {
483 customer_id => $::form->{customer_id},
484 billable_customer_id => $::form->{customer_id},
489 and => [ active => 1, @customer_cond ],
493 $::form->{ALL_PROJECTS} = SL::DB::Manager::Project->get_all_sorted(query => \@conditions);
494 $::form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{employee_id}, deleted => 0 ] ]);
495 $::form->{ALL_SALESMEN} = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{salesman_id}, deleted => 0 ] ]);
496 $::form->{ALL_SHIPTO} = SL::DB::Manager::Shipto->get_all_sorted(query => [
497 or => [ trans_id => $::form->{"$::form->{vc}_id"} * 1, and => [ shipto_id => $::form->{shipto_id} * 1, trans_id => undef ] ]
499 $::form->{ALL_CONTACTS} = SL::DB::Manager::Contact->get_all_sorted(query => [
501 cp_cv_id => $::form->{"$::form->{vc}_id"} * 1,
504 cp_id => $::form->{cp_id} * 1
509 my $dispatch_to_popup = '';
510 if ($form->{resubmit} && ($form->{format} eq "html")) {
511 $dispatch_to_popup = "window.open('about:blank','Beleg'); document.do.target = 'Beleg';";
512 $dispatch_to_popup .= "document.do.submit();";
513 } elsif ($form->{resubmit} && $form->{action_print}) {
514 # emulate click for resubmitting actions
515 $dispatch_to_popup = "kivi.SalesPurchase.show_print_dialog(); kivi.SalesPurchase.print_record();";
517 $::request->{layout}->add_javascripts_inline("\$(function(){$dispatch_to_popup});");
520 $form->{follow_up_trans_info} = $form->{donumber} .'('. $form->{VC_OBJ}->name .')' if $form->{VC_OBJ};
521 $form->{longdescription_dialog_size_percentage} = SL::Helper::UserPreferences::DisplayPreferences->new()->get_longdescription_dialog_size_percentage();
523 $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.File kivi.MassDeliveryOrderPrint kivi.SalesPurchase kivi.Part kivi.CustomerVendor kivi.Validator ckeditor/ckeditor ckeditor/adapters/jquery kivi.io));
525 setup_do_action_bar();
528 # Fix für Bug 1082 Erwartet wird: 'abteilungsNAME--abteilungsID'
529 # und Erweiterung für Bug 1760:
530 # Das war leider nur ein Teil-Fix, da das Verhalten den 'Erneuern'-Knopf
531 # nicht überlebt. Konsequent jetzt auf L umgestellt
532 # $ perldoc SL::Template::Plugin::L
533 # Daher entsprechend nur die Anpassung in form_header
534 # und in DO.pm gemacht. 4 Testfälle:
535 # department_id speichern | i.O.
536 # department_id lesen | i.O.
537 # department leer überlebt erneuern | i.O.
538 # department nicht leer überlebt erneuern | i.O.
539 # $main::lxdebug->message(0, 'ABTEILUNGS ID in form?' . $form->{department_id});
540 print $form->parse_html_template('do/form_header');
542 $main::lxdebug->leave_sub();
546 $main::lxdebug->enter_sub();
550 my $form = $main::form;
552 $form->{PRINT_OPTIONS} = setup_sales_purchase_print_options();
553 $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
555 my $shipto_cvars = SL::DB::Shipto->new->cvars_by_config;
556 foreach my $var (@{ $shipto_cvars }) {
557 my $name = "shiptocvar_" . $var->config->name;
558 $var->value($form->{$name}) if exists $form->{$name};
561 print $form->parse_html_template('do/form_footer',
562 {transfer_default => ($::instance_conf->get_transfer_default),
563 shipto_cvars => $shipto_cvars});
565 $main::lxdebug->leave_sub();
568 sub update_delivery_order {
569 $main::lxdebug->enter_sub();
573 my $form = $main::form;
574 my %myconfig = %main::myconfig;
576 set_headings($form->{"id"} ? "edit" : "add");
578 $form->{insertdate} = SL::DB::DeliveryOrder->new(id => $form->{id})->load->itime_as_date if $form->{id};
583 $payment_id = $form->{payment_id} if $form->{payment_id};
585 my $vc = $form->{vc};
586 if (($form->{"previous_${vc}_id"} || $form->{"${vc}_id"}) != $form->{"${vc}_id"}) {
587 $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
589 if ($vc eq 'customer') {
590 IS->get_customer(\%myconfig, $form);
591 $::form->{billing_address_id} = $::form->{default_billing_address_id};
593 IR->get_vendor(\%myconfig, $form);
597 $form->{discount} = $form->{"$form->{vc}_discount"} if defined $form->{"$form->{vc}_discount"};
598 # Problem: Wenn man ohne Erneuern einen Kunden/Lieferanten
599 # wechselt, wird der entsprechende Kunden/ Lieferantenrabatt
600 # nicht übernommen. Grundproblem: In Commit 82574e78
601 # hab ich aus discount customer_discount und vendor_discount
602 # gemacht und entsprechend an den Oberflächen richtig hin-
603 # geschoben. Die damals bessere Lösung wäre gewesen:
604 # In den Templates nur die hidden für form-discount wieder ein-
605 # setzen dann wäre die Verrenkung jetzt nicht notwendig.
606 # TODO: Ggf. Bugfix 1284, 1575 und 817 wieder zusammenführen
607 # Testfälle: Kunden mit Rabatt 0 -> Rabatt 20 i.O.
608 # Kunde mit Rabatt 20 -> Rabatt 0 i.O.
609 # Kunde mit Rabatt 20 -> Rabatt 5,5 i.O.
610 $form->{payment_id} = $payment_id if $form->{payment_id} eq "";
612 my $i = $form->{rowcount};
614 if ( ($form->{"partnumber_$i"} eq "")
615 && ($form->{"description_$i"} eq "")
616 && ($form->{"partsgroup_$i"} eq "")) {
623 if ($form->{type} eq 'purchase_delivery_order') {
624 IR->retrieve_item(\%myconfig, $form);
627 IS->retrieve_item(\%myconfig, $form);
631 my $rows = scalar @{ $form->{item_list} };
634 $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
635 if( !$form->{"qty_$i"} ) {
636 $form->{"qty_$i"} = 1;
641 select_item(mode => $mode, pre_entered_qty => $form->{"qty_$i"});
642 $::dispatcher->end_request;
646 my $sellprice = $form->parse_amount(\%myconfig, $form->{"sellprice_$i"});
648 map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} } keys %{ $form->{item_list}[0] };
650 $form->{"marge_price_factor_$i"} = $form->{item_list}->[0]->{price_factor};
653 $form->{"sellprice_$i"} = $sellprice;
655 my $record = _make_record();
656 my $price_source = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
657 my $best_price = $price_source->best_price;
658 my $best_discount = $price_source->best_discount;
661 $::form->{"sellprice_$i"} = $best_price->price;
662 $::form->{"active_price_source_$i"} = $best_price->source;
664 if ($best_discount) {
665 $::form->{"discount_$i"} = $best_discount->discount;
666 $::form->{"active_discount_source_$i"} = $best_discount->source;
670 $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"});
671 $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"});
672 $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"});
673 $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0);
680 # ok, so this is a new part
681 # ask if it is a part or service item
683 if ( $form->{"partsgroup_$i"}
684 && ($form->{"partsnumber_$i"} eq "")
685 && ($form->{"description_$i"} eq "")) {
687 $form->{"discount_$i"} = "";
688 $form->{"not_discountable_$i"} = "";
692 $form->{"id_$i"} = 0;
698 $main::lxdebug->leave_sub();
702 $main::lxdebug->enter_sub();
706 my $form = $main::form;
707 my %myconfig = %main::myconfig;
708 my $locale = $main::locale;
710 $form->{vc} = $form->{type} eq 'purchase_delivery_order' ? 'vendor' : 'customer';
712 $form->get_lists("projects" => { "key" => "ALL_PROJECTS",
714 "business_types" => "ALL_BUSINESS_TYPES");
715 $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]);
716 $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all_sorted;
717 $form->{title} = $locale->text('Delivery Orders');
719 setup_do_search_action_bar();
723 print $form->parse_html_template('do/search');
725 $main::lxdebug->leave_sub();
729 $main::lxdebug->enter_sub();
733 my $form = $main::form;
734 my %myconfig = %main::myconfig;
735 my $locale = $main::locale;
736 my $cgi = $::request->{cgi};
738 $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.MassDeliveryOrderPrint kivi.SalesPurchase));
739 ($form->{ $form->{vc} }, $form->{"$form->{vc}_id"}) = split(/--/, $form->{ $form->{vc} });
741 report_generator_set_default_sort('transdate', 1);
745 $form->{rowcount} = scalar @{ $form->{DO} };
748 ids transdate reqdate
750 ordnumber customernumber cusordnumber
751 name employee salesman
752 shipvia globalprojectnumber
753 transaction_description department
758 $form->{l_open} = $form->{l_closed} = "Y" if ($form->{open} && $form->{closed});
759 $form->{l_delivered} = "Y" if ($form->{delivered} && $form->{notdelivered});
761 $form->{title} = $locale->text('Delivery Orders');
763 my $attachment_basename = $form->{vc} eq 'vendor' ? $locale->text('purchase_delivery_order_list') : $locale->text('sales_delivery_order_list');
765 my $report = SL::ReportGenerator->new(\%myconfig, $form);
767 my @hidden_variables = map { "l_${_}" } @columns;
768 push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber serialnumber cusordnumber
769 transaction_description transdatefrom transdateto reqdatefrom reqdateto
770 type vc employee_id salesman_id project_id parts_partnumber parts_description
771 insertdatefrom insertdateto business_id all department_id);
773 my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
776 'ids' => { raw_header_data => SL::Presenter::Tag::checkbox_tag("", id => "multi_all", checkall => "[data-checkall=1]"), align => 'center' },
777 'transdate' => { 'text' => $locale->text('Delivery Order Date'), },
778 'reqdate' => { 'text' => $locale->text('Reqdate'), },
779 'id' => { 'text' => $locale->text('ID'), },
780 'donumber' => { 'text' => $locale->text('Delivery Order'), },
781 'ordnumber' => { 'text' => $locale->text('Order'), },
782 'customernumber' => { 'text' => $locale->text('Customer Number'), },
783 'cusordnumber' => { 'text' => $locale->text('Customer Order Number'), },
784 'name' => { 'text' => $form->{vc} eq 'customer' ? $locale->text('Customer') : $locale->text('Vendor'), },
785 'employee' => { 'text' => $locale->text('Employee'), },
786 'salesman' => { 'text' => $locale->text('Salesman'), },
787 'shipvia' => { 'text' => $locale->text('Ship via'), },
788 'globalprojectnumber' => { 'text' => $locale->text('Project Number'), },
789 'transaction_description' => { 'text' => $locale->text('Transaction description'), },
790 'open' => { 'text' => $locale->text('Open'), },
791 'delivered' => { 'text' => $locale->text('Delivered'), },
792 'department' => { 'text' => $locale->text('Department'), },
793 'insertdate' => { 'text' => $locale->text('Insert Date'), },
796 foreach my $name (qw(id transdate reqdate donumber ordnumber name employee salesman shipvia transaction_description department insertdate)) {
797 my $sortdir = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
798 $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
801 $form->{"l_type"} = "Y";
802 map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
804 $column_defs{ids}->{visible} = 'HTML';
806 $report->set_columns(%column_defs);
807 $report->set_column_order(@columns);
809 $report->set_export_options('orders', @hidden_variables, qw(sort sortdir));
811 $report->set_sort_indicator($form->{sort}, $form->{sortdir});
814 if ($form->{customer}) {
815 push @options, $locale->text('Customer') . " : $form->{customer}";
817 if ($form->{vendor}) {
818 push @options, $locale->text('Vendor') . " : $form->{vendor}";
820 if ($form->{cp_name}) {
821 push @options, $locale->text('Contact Person') . " : $form->{cp_name}";
823 if ($form->{department_id}) {
824 push @options, $locale->text('Department') . " : " . SL::DB::Department->new(id => $form->{department_id})->load->description;
826 if ($form->{donumber}) {
827 push @options, $locale->text('Delivery Order Number') . " : $form->{donumber}";
829 if ($form->{ordnumber}) {
830 push @options, $locale->text('Order Number') . " : $form->{ordnumber}";
832 push @options, $locale->text('Serial Number') . " : $form->{serialnumber}" if $form->{serialnumber};
833 if ($form->{business_id}) {
834 my $vc_type_label = $form->{vc} eq 'customer' ? $locale->text('Customer type') : $locale->text('Vendor type');
835 push @options, $vc_type_label . " : " . SL::DB::Business->new(id => $form->{business_id})->load->description;
837 if ($form->{transaction_description}) {
838 push @options, $locale->text('Transaction description') . " : $form->{transaction_description}";
840 if ($form->{parts_description}) {
841 push @options, $locale->text('Part Description') . " : $form->{parts_description}";
843 if ($form->{parts_partnumber}) {
844 push @options, $locale->text('Part Number') . " : $form->{parts_partnumber}";
846 if ( $form->{transdatefrom} or $form->{transdateto} ) {
847 push @options, $locale->text('Delivery Order Date');
848 push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1) if $form->{transdatefrom};
849 push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1) if $form->{transdateto};
851 if ( $form->{reqdatefrom} or $form->{reqdateto} ) {
852 push @options, $locale->text('Reqdate');
853 push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1) if $form->{reqdatefrom};
854 push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{reqdateto}, 1) if $form->{reqdateto};
856 if ( $form->{insertdatefrom} or $form->{insertdateto} ) {
857 push @options, $locale->text('Insert Date');
858 push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1) if $form->{insertdatefrom};
859 push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{insertdateto}, 1) if $form->{insertdateto};
862 push @options, $locale->text('Open');
864 if ($form->{closed}) {
865 push @options, $locale->text('Closed');
867 if ($form->{delivered}) {
868 push @options, $locale->text('Delivered');
870 if ($form->{notdelivered}) {
871 push @options, $locale->text('Not delivered');
873 push @options, $locale->text('Quick Search') . " : $form->{all}" if $form->{all};
875 my $pr = SL::DB::Manager::Printer->find_by(
876 printer_description => $::locale->text("sales_delivery_order_printer"));
878 $form->{printer_id} = $pr->id;
881 my $print_options = SL::Helper::PrintOptions->get_print_options(
883 hide_language_id => 1,
889 $report->set_options('top_info_text' => join("\n", @options),
890 'raw_top_info_text' => $form->parse_html_template('do/orders_top'),
891 'raw_bottom_info_text' => $form->parse_html_template('do/orders_bottom', { print_options => $print_options }),
892 'output_format' => 'HTML',
893 'title' => $form->{title},
894 'attachment_basename' => $attachment_basename . strftime('_%Y%m%d', localtime time),
896 $report->set_options_from_form();
897 $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
899 # add sort and escape callback, this one we use for the add sub
900 $form->{callback} = $href .= "&sort=$form->{sort}";
902 # escape callback for href
903 my $callback = $form->escape($href);
905 my $edit_url = build_std_url('action=edit', 'type', 'vc');
906 my $edit_order_url = ($::instance_conf->get_feature_experimental_order)
907 ? build_std_url('script=controller.pl', 'action=Order/edit', 'type=' . ($form->{type} eq 'sales_delivery_order' ? 'sales_order' : 'purchase_order'))
908 : build_std_url('script=oe.pl', 'action=edit', 'type=' . ($form->{type} eq 'sales_delivery_order' ? 'sales_order' : 'purchase_order'));
912 foreach my $dord (@{ $form->{DO} }) {
913 $dord->{open} = $dord->{closed} ? $locale->text('No') : $locale->text('Yes');
914 $dord->{delivered} = $dord->{delivered} ? $locale->text('Yes') : $locale->text('No');
916 my $row = { map { $_ => { 'data' => $dord->{$_} } } @columns };
918 my $ord_id = $dord->{id};
920 'raw_data' => $cgi->hidden('-name' => "trans_id_${idx}", '-value' => $ord_id)
921 . $cgi->checkbox('-name' => "multi_id_${idx}",' id' => "multi_id_id_".$ord_id, '-value' => 1, 'data-checkall' => 1, '-label' => ''),
922 'valign' => 'center',
926 $row->{donumber}->{link} = SL::DB::DeliveryOrder::TypeData::get3($dord->{order_type}, "show_menu", "new_controller")
927 ? SL::Controller::DeliveryOrder->url_for(action => "edit", id => $dord->{id}, type => $dord->{order_type})
928 : $edit_url . "&id=" . E($dord->{id}) . "&callback=${callback}";
929 $row->{ordnumber}->{link} = $edit_order_url . "&id=" . E($dord->{oe_id}) . "&callback=${callback}" if $dord->{oe_id};
930 $report->add_data($row);
935 setup_do_orders_action_bar();
937 $report->generate_with_headers();
939 $main::lxdebug->leave_sub();
943 $main::lxdebug->enter_sub();
947 check_do_access_for_edit();
949 my $form = $main::form;
950 my %myconfig = %main::myconfig;
951 my $locale = $main::locale;
953 $form->mtime_ischanged('delivery_orders');
955 $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
957 $form->isblank("transdate", $locale->text('Delivery Order Date missing!'));
959 $form->{donumber} =~ s/^\s*//g;
960 $form->{donumber} =~ s/\s*$//g;
962 my $msg = ucfirst $form->{vc};
963 $form->isblank($form->{vc} . "_id", $locale->text($msg . " missing!"));
965 # $locale->text('Customer missing!');
966 # $locale->text('Vendor missing!');
968 remove_emptied_rows();
971 # check for serial number if part needs one
972 for my $i (1 .. $form->{rowcount} - 1) {
973 next unless $form->{"has_sernumber_$i"};
974 $form->isblank("serialnumber_$i",
975 $locale->text('Serial Number missing in Row') . " $i");
977 # if the name changed get new values
978 my $vc = $form->{vc};
979 if (($form->{"previous_${vc}_id"} || $form->{"${vc}_id"}) != $form->{"${vc}_id"}) {
980 $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
982 if ($vc eq 'customer') {
983 IS->get_customer(\%myconfig, $form);
984 $::form->{billing_address_id} = $::form->{default_billing_address_id};
986 IR->get_vendor(\%myconfig, $form);
990 $::dispatcher->end_request;
993 $form->{id} = 0 if $form->{saveasnew};
994 # we rely on converted_from_orderitems, if the workflow is used
995 # be sure that at least one position is linked to the original orderitem
996 if ($form->{convert_from_oe_ids}) {
998 for my $i (1 .. $form->{rowcount}) {
999 if ($form->{"converted_from_orderitems_id_$i"}) {
1000 $has_linked_pos = 1;
1004 if (!$has_linked_pos) {
1005 $form->error($locale->text('Need at least one original position for the workflow Order to Delivery Order!'));
1009 # saving the history
1010 if(!exists $form->{addition}) {
1011 $form->{snumbers} = qq|donumber_| . $form->{donumber};
1012 $form->{addition} = "SAVED";
1013 $form->save_history;
1015 # /saving the history
1017 $form->{simple_save} = 1;
1018 if (!$params{no_redirect} && !$form->{print_and_save}) {
1019 delete @{$form}{ary_diff([keys %{ $form }], [qw(login id script type cursor_fokus)])};
1021 $::dispatcher->end_request;
1023 $main::lxdebug->leave_sub();
1027 $main::lxdebug->enter_sub();
1029 check_do_access_for_edit();
1031 my $form = $main::form;
1032 my %myconfig = %main::myconfig;
1033 my $locale = $main::locale;
1035 if ($ret = DO->delete()) {
1036 # saving the history
1037 if(!exists $form->{addition}) {
1038 $form->{snumbers} = qq|donumber_| . $form->{donumber};
1039 $form->{addition} = "DELETED";
1040 $form->save_history;
1042 # /saving the history
1044 $form->info($locale->text('Delivery Order deleted!'));
1045 $::dispatcher->end_request;
1048 $form->error($locale->text('Cannot delete delivery order!') . $ret);
1050 $main::lxdebug->leave_sub();
1052 sub delete_transfers {
1053 $main::lxdebug->enter_sub();
1055 check_do_access_for_edit();
1057 my $form = $main::form;
1058 my %myconfig = %main::myconfig;
1059 my $locale = $main::locale;
1062 die "Invalid form type" unless $form->{type} =~ m/^(sales|purchase)_delivery_order$/;
1064 if ($ret = DO->delete_transfers()) {
1065 # saving the history
1066 if(!exists $form->{addition}) {
1067 $form->{snumbers} = qq|donumber_| . $form->{donumber};
1068 $form->{addition} = "UNDO TRANSFER";
1069 $form->save_history;
1071 # /saving the history
1073 flash_later('info', $locale->text("Transfer undone."));
1075 $form->{callback} = 'do.pl?action=edit&type=' . $form->{type} . '&id=' . $form->escape($form->{id});
1079 $form->error($locale->text('Cannot undo delivery order transfer!') . $ret);
1081 $main::lxdebug->leave_sub();
1085 $main::lxdebug->enter_sub();
1087 my $form = $main::form;
1088 my %myconfig = %main::myconfig;
1089 my $locale = $main::locale;
1092 $form->mtime_ischanged('delivery_orders');
1094 $main::auth->assert($form->{type} eq 'purchase_delivery_order' ? 'vendor_invoice_edit' : 'invoice_edit');
1096 $form->get_employee();
1098 $form->{convert_from_do_ids} = $form->{id};
1099 # if we have a reqdate (Liefertermin), this is definetely the preferred
1100 # deliverydate for invoices
1101 $form->{deliverydate} = $form->{reqdate} || $form->{transdate};
1102 $form->{transdate} = $form->{invdate} = $form->current_date(\%myconfig);
1103 $form->{duedate} = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
1104 $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
1106 $form->{rowcount}--;
1108 delete @{$form}{qw(id closed delivered)};
1110 my ($script, $buysell);
1111 if ($form->{type} eq 'purchase_delivery_order') {
1112 $form->{title} = $locale->text('Add Vendor Invoice');
1113 $form->{script} = 'ir.pl';
1118 $form->{title} = $locale->text('Add Sales Invoice');
1119 $form->{script} = 'is.pl';
1124 for my $i (1 .. $form->{rowcount}) {
1125 map { $form->{"${_}_${i}"} = $form->parse_amount(\%myconfig, $form->{"${_}_${i}"}) if $form->{"${_}_${i}"} } qw(ship qty sellprice lastcost basefactor discount);
1127 # adds a customer/vendor discount, unless we have a workflow case
1128 # CAVEAT: has to be done, after the above parse_amount
1129 unless ($form->{"ordnumber"}) {
1130 if ($form->{discount}) { # Falls wir einen Lieferanten-/Kundenrabatt haben
1131 # und rabattfähig sind, dann
1132 unless ($form->{"not_discountable_$i"}) {
1133 $form->{"discount_$i"} = $form->{discount}*100; # ... nehmen wir diesen Rabatt
1137 $form->{"donumber_$i"} = $form->{donumber};
1138 $form->{"converted_from_delivery_order_items_id_$i"} = delete $form->{"delivery_order_items_id_$i"};
1141 $form->{type} = "invoice";
1144 $main::locale = Locale->new("$myconfig{countrycode}", "$script");
1145 $locale = $main::locale;
1147 require "bin/mozilla/$form->{script}";
1149 my $currency = $form->{currency};
1152 if ($form->{ordnumber}) {
1153 require SL::DB::Order;
1154 my $vc_id = $form->{type} =~ /^sales/ ? 'customer_id' : 'vendor_id';
1155 if (my $order = SL::DB::Manager::Order->find_by(ordnumber => $form->{ordnumber}, $vc_id => $form->{"$vc_id"})) {
1157 $form->{orddate} = $order->transdate_as_date;
1158 $form->{$_} = $order->$_ for qw(payment_id salesman_id taxzone_id quonumber taxincluded);
1159 $form->{taxincluded_changed_by_user} = 1;
1163 $form->{currency} = $currency;
1164 $form->{exchangerate} = "";
1165 $form->{forex} = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, $buysell);
1166 $form->{exchangerate} = $form->{forex} if ($form->{forex});
1171 for my $i (1 .. $form->{rowcount}) {
1172 $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
1174 my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
1176 my $decimalplaces = ($dec > 2) ? $dec : 2;
1178 # copy delivery date from reqdate for order -> invoice conversion
1179 $form->{"deliverydate_$i"} = $form->{"reqdate_$i"}
1180 unless $form->{"deliverydate_$i"};
1183 $form->{"sellprice_$i"} =
1184 $form->format_amount(\%myconfig, $form->{"sellprice_$i"},
1187 $form->{"lastcost_$i"} =
1188 $form->format_amount(\%myconfig, $form->{"lastcost_$i"},
1191 (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
1192 $dec_qty = length $dec_qty;
1194 $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
1200 $main::lxdebug->leave_sub();
1204 $main::lxdebug->enter_sub();
1206 my $form = $main::form;
1207 my %myconfig = %main::myconfig;
1208 my $locale = $main::locale;
1211 $main::auth->assert($form->{type} eq 'sales_delivery_order' ? 'invoice_edit' : 'vendor_invoice_edit');
1213 my @do_ids = map { $form->{"trans_id_$_"} } grep { $form->{"multi_id_$_"} } (1..$form->{rowcount});
1215 if (!scalar @do_ids) {
1216 $form->show_generic_error($locale->text('You have not selected any delivery order.'));
1219 map { delete $form->{$_} } grep { m/^(?:trans|multi)_id_\d+/ } keys %{ $form };
1221 if (!DO->retrieve('vc' => $form->{vc}, 'ids' => \@do_ids)) {
1222 $form->show_generic_error($form->{vc} eq 'customer' ?
1223 $locale->text('You cannot create an invoice for delivery orders for different customers.') :
1224 $locale->text('You cannot create an invoice for delivery orders from different vendors.'),
1225 'back_button' => 1);
1228 my $source_type = $form->{type};
1229 $form->{convert_from_do_ids} = join ' ', @do_ids;
1230 # bei der auswahl von mehreren Lieferscheinen fuer eine Rechnung, die einfach in donumber_array
1231 # zwischenspeichern (DO.pm) und als ' '-separierte Liste wieder zurueckschreiben
1232 # Hinweis: delete gibt den wert zurueck und loescht danach das element (nett und einfach)
1233 # $shell: perldoc perlunc; /delete EXPR
1234 $form->{donumber} = delete $form->{donumber_array};
1235 $form->{ordnumber} = delete $form->{ordnumber_array};
1236 $form->{cusordnumber} = delete $form->{cusordnumber_array};
1237 $form->{deliverydate} = $form->{transdate};
1238 $form->{transdate} = $form->current_date(\%myconfig);
1239 $form->{duedate} = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
1240 $form->{type} = "invoice";
1241 $form->{closed} = 0;
1242 $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
1244 my ($script, $buysell);
1245 if ($source_type eq 'purchase_delivery_order') {
1246 $form->{title} = $locale->text('Add Vendor Invoice');
1247 $form->{script} = 'ir.pl';
1252 $form->{title} = $locale->text('Add Sales Invoice');
1253 $form->{script} = 'is.pl';
1258 map { delete $form->{$_} } qw(id subject message cc bcc printed emailed queued);
1260 # get vendor or customer discount
1262 my $saved_form = save_form();
1263 if ($form->{vc} eq 'vendor') {
1264 IR->get_vendor(\%myconfig, \%$form);
1265 $vc_discount = $form->{vendor_discount};
1267 IS->get_customer(\%myconfig, \%$form);
1268 $vc_discount = $form->{customer_discount};
1270 # use payment terms from customer or vendor
1271 restore_form($saved_form,0,qw(payment_id));
1273 $form->{rowcount} = 0;
1274 foreach my $ref (@{ $form->{form_details} }) {
1275 $form->{rowcount}++;
1276 $ref->{reqdate} ||= $ref->{dord_transdate}; # copy transdates into each invoice row
1277 map { $form->{"${_}_$form->{rowcount}"} = $ref->{$_} } keys %{ $ref };
1278 map { $form->{"${_}_$form->{rowcount}"} = $form->format_amount(\%myconfig, $ref->{$_}) } qw(qty sellprice lastcost);
1279 $form->{"converted_from_delivery_order_items_id_$form->{rowcount}"} = delete $form->{"delivery_order_items_id_$form->{rowcount}"};
1281 if ($vc_discount){ # falls wir einen Lieferanten/Kundenrabatt haben
1282 # und keinen anderen discount wert an $i ...
1283 $form->{"discount_$form->{rowcount}"} ||= $vc_discount; # ... nehmen wir diesen Rabatt
1286 $form->{"discount_$form->{rowcount}"} = $form->{"discount_$form->{rowcount}"} * 100; #s.a. Bug 1151
1287 # Anm.: Eine Änderung des discounts in der SL/DO.pm->retrieve (select (doi.discount * 100) as discount) ergibt in psql einen
1288 # Wert von 10.0000001490116. Ferner ist der Rabatt in der Rechnung dann bei 1.0 (?). Deswegen lasse ich das hier. jb 10.10.09
1290 $form->{"discount_$form->{rowcount}"} = $form->format_amount(\%myconfig, $form->{"discount_$form->{rowcount}"});
1292 delete $form->{form_details};
1294 $locale = Locale->new("$myconfig{countrycode}", "$script");
1296 require "bin/mozilla/$form->{script}";
1303 $main::lxdebug->leave_sub();
1307 $main::lxdebug->enter_sub();
1309 check_do_access_for_edit();
1311 my $form = $main::form;
1313 $form->{saveasnew} = 1;
1314 $form->{closed} = 0;
1315 $form->{delivered} = 0;
1316 map { delete $form->{$_} } qw(printed emailed queued);
1317 delete @{ $form }{ grep { m/^stock_(?:in|out)_\d+/ } keys %{ $form } };
1318 $form->{"converted_from_delivery_order_items_id_$_"} = delete $form->{"delivery_order_items_id_$_"} for 1 .. $form->{"rowcount"};
1319 # Let kivitendo assign a new order number if the user hasn't changed the
1320 # previous one. If it has been changed manually then use it as-is.
1321 $form->{donumber} =~ s/^\s*//g;
1322 $form->{donumber} =~ s/\s*$//g;
1323 if ($form->{saved_donumber} && ($form->{saved_donumber} eq $form->{donumber})) {
1324 delete($form->{donumber});
1329 $main::lxdebug->leave_sub();
1332 sub calculate_stock_in_out {
1333 $main::lxdebug->enter_sub();
1335 my $form = $main::form;
1339 if (!$form->{"id_${i}"}) {
1340 $main::lxdebug->leave_sub();
1344 my $all_units = AM->retrieve_all_units();
1346 my $in_out = $form->{type} =~ /^sales/ ? 'out' : 'in';
1347 my $sinfo = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_${i}"});
1349 my $do_qty = AM->sum_with_unit($::form->{"qty_$i"}, $::form->{"unit_$i"});
1350 my $sum = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $sinfo });
1351 my $matches = $do_qty == $sum;
1353 my $amount_unit = $all_units->{$form->{"partunit_$i"}}->{base_unit};
1354 my $content = $form->format_amount(\%::myconfig, AM->convert_unit($amount_unit, $form->{"unit_$i"}) * $sum * 1) . ' ' . $form->{"unit_$i"};
1356 $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="?">|;
1358 $main::lxdebug->leave_sub();
1363 sub get_basic_bin_wh_info {
1364 $main::lxdebug->enter_sub();
1366 my $stock_info = shift;
1368 my $form = $main::form;
1370 foreach my $sinfo (@{ $stock_info }) {
1371 next unless ($sinfo->{bin_id});
1373 my $bin_info = WH->get_basic_bin_info('id' => $sinfo->{bin_id});
1374 map { $sinfo->{"${_}_description"} = $sinfo->{"${_}description"} = $bin_info->{"${_}_description"} } qw(bin warehouse);
1377 $main::lxdebug->leave_sub();
1380 sub stock_in_out_form {
1381 $main::lxdebug->enter_sub();
1383 my $form = $main::form;
1385 if ($form->{in_out} eq 'out') {
1391 $main::lxdebug->leave_sub();
1394 sub redo_stock_info {
1395 $main::lxdebug->enter_sub();
1399 my $form = $main::form;
1401 my @non_empty = grep { $_->{qty} } @{ $params{stock_info} };
1403 if ($params{add_empty_row}) {
1405 'warehouse_id' => scalar(@non_empty) ? $non_empty[-1]->{warehouse_id} : undef,
1406 'bin_id' => scalar(@non_empty) ? $non_empty[-1]->{bin_id} : undef,
1410 @{ $params{stock_info} } = @non_empty;
1412 $main::lxdebug->leave_sub();
1415 sub update_stock_in {
1416 $main::lxdebug->enter_sub();
1418 my $form = $main::form;
1419 my %myconfig = %main::myconfig;
1421 my $stock_info = [];
1423 foreach my $i (1..$form->{rowcount}) {
1424 $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
1425 push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(warehouse_id bin_id chargenumber
1426 bestbefore qty unit delivery_order_items_stock_id) };
1429 display_stock_in_form($stock_info);
1431 $main::lxdebug->leave_sub();
1435 $main::lxdebug->enter_sub();
1437 my $form = $main::form;
1439 my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
1441 display_stock_in_form($stock_info);
1443 $main::lxdebug->leave_sub();
1446 sub display_stock_in_form {
1447 $main::lxdebug->enter_sub();
1449 my $stock_info = shift;
1451 my $form = $main::form;
1452 my %myconfig = %main::myconfig;
1453 my $locale = $main::locale;
1455 $form->{title} = $locale->text('Stock');
1457 my $part_info = IC->get_basic_part_info('id' => $form->{parts_id});
1459 # Standardlagerplatz für Standard-Auslagern verwenden, falls keiner für die Ware explizit definiert wurde
1460 if ($::instance_conf->get_transfer_default_use_master_default_bin) {
1461 $part_info->{warehouse_id} ||= $::instance_conf->get_warehouse_id;
1462 $part_info->{bin_id} ||= $::instance_conf->get_bin_id;
1465 my $units = AM->retrieve_units(\%myconfig, $form);
1466 # der zweite Parameter von unit_select_data gibt den default-Namen (selected) vor
1467 my $units_data = AM->unit_select_data($units, $form->{do_unit}, undef, $part_info->{unit});
1469 $form->get_lists('warehouses' => { 'key' => 'WAREHOUSES',
1470 'bins' => 'BINS' });
1472 redo_stock_info('stock_info' => $stock_info, 'add_empty_row' => !$form->{delivered});
1474 get_basic_bin_wh_info($stock_info);
1476 $form->header(no_layout => 1);
1477 print $form->parse_html_template('do/stock_in_form', { 'UNITS' => $units_data,
1478 'STOCK_INFO' => $stock_info,
1479 'PART_INFO' => $part_info, });
1481 $main::lxdebug->leave_sub();
1484 sub _stock_in_out_set_qty_display {
1485 my $stock_info = shift;
1487 my $all_units = AM->retrieve_all_units();
1488 my $sum = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
1489 my $amount_unit = $all_units->{$form->{"partunit"}}->{base_unit};
1490 $form->{qty_display} = $form->format_amount(\%::myconfig, AM->convert_unit($amount_unit, $form->{"do_unit"}) * $sum * 1) . ' ' . $form->{"do_unit"};
1494 $main::lxdebug->enter_sub();
1496 my $form = $main::form;
1497 my %myconfig = %main::myconfig;
1499 my $stock_info = [];
1501 foreach my $i (1..$form->{rowcount}) {
1502 $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
1504 next if ($form->{"qty_$i"} <= 0);
1506 push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(delivery_order_items_stock_id warehouse_id bin_id chargenumber bestbefore qty unit) };
1509 $form->{stock} = SL::YAML::Dump($stock_info);
1511 _stock_in_out_set_qty_display($stock_info);
1513 my $do_qty = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
1514 my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
1517 print $form->parse_html_template('do/set_stock_in_out', {
1518 qty_matches => $do_qty == $transfer_qty,
1521 $main::lxdebug->leave_sub();
1524 sub stock_out_form {
1525 $main::lxdebug->enter_sub();
1527 my $form = $main::form;
1528 my %myconfig = %main::myconfig;
1529 my $locale = $main::locale;
1531 $form->{title} = $locale->text('Release From Stock');
1533 my $part_info = IC->get_basic_part_info('id' => $form->{parts_id});
1535 my $units = AM->retrieve_units(\%myconfig, $form);
1536 my $units_data = AM->unit_select_data($units, undef, undef, $part_info->{unit});
1538 my @contents = DO->get_item_availability('parts_id' => $form->{parts_id});
1540 my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
1542 if (!$form->{delivered}) {
1543 foreach my $row (@contents) {
1544 $row->{available_qty} = $form->format_amount(\%::myconfig, $row->{qty} * 1) . ' ' . $part_info->{unit};
1546 foreach my $sinfo (@{ $stock_info }) {
1547 next if (($row->{bin_id} != $sinfo->{bin_id}) ||
1548 ($row->{warehouse_id} != $sinfo->{warehouse_id}) ||
1549 ($row->{chargenumber} ne $sinfo->{chargenumber}) ||
1550 ($row->{bestbefore} ne $sinfo->{bestbefore}));
1552 map { $row->{"stock_$_"} = $sinfo->{$_} } qw(qty unit error delivery_order_items_stock_id);
1557 get_basic_bin_wh_info($stock_info);
1559 foreach my $sinfo (@{ $stock_info }) {
1560 map { $sinfo->{"stock_$_"} = $sinfo->{$_} } qw(qty unit);
1564 $form->header(no_layout => 1);
1565 print $form->parse_html_template('do/stock_out_form', { 'UNITS' => $units_data,
1566 'WHCONTENTS' => $form->{delivered} ? $stock_info : \@contents,
1567 'PART_INFO' => $part_info, });
1569 $main::lxdebug->leave_sub();
1573 $main::lxdebug->enter_sub();
1575 my $form = $main::form;
1576 my %myconfig = %main::myconfig;
1577 my $locale = $main::locale;
1579 my $stock_info = [];
1581 foreach my $i (1 .. $form->{rowcount}) {
1582 $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
1584 next if ($form->{"qty_$i"} <= 0);
1586 push @{ $stock_info }, {
1587 'warehouse_id' => $form->{"warehouse_id_$i"},
1588 'bin_id' => $form->{"bin_id_$i"},
1589 'chargenumber' => $form->{"chargenumber_$i"},
1590 'bestbefore' => $form->{"bestbefore_$i"},
1591 'qty' => $form->{"qty_$i"},
1592 'unit' => $form->{"unit_$i"},
1594 'delivery_order_items_stock_id' => $form->{"delivery_order_items_stock_id_$i"},
1598 my @errors = DO->check_stock_availability('requests' => $stock_info,
1599 'parts_id' => $form->{parts_id});
1601 $form->{stock} = SL::YAML::Dump($stock_info);
1604 $form->{ERRORS} = [];
1605 map { push @{ $form->{ERRORS} }, $locale->text('Error in row #1: The quantity you entered is bigger than the stocked quantity.', $_->{row}); } @errors;
1606 stock_in_out_form();
1609 _stock_in_out_set_qty_display($stock_info);
1611 my $do_qty = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
1612 my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
1615 print $form->parse_html_template('do/set_stock_in_out', {
1616 qty_matches => $do_qty == $transfer_qty,
1620 $main::lxdebug->leave_sub();
1624 $main::lxdebug->enter_sub();
1626 my $form = $main::form;
1627 my %myconfig = %main::myconfig;
1628 my $locale = $main::locale;
1630 if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
1631 $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred in.'));
1634 save(no_redirect => 1);
1636 my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_in_${_}"} } (1 .. $form->{rowcount});
1640 my $units = AM->retrieve_units(\%myconfig, $form);
1641 my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
1644 $form->{ERRORS} = [];
1646 foreach my $i (1 .. $form->{rowcount}) {
1647 next unless ($form->{"id_$i"} && $form->{"stock_in_$i"});
1649 my $row_sum_base_qty = 0;
1650 my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
1652 foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_in_$i"}) }) {
1653 $request->{parts_id} = $form->{"id_$i"};
1654 $row_sum_base_qty += $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
1656 $request->{project_id} = $form->{"project_id_$i"} || $form->{globalproject_id};
1658 push @all_requests, $request;
1661 next if (0 == $row_sum_base_qty);
1663 my $do_base_qty = $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
1665 # if ($do_base_qty != $row_sum_base_qty) {
1666 # push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no stock at all or the full quantity of #2 #3.',
1667 # $i, $form->{"qty_$i"}, $form->{"unit_$i"});
1671 if (@{ $form->{ERRORS} }) {
1672 push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
1674 set_headings('edit');
1676 $main::lxdebug->leave_sub();
1678 $::dispatcher->end_request;
1682 DO->transfer_in_out('direction' => 'in',
1683 'requests' => \@all_requests);
1685 SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
1687 flash_later('info', $locale->text("Transfer successful"));
1688 $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id});
1691 $main::lxdebug->leave_sub();
1695 $main::lxdebug->enter_sub();
1697 my $form = $main::form;
1698 my %myconfig = %main::myconfig;
1699 my $locale = $main::locale;
1701 if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
1702 $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred out.'));
1705 save(no_redirect => 1);
1707 my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_out_${_}"} } (1 .. $form->{rowcount});
1711 my $units = AM->retrieve_units(\%myconfig, $form);
1712 my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
1715 $form->{ERRORS} = [];
1717 foreach my $i (1 .. $form->{rowcount}) {
1718 next unless ($form->{"id_$i"} && $form->{"stock_out_$i"});
1720 my $row_sum_base_qty = 0;
1721 my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
1723 foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_out_$i"}) }) {
1724 $request->{parts_id} = $form->{"id_$i"};
1725 $request->{base_qty} = $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
1726 $request->{project_id} = $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id};
1728 my $map_key = join '--', ($form->{"id_$i"}, @{$request}{qw(warehouse_id bin_id chargenumber bestbefore)});
1730 $request_map{$map_key} ||= $request;
1731 $request_map{$map_key}->{sum_base_qty} ||= 0;
1732 $request_map{$map_key}->{sum_base_qty} += $request->{base_qty};
1733 $row_sum_base_qty += $request->{base_qty};
1735 push @all_requests, $request;
1738 next if (0 == $row_sum_base_qty);
1740 my $do_base_qty = $form->{"qty_$i"} * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
1742 # if ($do_base_qty != $row_sum_base_qty) {
1743 # push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no transfer at all or the full quantity of #2 #3.',
1744 # $i, $form->{"qty_$i"}, $form->{"unit_$i"});
1749 my @bin_ids = map { $_->{bin_id} } values %request_map;
1750 my %bin_info_map = WH->get_basic_bin_info('id' => \@bin_ids);
1751 my @contents = DO->get_item_availability('parts_id' => \@part_ids);
1753 foreach my $inv (@contents) {
1754 my $map_key = join '--', @{$inv}{qw(parts_id warehouse_id bin_id chargenumber bestbefore)};
1756 next unless ($request_map{$map_key});
1758 my $request = $request_map{$map_key};
1759 $request->{ok} = $request->{sum_base_qty} <= $inv->{qty};
1762 foreach my $request (values %request_map) {
1763 next if ($request->{ok});
1765 my $pinfo = $part_info_map{$request->{parts_id}};
1766 my $binfo = $bin_info_map{$request->{bin_id}};
1768 if ($::instance_conf->get_show_bestbefore) {
1769 push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, #5, for the transfer of #6.",
1770 $pinfo->{description},
1771 $binfo->{warehouse_description},
1772 $binfo->{bin_description},
1773 $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
1774 $request->{bestbefore} ? $locale->text('bestbefore #1', $request->{bestbefore}) : $locale->text('no bestbefore'),
1775 $form->format_amount(\%::myconfig, $request->{sum_base_qty}) . ' ' . $pinfo->{unit});
1777 push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, for the transfer of #5.",
1778 $pinfo->{description},
1779 $binfo->{warehouse_description},
1780 $binfo->{bin_description},
1781 $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
1782 $form->format_amount(\%::myconfig, $request->{sum_base_qty}) . ' ' . $pinfo->{unit});
1787 if (@{ $form->{ERRORS} }) {
1788 push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
1790 set_headings('edit');
1792 $main::lxdebug->leave_sub();
1794 $::dispatcher->end_request;
1797 DO->transfer_in_out('direction' => 'out',
1798 'requests' => \@all_requests);
1800 SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
1802 flash_later('info', $locale->text("Transfer successful"));
1803 $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id});
1806 $main::lxdebug->leave_sub();
1810 $main::lxdebug->enter_sub();
1812 my $form = $main::form;
1814 DO->close_orders('ids' => [ $form->{id} ]);
1816 $form->{closed} = 1;
1820 $main::lxdebug->leave_sub();
1824 $::lxdebug->enter_sub;
1829 retrieve_partunits();
1831 my $new_rowcount = $::form->{"rowcount"} * 1 + 1;
1832 $::form->{"project_id_${new_rowcount}"} = $::form->{"globalproject_id"};
1834 $::form->language_payment(\%::myconfig);
1836 Common::webdav_folder($::form);
1839 display_row(++$::form->{rowcount});
1842 $::lxdebug->leave_sub;
1846 call_sub($main::form->{yes_nextsub});
1850 call_sub($main::form->{no_nextsub});
1854 call_sub($main::form->{update_nextsub} || $main::form->{nextsub} || 'update_delivery_order');
1858 my $form = $main::form;
1859 my $locale = $main::locale;
1861 foreach my $action (qw(update print save transfer_out transfer_out_default sort
1862 transfer_in transfer_in_default mark_closed save_as_new invoice delete)) {
1863 if ($form->{"action_${action}"}) {
1869 $form->error($locale->text('No action defined.'));
1872 sub transfer_out_default {
1873 $main::lxdebug->enter_sub();
1875 my $form = $main::form;
1877 transfer_in_out_default('direction' => 'out');
1879 $main::lxdebug->leave_sub();
1882 sub transfer_in_default {
1883 $main::lxdebug->enter_sub();
1885 my $form = $main::form;
1887 transfer_in_out_default('direction' => 'in');
1889 $main::lxdebug->leave_sub();
1892 # Falls das Standardlagerverfahren aktiv ist, wird
1893 # geprüft, ob alle Standardlagerplätze für die Auslager-
1894 # artikel vorhanden sind UND ob die Warenmenge ausreicht zum
1895 # Auslagern. Falls nicht wird entsprechend eine Fehlermeldung
1896 # generiert. Offen Chargennummer / bestbefore wird nicht berücksichtigt
1897 sub transfer_in_out_default {
1898 $main::lxdebug->enter_sub();
1900 my $form = $main::form;
1901 my %myconfig = %main::myconfig;
1902 my $locale = $main::locale;
1905 my (%missing_default_bins, %qty_parts, @all_requests, %part_info_map, $default_warehouse_id, $default_bin_id);
1907 Common::check_params(\%params, qw(direction));
1909 # entsprechende defaults holen, falls standardlagerplatz verwendet werden soll
1910 if ($::instance_conf->get_transfer_default_use_master_default_bin) {
1911 $default_warehouse_id = $::instance_conf->get_warehouse_id;
1912 $default_bin_id = $::instance_conf->get_bin_id;
1916 my @part_ids = map { $form->{"id_${_}"} } (1 .. $form->{rowcount});
1918 my $units = AM->retrieve_units(\%myconfig, $form);
1919 %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
1920 foreach my $i (1 .. $form->{rowcount}) {
1921 next unless ($form->{"id_$i"});
1922 my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
1923 my $qty = $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
1925 $form->show_generic_error($locale->text("Cannot transfer negative entries." )) if ($qty < 0);
1926 # if we do not want to transfer services and this part is a service, set qty to zero
1927 # ... and do not create a hash entry in %qty_parts below (will skip check for bins for the transfer == out case)
1928 # ... 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)
1930 $qty = 0 if (!$::instance_conf->get_transfer_default_services && $part_info_map{$form->{"id_$i"}}->{part_type} eq 'service');
1931 $qty_parts{$form->{"id_$i"}} += $qty;
1933 delete $qty_parts{$form->{"id_$i"}} unless $qty_parts{$form->{"id_$i"}};
1934 undef $form->{"stock_in_$i"};
1937 $part_info_map{$form->{"id_$i"}}{bin_id} ||= $default_bin_id;
1938 $part_info_map{$form->{"id_$i"}}{warehouse_id} ||= $default_warehouse_id;
1940 push @all_requests, ($qty == 0) ? { } : {
1941 'chargenumber' => '', #?? die müsste entsprechend geholt werden
1942 #'bestbefore' => undef, # TODO wird nicht berücksichtigt
1943 'bin_id' => $part_info_map{$form->{"id_$i"}}{bin_id},
1945 'parts_id' => $form->{"id_$i"},
1946 'comment' => $locale->text("Default transfer delivery order"),
1947 'unit' => $part_info_map{$form->{"id_$i"}}{unit},
1948 'warehouse_id' => $part_info_map{$form->{"id_$i"}}{warehouse_id},
1949 'oe_id' => $form->{id},
1950 'project_id' => $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id}
1954 # jetzt wird erst überprüft, ob die Stückzahl entsprechend stimmt.
1955 # check if bin (transfer in and transfer out and qty (transfer out) is correct
1956 foreach my $key (keys %qty_parts) {
1958 $missing_default_bins{$key}{missing_bin} = 1 unless ($part_info_map{$key}{bin_id});
1959 next unless ($part_info_map{$key}{bin_id}); # abbruch
1961 if ($params{direction} eq 'out') { # wird nur für ausgehende Mengen benötigt
1962 my ($max_qty, $error) = WH->get_max_qty_parts_bin(parts_id => $key, bin_id => $part_info_map{$key}{bin_id});
1964 # wir können nicht entscheiden, welche charge oder mhd (bestbefore) ausgewählt sein soll
1965 # deshalb rückmeldung nach oben geben, manuell auszulagern
1966 # TODO Bei nur einem Treffer mit Charge oder bestbefore wäre das noch möglich
1967 $missing_default_bins{$key}{chargenumber} = 1;
1969 if ($max_qty < $qty_parts{$key}){
1970 $missing_default_bins{$key}{missing_qty} = $max_qty - $qty_parts{$key};
1976 # Abfrage für Fehlerbehandlung (nur bei direction == out)
1977 if (scalar (keys %missing_default_bins)) {
1979 foreach my $fehler (keys %missing_default_bins) {
1981 my $ware = WH->get_part_description(parts_id => $fehler);
1982 if ($missing_default_bins{$fehler}{missing_bin}){
1983 $fehlertext .= "Kein Standardlagerplatz definiert bei $ware <br>";
1985 if ($missing_default_bins{$fehler}{missing_qty}) { # missing_qty
1986 $fehlertext .= "Es fehlen " . $missing_default_bins{$fehler}{missing_qty}*-1 .
1987 " von $ware auf dem Standard-Lagerplatz " . $part_info_map{$fehler}{bin} . " zum Auslagern<br>";
1989 if ($missing_default_bins{$fehler}{chargenumber}){
1990 $fehlertext .= "Die Ware hat eine Chargennummer oder eine Mindesthaltbarkeit definiert.
1991 Hier kann man nicht automatisch entscheiden.
1992 Bitte diesen Lieferschein manuell auslagern.
1995 # auslagern soll immer gehen, auch wenn nicht genügend auf lager ist.
1996 # der lagerplatz ist hier extra konfigurierbar, bspw. Lager-Korrektur mit
1997 # Lagerplatz Lagerplatz-Korrektur
1998 my $default_warehouse_id_ignore_onhand = $::instance_conf->get_warehouse_id_ignore_onhand;
1999 my $default_bin_id_ignore_onhand = $::instance_conf->get_bin_id_ignore_onhand;
2000 if ($::instance_conf->get_transfer_default_ignore_onhand && $default_bin_id_ignore_onhand) {
2001 # entsprechende defaults holen
2002 # falls chargenumber, bestbefore oder anzahl nicht stimmt, auf automatischen
2003 # lagerplatz wegbuchen!
2004 foreach (@all_requests) {
2005 if ($_->{parts_id} eq $fehler){
2006 $_->{bin_id} = $default_bin_id_ignore_onhand;
2007 $_->{warehouse_id} = $default_warehouse_id_ignore_onhand;
2011 #$main::lxdebug->message(0, 'Fehlertext: ' . $fehlertext);
2012 $form->show_generic_error($locale->text("Cannot transfer. <br> Reason:<br>#1", $fehlertext ));
2018 # hier der eigentliche fallunterschied für in oder out
2019 my $prefix = $params{direction} eq 'in' ? 'in' : 'out';
2021 # dieser array_ref ist für DO->save da:
2022 # einmal die all_requests in YAML verwandeln, damit delivery_order_items_stock
2023 # gefüllt werden kann.
2024 # could be dumped to the form in the first loop,
2025 # but maybe bin_id and warehouse_id has changed to the "korrekturlager" with
2026 # allowed negative qty ($::instance_conf->get_warehouse_id_ignore_onhand) ...
2028 foreach (@all_requests){
2030 next unless scalar(%{ $_ });
2031 $form->{"stock_${prefix}_$i"} = SL::YAML::Dump([$_]);
2034 save(no_redirect => 1); # Wir können auslagern, deshalb beleg speichern
2035 # und in delivery_order_items_stock speichern
2037 # ... and fill back the persistent dois_id for inventory fk
2038 undef (@all_requests);
2039 foreach my $i (1 .. $form->{rowcount}) {
2040 next unless ($form->{"id_$i"} && $form->{"stock_${prefix}_$i"});
2041 push @all_requests, @{ DO->unpack_stock_information('packed' => $form->{"stock_${prefix}_$i"}) };
2043 DO->transfer_in_out('direction' => $prefix,
2044 'requests' => \@all_requests);
2046 SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
2048 $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'out';
2049 $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'in';
2055 $main::lxdebug->enter_sub();
2059 my $form = $main::form;
2062 save(no_redirect => 1); # has to be done, at least for newly added positions
2064 # hashify partnumbers, positions. key is delivery_order_items_id
2065 for my $i (1 .. ($form->{rowcount}) ) {
2066 $temp_hash{$form->{"delivery_order_items_id_$i"}} = { runningnumber => $form->{"runningnumber_$i"}, partnumber => $form->{"partnumber_$i"} };
2067 if ($form->{id} && $form->{"discount_$i"}) {
2068 # prepare_order assumes a db value if there is a form->id and multiplies *100
2069 # We hope for new controller code (no more format_amount/parse_amount distinction)
2070 $form->{"discount_$i"} /=100;
2073 # naturally sort partnumbers and get a sorted array of doi_ids
2074 my @sorted_doi_ids = sort { Sort::Naturally::ncmp($temp_hash{$a}->{"partnumber"}, $temp_hash{$b}->{"partnumber"}) } keys %temp_hash;
2079 for (@sorted_doi_ids) {
2080 $form->{"runningnumber_$temp_hash{$_}->{runningnumber}"} = $new_number;
2083 # all parse_amounts changes are in form (i.e. , to .) therefore we need
2084 # another format_amount to change it back, for the next save ;-(
2085 # works great except for row discounts (see above comment)
2089 $main::lxdebug->leave_sub();
2101 do.pl - Script for all calls to delivery order
2109 Sorts all position with Natural Sort. Can be activated in form_footer.html like this
2110 C<E<lt>input class="submit" type="submit" name="action_sort" id="sort_button" value="[% 'Sort and Save' | $T8 %]"E<gt>>
2116 Sort and Save can be implemented as an optional button if configuration ca be set by client config.
2117 Example coding for database scripts and templates in (git show af2f24b8), check also
2118 autogeneration for rose (scripts/rose_auto_create_model.pl --h)