1 #=====================================================================
4 # Based on SQL-Ledger Version 2.1.9
5 # Web http://www.lx-office.org
7 #=====================================================================
8 # SQL-Ledger, Accounting
9 # Copyright (c) 1998-2003
11 # Author: Dieter Simader
12 # Email: dsimader@sql-ledger.org
13 # Web: http://www.sql-ledger.org
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
29 #======================================================================
32 #======================================================================
35 use List::MoreUtils qw(uniq);
36 use List::Util qw(max sum);
37 use POSIX qw(strftime);
39 use SL::Controller::DeliveryOrder;
40 use SL::DB::DeliveryOrder;
41 use SL::DB::DeliveryOrder::TypeData qw(:types validate_type);
45 use SL::MoreCommon qw(ary_diff restore_form save_form);
46 use SL::ReportGenerator;
49 use Sort::Naturally ();
50 require "bin/mozilla/common.pl";
51 require "bin/mozilla/io.pl";
52 require "bin/mozilla/reportgenerator.pl";
60 sub check_do_access_for_edit {
61 validate_type($::form->{type});
63 my $right = SL::DB::DeliveryOrder::TypeData::get3($::form->{type}, "rights", "edit");
64 $main::auth->assert($right);
68 validate_type($::form->{type});
70 my $right = SL::DB::DeliveryOrder::TypeData::get3($::form->{type}, "rights", "view");
71 $main::auth->assert($right);
75 $main::lxdebug->enter_sub();
81 my $form = $main::form;
82 my $locale = $main::locale;
84 if ($form->{type} eq 'purchase_delivery_order') {
85 $form->{vc} = 'vendor';
86 $form->{title} = $action eq "edit" ? $locale->text('Edit Purchase Delivery Order') : $locale->text('Add Purchase Delivery Order');
88 $form->{vc} = 'customer';
89 $form->{title} = $action eq "edit" ? $locale->text('Edit Sales Delivery Order') : $locale->text('Add Sales Delivery Order');
92 $form->{heading} = $locale->text('Delivery Order');
94 $main::lxdebug->leave_sub();
98 $main::lxdebug->enter_sub();
100 check_do_access_for_edit();
102 if (($::form->{type} =~ /purchase/) && !$::instance_conf->get_allow_new_purchase_invoice) {
103 $::form->show_generic_error($::locale->text("You do not have the permissions to access this function."));
106 my $form = $main::form;
110 $form->{show_details} = $::myconfig{show_form_details};
111 $form->{callback} = build_std_url('action=add', 'type', 'vc') unless ($form->{callback});
113 order_links(is_new => 1);
117 $main::lxdebug->leave_sub();
121 $main::lxdebug->enter_sub();
125 my $form = $main::form;
127 $form->{show_details} = $::myconfig{show_form_details};
129 # show history button
130 $form->{javascript} = qq|<script type="text/javascript" src="js/show_history.js"></script>|;
131 #/show hhistory button
133 $form->{simple_save} = 0;
135 set_headings("edit");
137 # editing without stuff to edit? try adding it first
138 if ($form->{rowcount} && !$form->{print_and_save}) {
139 # map { $id++ if $form->{"multi_id_$_"} } (1 .. $form->{rowcount});
143 undef $form->{rowcount};
145 $main::lxdebug->leave_sub();
148 } elsif (!$form->{id}) {
150 $main::lxdebug->leave_sub();
154 my ($language_id, $printer_id);
155 if ($form->{print_and_save}) {
156 $form->{action} = "dispatcher";
157 $form->{action_print} = "1";
158 $form->{resubmit} = 1;
159 $language_id = $form->{language_id};
160 $printer_id = $form->{printer_id};
163 set_headings("edit");
168 if ($form->{print_and_save}) {
169 $form->{language_id} = $language_id;
170 $form->{printer_id} = $printer_id;
175 $main::lxdebug->leave_sub();
179 $main::lxdebug->enter_sub();
184 my $form = $main::form;
185 my %myconfig = %main::myconfig;
187 # retrieve order/quotation
188 my $editing = $form->{id};
190 DO->retrieve('vc' => $form->{vc},
191 'ids' => $form->{id});
193 $form->backup_vars(qw(payment_id language_id taxzone_id salesman_id taxincluded cp_id intnotes delivery_term_id currency));
195 # get customer / vendor
196 if ($form->{vc} eq 'vendor') {
197 IR->get_vendor(\%myconfig, \%$form);
198 $form->{discount} = $form->{vendor_discount};
200 IS->get_customer(\%myconfig, \%$form);
201 $form->{discount} = $form->{customer_discount};
202 $form->{billing_address_id} = $form->{default_billing_address_id} if $params{is_new};
205 $form->restore_vars(qw(payment_id language_id taxzone_id intnotes cp_id delivery_term_id));
206 $form->restore_vars(qw(currency)) if ($form->{id} || $form->{convert_from_oe_ids});
207 $form->restore_vars(qw(taxincluded)) if $form->{id};
208 $form->restore_vars(qw(salesman_id)) if $editing;
210 $main::lxdebug->leave_sub();
214 $main::lxdebug->enter_sub();
218 my $form = $main::form;
219 my %myconfig = %main::myconfig;
221 $form->{formname} = $form->{type} unless $form->{formname};
224 foreach my $ref (@{ $form->{form_details} }) {
225 $form->{rowcount} = ++$i;
227 map { $form->{"${_}_$i"} = $ref->{$_} } keys %{$ref};
229 for my $i (1 .. $form->{rowcount}) {
231 $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100);
233 $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
235 my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
237 my $decimalplaces = ($dec > 2) ? $dec : 2;
239 # copy reqdate from deliverydate for invoice -> order conversion
240 $form->{"reqdate_$i"} = $form->{"deliverydate_$i"} unless $form->{"reqdate_$i"};
242 $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
243 $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}, $decimalplaces);
245 (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
246 $dec_qty = length $dec_qty;
247 $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
250 $main::lxdebug->leave_sub();
253 sub setup_do_action_bar {
254 my @transfer_qty = qw(kivi.SalesPurchase.delivery_order_check_transfer_qty);
255 my @req_trans_desc = qw(kivi.SalesPurchase.check_transaction_description) x!!$::instance_conf->get_require_transaction_description_ps;
256 my $is_customer = $::form->{vc} eq 'customer';
258 my $undo_date = DateTime->today->subtract(days => $::instance_conf->get_undo_transfer_interval);
259 my $insertdate = DateTime->from_kivitendo($::form->{insertdate});
260 my $undo_transfer = 0;
261 if (ref $undo_date eq 'DateTime' && ref $insertdate eq 'DateTime') {
262 $undo_transfer = $insertdate > $undo_date;
265 my $may_edit_create = $::auth->assert(SL::DB::DeliveryOrder::TypeData::get3($::form->{type}, "rights", "edit"), 1);
267 for my $bar ($::request->layout->get('actionbar')) {
271 submit => [ '#form', { action => "update" } ],
272 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.') : undef,
273 id => 'update_button',
274 accesskey => 'enter',
280 submit => [ '#form', { action => "save" } ],
281 checks => [ 'kivi.validate_form' ],
282 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
283 : $::form->{delivered} ? t8('This record has already been delivered.')
288 submit => [ '#form', { action => "save_as_new" } ],
289 checks => [ 'kivi.validate_form' ],
290 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
294 t8('Mark as closed'),
295 submit => [ '#form', { action => "mark_closed" } ],
296 checks => [ 'kivi.validate_form' ],
297 confirm => t8('This will remove the delivery order from showing as open even if contents are not delivered. Proceed?'),
298 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
299 : !$::form->{id} ? t8('This record has not been saved yet.')
300 : $::form->{closed} ? t8('This record has already been closed.')
303 ], # end of combobox "Save"
307 submit => [ '#form', { action => "delete" } ],
308 confirm => t8('Do you really want to delete this object?'),
309 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
310 : !$::form->{id} ? t8('This record has not been saved yet.')
311 : $::form->{delivered} ? t8('This record has already been delivered.')
312 : ($::form->{vc} eq 'customer' && !$::instance_conf->get_sales_delivery_order_show_delete) ? t8('Deleting this type of record has been disabled in the configuration.')
313 : ($::form->{vc} eq 'vendor' && !$::instance_conf->get_purchase_delivery_order_show_delete) ? t8('Deleting this type of record has been disabled in the configuration.')
320 submit => [ '#form', { action => "transfer_out" } ],
321 checks => [ 'kivi.validate_form', @transfer_qty ],
322 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
323 : $::form->{delivered} ? t8('This record has already been delivered.')
325 only_if => $is_customer,
328 t8('Transfer out via default'),
329 submit => [ '#form', { action => "transfer_out_default" } ],
330 checks => [ 'kivi.validate_form' ],
331 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
332 : $::form->{delivered} ? t8('This record has already been delivered.')
334 only_if => $is_customer && $::instance_conf->get_transfer_default,
338 submit => [ '#form', { action => "transfer_in" } ],
339 checks => [ 'kivi.validate_form', @transfer_qty ],
340 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
341 : $::form->{delivered} ? t8('This record has already been delivered.')
343 only_if => !$is_customer,
346 t8('Transfer in via default'),
347 submit => [ '#form', { action => "transfer_in_default" } ],
348 checks => [ 'kivi.validate_form' ],
349 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
350 : $::form->{delivered} ? t8('This record has already been delivered.')
352 only_if => !$is_customer && $::instance_conf->get_transfer_default,
356 submit => [ '#form', { action => "delete_transfers" } ],
357 checks => [ 'kivi.validate_form' ],
358 only_if => $::form->{delivered},
359 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
360 : !$undo_transfer ? t8('Transfer date exceeds the maximum allowed interval.')
363 ], # end of combobox "Transfer out"
370 submit => [ '#form', { action => "invoice" } ],
371 disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
372 confirm => $::form->{delivered} ? undef
373 : ($::form->{vc} eq 'customer' && $::instance_conf->get_sales_delivery_order_check_stocked) ? t8('This record has not been stocked out. Proceed?')
374 : ($::form->{vc} eq 'vendor' && $::instance_conf->get_purchase_delivery_order_check_stocked) ? t8('This record has not been stocked in. Proceed?')
379 action => [ t8('Export') ],
382 call => [ 'kivi.SalesPurchase.show_print_dialog' ],
383 checks => [ 'kivi.validate_form' ],
384 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.') : undef,
388 call => [ 'kivi.SalesPurchase.show_email_dialog' ],
389 checks => [ 'kivi.validate_form' ],
390 disabled => !$may_edit_create ? t8('You do not have the permissions to access this function.')
391 : !$::form->{id} ? t8('This record has not been saved yet.')
394 ], # end of combobox "Export"
397 action => [ t8('more') ],
400 call => [ 'set_history_window', $::form->{id} * 1, 'id' ],
401 disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
405 call => [ 'follow_up_window' ],
406 disabled => !$::form->{id} ? t8('This record has not been saved yet.') : undef,
408 ], # end if combobox "more"
411 $::request->layout->add_javascripts('kivi.Validator.js');
414 sub setup_do_search_action_bar {
417 for my $bar ($::request->layout->get('actionbar')) {
421 submit => [ '#form' ],
422 accesskey => 'enter',
423 checks => [ 'kivi.validate_form' ],
427 $::request->layout->add_javascripts('kivi.Validator.js');
430 sub setup_do_orders_action_bar {
433 for my $bar ($::request->layout->get('actionbar')) {
437 submit => [ '#form', { action => 'invoice_multi' } ],
438 checks => [ [ 'kivi.check_if_entries_selected', '#form tbody input[type=checkbox]' ] ],
439 accesskey => 'enter',
443 call => [ 'kivi.SalesPurchase.show_print_dialog', 'js:kivi.MassDeliveryOrderPrint.submitMultiOrders' ],
444 checks => [ [ 'kivi.check_if_entries_selected', '#form tbody input[type=checkbox]' ] ],
451 $main::lxdebug->enter_sub();
455 my $form = $main::form;
456 my %myconfig = %main::myconfig;
458 my $class = "SL::DB::" . ($form->{vc} eq 'customer' ? 'Customer' : 'Vendor');
459 $form->{VC_OBJ} = $class->load_cached($form->{ $form->{vc} . '_id' });
461 $form->{CONTACT_OBJ} = $form->{cp_id} ? SL::DB::Contact->load_cached($form->{cp_id}) : undef;
462 my $current_employee = SL::DB::Manager::Employee->current;
463 $form->{employee_id} = $form->{old_employee_id} if $form->{old_employee_id};
464 $form->{salesman_id} = $form->{old_salesman_id} if $form->{old_salesman_id};
465 $form->{employee_id} ||= $current_employee->id;
466 $form->{salesman_id} ||= $current_employee->id;
468 my $vc = $form->{vc} eq "customer" ? "customers" : "vendors";
469 $form->get_lists("price_factors" => "ALL_PRICE_FACTORS",
470 "business_types" => "ALL_BUSINESS_TYPES",
472 $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all_sorted;
473 $form->{ALL_LANGUAGES} = SL::DB::Manager::Language->get_all_sorted;
476 my @old_project_ids = uniq grep { $_ } map { $_ * 1 } ($form->{"globalproject_id"}, map { $form->{"project_id_$_"} } 1..$form->{"rowcount"});
477 my @old_ids_cond = @old_project_ids ? (id => \@old_project_ids) : ();
479 if (($vc eq 'customers') && $::instance_conf->get_customer_projects_only_in_sales) {
482 customer_id => $::form->{customer_id},
483 billable_customer_id => $::form->{customer_id},
488 and => [ active => 1, @customer_cond ],
492 $::form->{ALL_PROJECTS} = SL::DB::Manager::Project->get_all_sorted(query => \@conditions);
493 $::form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{employee_id}, deleted => 0 ] ]);
494 $::form->{ALL_SALESMEN} = SL::DB::Manager::Employee->get_all_sorted(query => [ or => [ id => $::form->{salesman_id}, deleted => 0 ] ]);
495 $::form->{ALL_SHIPTO} = SL::DB::Manager::Shipto->get_all_sorted(query => [
496 or => [ trans_id => $::form->{"$::form->{vc}_id"} * 1, and => [ shipto_id => $::form->{shipto_id} * 1, trans_id => undef ] ]
498 $::form->{ALL_CONTACTS} = SL::DB::Manager::Contact->get_all_sorted(query => [
500 cp_cv_id => $::form->{"$::form->{vc}_id"} * 1,
503 cp_id => $::form->{cp_id} * 1
508 my $dispatch_to_popup = '';
509 if ($form->{resubmit} && ($form->{format} eq "html")) {
510 $dispatch_to_popup = "window.open('about:blank','Beleg'); document.do.target = 'Beleg';";
511 $dispatch_to_popup .= "document.do.submit();";
512 } elsif ($form->{resubmit} && $form->{action_print}) {
513 # emulate click for resubmitting actions
514 $dispatch_to_popup = "kivi.SalesPurchase.show_print_dialog(); kivi.SalesPurchase.print_record();";
516 $::request->{layout}->add_javascripts_inline("\$(function(){$dispatch_to_popup});");
519 $form->{follow_up_trans_info} = $form->{donumber} .'('. $form->{VC_OBJ}->name .')' if $form->{VC_OBJ};
521 $::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));
523 setup_do_action_bar();
526 # Fix für Bug 1082 Erwartet wird: 'abteilungsNAME--abteilungsID'
527 # und Erweiterung für Bug 1760:
528 # Das war leider nur ein Teil-Fix, da das Verhalten den 'Erneuern'-Knopf
529 # nicht überlebt. Konsequent jetzt auf L umgestellt
530 # $ perldoc SL::Template::Plugin::L
531 # Daher entsprechend nur die Anpassung in form_header
532 # und in DO.pm gemacht. 4 Testfälle:
533 # department_id speichern | i.O.
534 # department_id lesen | i.O.
535 # department leer überlebt erneuern | i.O.
536 # department nicht leer überlebt erneuern | i.O.
537 # $main::lxdebug->message(0, 'ABTEILUNGS ID in form?' . $form->{department_id});
538 print $form->parse_html_template('do/form_header');
540 $main::lxdebug->leave_sub();
544 $main::lxdebug->enter_sub();
548 my $form = $main::form;
550 $form->{PRINT_OPTIONS} = setup_sales_purchase_print_options();
551 $form->{ALL_DELIVERY_TERMS} = SL::DB::Manager::DeliveryTerm->get_all_sorted();
553 my $shipto_cvars = SL::DB::Shipto->new->cvars_by_config;
554 foreach my $var (@{ $shipto_cvars }) {
555 my $name = "shiptocvar_" . $var->config->name;
556 $var->value($form->{$name}) if exists $form->{$name};
559 print $form->parse_html_template('do/form_footer',
560 {transfer_default => ($::instance_conf->get_transfer_default),
561 shipto_cvars => $shipto_cvars});
563 $main::lxdebug->leave_sub();
566 sub update_delivery_order {
567 $main::lxdebug->enter_sub();
571 my $form = $main::form;
572 my %myconfig = %main::myconfig;
574 set_headings($form->{"id"} ? "edit" : "add");
576 $form->{insertdate} = SL::DB::DeliveryOrder->new(id => $form->{id})->load->itime_as_date if $form->{id};
581 $payment_id = $form->{payment_id} if $form->{payment_id};
583 my $vc = $form->{vc};
584 if (($form->{"previous_${vc}_id"} || $form->{"${vc}_id"}) != $form->{"${vc}_id"}) {
585 $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
587 if ($vc eq 'customer') {
588 IS->get_customer(\%myconfig, $form);
589 $::form->{billing_address_id} = $::form->{default_billing_address_id};
591 IR->get_vendor(\%myconfig, $form);
595 $form->{discount} = $form->{"$form->{vc}_discount"} if defined $form->{"$form->{vc}_discount"};
596 # Problem: Wenn man ohne Erneuern einen Kunden/Lieferanten
597 # wechselt, wird der entsprechende Kunden/ Lieferantenrabatt
598 # nicht übernommen. Grundproblem: In Commit 82574e78
599 # hab ich aus discount customer_discount und vendor_discount
600 # gemacht und entsprechend an den Oberflächen richtig hin-
601 # geschoben. Die damals bessere Lösung wäre gewesen:
602 # In den Templates nur die hidden für form-discount wieder ein-
603 # setzen dann wäre die Verrenkung jetzt nicht notwendig.
604 # TODO: Ggf. Bugfix 1284, 1575 und 817 wieder zusammenführen
605 # Testfälle: Kunden mit Rabatt 0 -> Rabatt 20 i.O.
606 # Kunde mit Rabatt 20 -> Rabatt 0 i.O.
607 # Kunde mit Rabatt 20 -> Rabatt 5,5 i.O.
608 $form->{payment_id} = $payment_id if $form->{payment_id} eq "";
610 my $i = $form->{rowcount};
612 if ( ($form->{"partnumber_$i"} eq "")
613 && ($form->{"description_$i"} eq "")
614 && ($form->{"partsgroup_$i"} eq "")) {
621 if ($form->{type} eq 'purchase_delivery_order') {
622 IR->retrieve_item(\%myconfig, $form);
625 IS->retrieve_item(\%myconfig, $form);
629 my $rows = scalar @{ $form->{item_list} };
632 $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
633 if( !$form->{"qty_$i"} ) {
634 $form->{"qty_$i"} = 1;
639 select_item(mode => $mode, pre_entered_qty => $form->{"qty_$i"});
640 $::dispatcher->end_request;
644 my $sellprice = $form->parse_amount(\%myconfig, $form->{"sellprice_$i"});
646 map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} } keys %{ $form->{item_list}[0] };
648 $form->{"marge_price_factor_$i"} = $form->{item_list}->[0]->{price_factor};
651 $form->{"sellprice_$i"} = $sellprice;
653 my $record = _make_record();
654 my $price_source = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
655 my $best_price = $price_source->best_price;
656 my $best_discount = $price_source->best_discount;
659 $::form->{"sellprice_$i"} = $best_price->price;
660 $::form->{"active_price_source_$i"} = $best_price->source;
662 if ($best_discount) {
663 $::form->{"discount_$i"} = $best_discount->discount;
664 $::form->{"active_discount_source_$i"} = $best_discount->source;
668 $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"});
669 $form->{"lastcost_$i"} = $form->format_amount(\%myconfig, $form->{"lastcost_$i"});
670 $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"});
671 $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0);
678 # ok, so this is a new part
679 # ask if it is a part or service item
681 if ( $form->{"partsgroup_$i"}
682 && ($form->{"partsnumber_$i"} eq "")
683 && ($form->{"description_$i"} eq "")) {
685 $form->{"discount_$i"} = "";
686 $form->{"not_discountable_$i"} = "";
690 $form->{"id_$i"} = 0;
696 $main::lxdebug->leave_sub();
700 $main::lxdebug->enter_sub();
704 my $form = $main::form;
705 my %myconfig = %main::myconfig;
706 my $locale = $main::locale;
708 $form->{vc} = $form->{type} eq 'purchase_delivery_order' ? 'vendor' : 'customer';
710 $form->get_lists("projects" => { "key" => "ALL_PROJECTS",
712 "business_types" => "ALL_BUSINESS_TYPES");
713 $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]);
714 $form->{ALL_DEPARTMENTS} = SL::DB::Manager::Department->get_all_sorted;
715 $form->{title} = $locale->text('Delivery Orders');
717 setup_do_search_action_bar();
721 print $form->parse_html_template('do/search');
723 $main::lxdebug->leave_sub();
727 $main::lxdebug->enter_sub();
731 my $form = $main::form;
732 my %myconfig = %main::myconfig;
733 my $locale = $main::locale;
734 my $cgi = $::request->{cgi};
736 $::request->{layout}->use_javascript(map { "${_}.js" } qw(kivi.MassDeliveryOrderPrint kivi.SalesPurchase));
737 ($form->{ $form->{vc} }, $form->{"$form->{vc}_id"}) = split(/--/, $form->{ $form->{vc} });
739 report_generator_set_default_sort('transdate', 1);
743 $form->{rowcount} = scalar @{ $form->{DO} };
746 ids transdate reqdate
748 ordnumber customernumber cusordnumber
749 name employee salesman
750 shipvia globalprojectnumber
751 transaction_description department
756 $form->{l_open} = $form->{l_closed} = "Y" if ($form->{open} && $form->{closed});
757 $form->{l_delivered} = "Y" if ($form->{delivered} && $form->{notdelivered});
759 $form->{title} = $locale->text('Delivery Orders');
761 my $attachment_basename = $form->{vc} eq 'vendor' ? $locale->text('purchase_delivery_order_list') : $locale->text('sales_delivery_order_list');
763 my $report = SL::ReportGenerator->new(\%myconfig, $form);
765 my @hidden_variables = map { "l_${_}" } @columns;
766 push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber serialnumber cusordnumber
767 transaction_description transdatefrom transdateto reqdatefrom reqdateto
768 type vc employee_id salesman_id project_id parts_partnumber parts_description
769 insertdatefrom insertdateto business_id all department_id);
771 my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
774 'ids' => { raw_header_data => SL::Presenter::Tag::checkbox_tag("", id => "multi_all", checkall => "[data-checkall=1]"), align => 'center' },
775 'transdate' => { 'text' => $locale->text('Delivery Order Date'), },
776 'reqdate' => { 'text' => $locale->text('Reqdate'), },
777 'id' => { 'text' => $locale->text('ID'), },
778 'donumber' => { 'text' => $locale->text('Delivery Order'), },
779 'ordnumber' => { 'text' => $locale->text('Order'), },
780 'customernumber' => { 'text' => $locale->text('Customer Number'), },
781 'cusordnumber' => { 'text' => $locale->text('Customer Order Number'), },
782 'name' => { 'text' => $form->{vc} eq 'customer' ? $locale->text('Customer') : $locale->text('Vendor'), },
783 'employee' => { 'text' => $locale->text('Employee'), },
784 'salesman' => { 'text' => $locale->text('Salesman'), },
785 'shipvia' => { 'text' => $locale->text('Ship via'), },
786 'globalprojectnumber' => { 'text' => $locale->text('Project Number'), },
787 'transaction_description' => { 'text' => $locale->text('Transaction description'), },
788 'open' => { 'text' => $locale->text('Open'), },
789 'delivered' => { 'text' => $locale->text('Delivered'), },
790 'department' => { 'text' => $locale->text('Department'), },
791 'insertdate' => { 'text' => $locale->text('Insert Date'), },
794 foreach my $name (qw(id transdate reqdate donumber ordnumber name employee salesman shipvia transaction_description department insertdate)) {
795 my $sortdir = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
796 $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
799 $form->{"l_type"} = "Y";
800 map { $column_defs{$_}->{visible} = $form->{"l_${_}"} ? 1 : 0 } @columns;
802 $column_defs{ids}->{visible} = 'HTML';
804 $report->set_columns(%column_defs);
805 $report->set_column_order(@columns);
807 $report->set_export_options('orders', @hidden_variables, qw(sort sortdir));
809 $report->set_sort_indicator($form->{sort}, $form->{sortdir});
812 if ($form->{customer}) {
813 push @options, $locale->text('Customer') . " : $form->{customer}";
815 if ($form->{vendor}) {
816 push @options, $locale->text('Vendor') . " : $form->{vendor}";
818 if ($form->{cp_name}) {
819 push @options, $locale->text('Contact Person') . " : $form->{cp_name}";
821 if ($form->{department_id}) {
822 push @options, $locale->text('Department') . " : " . SL::DB::Department->new(id => $form->{department_id})->load->description;
824 if ($form->{donumber}) {
825 push @options, $locale->text('Delivery Order Number') . " : $form->{donumber}";
827 if ($form->{ordnumber}) {
828 push @options, $locale->text('Order Number') . " : $form->{ordnumber}";
830 push @options, $locale->text('Serial Number') . " : $form->{serialnumber}" if $form->{serialnumber};
831 if ($form->{business_id}) {
832 my $vc_type_label = $form->{vc} eq 'customer' ? $locale->text('Customer type') : $locale->text('Vendor type');
833 push @options, $vc_type_label . " : " . SL::DB::Business->new(id => $form->{business_id})->load->description;
835 if ($form->{transaction_description}) {
836 push @options, $locale->text('Transaction description') . " : $form->{transaction_description}";
838 if ($form->{parts_description}) {
839 push @options, $locale->text('Part Description') . " : $form->{parts_description}";
841 if ($form->{parts_partnumber}) {
842 push @options, $locale->text('Part Number') . " : $form->{parts_partnumber}";
844 if ( $form->{transdatefrom} or $form->{transdateto} ) {
845 push @options, $locale->text('Delivery Order Date');
846 push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{transdatefrom}, 1) if $form->{transdatefrom};
847 push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{transdateto}, 1) if $form->{transdateto};
849 if ( $form->{reqdatefrom} or $form->{reqdateto} ) {
850 push @options, $locale->text('Reqdate');
851 push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1) if $form->{reqdatefrom};
852 push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{reqdateto}, 1) if $form->{reqdateto};
854 if ( $form->{insertdatefrom} or $form->{insertdateto} ) {
855 push @options, $locale->text('Insert Date');
856 push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1) if $form->{insertdatefrom};
857 push @options, $locale->text('Bis') . " " . $locale->date(\%myconfig, $form->{insertdateto}, 1) if $form->{insertdateto};
860 push @options, $locale->text('Open');
862 if ($form->{closed}) {
863 push @options, $locale->text('Closed');
865 if ($form->{delivered}) {
866 push @options, $locale->text('Delivered');
868 if ($form->{notdelivered}) {
869 push @options, $locale->text('Not delivered');
871 push @options, $locale->text('Quick Search') . " : $form->{all}" if $form->{all};
873 my $pr = SL::DB::Manager::Printer->find_by(
874 printer_description => $::locale->text("sales_delivery_order_printer"));
876 $form->{printer_id} = $pr->id;
879 my $print_options = SL::Helper::PrintOptions->get_print_options(
881 hide_language_id => 1,
887 $report->set_options('top_info_text' => join("\n", @options),
888 'raw_top_info_text' => $form->parse_html_template('do/orders_top'),
889 'raw_bottom_info_text' => $form->parse_html_template('do/orders_bottom', { print_options => $print_options }),
890 'output_format' => 'HTML',
891 'title' => $form->{title},
892 'attachment_basename' => $attachment_basename . strftime('_%Y%m%d', localtime time),
894 $report->set_options_from_form();
895 $locale->set_numberformat_wo_thousands_separator(\%myconfig) if lc($report->{options}->{output_format}) eq 'csv';
897 # add sort and escape callback, this one we use for the add sub
898 $form->{callback} = $href .= "&sort=$form->{sort}";
900 # escape callback for href
901 my $callback = $form->escape($href);
903 my $edit_url = build_std_url('action=edit', 'type', 'vc');
904 my $edit_order_url = ($::instance_conf->get_feature_experimental_order)
905 ? build_std_url('script=controller.pl', 'action=Order/edit', 'type=' . ($form->{type} eq 'sales_delivery_order' ? 'sales_order' : 'purchase_order'))
906 : build_std_url('script=oe.pl', 'action=edit', 'type=' . ($form->{type} eq 'sales_delivery_order' ? 'sales_order' : 'purchase_order'));
910 foreach my $dord (@{ $form->{DO} }) {
911 $dord->{open} = $dord->{closed} ? $locale->text('No') : $locale->text('Yes');
912 $dord->{delivered} = $dord->{delivered} ? $locale->text('Yes') : $locale->text('No');
914 my $row = { map { $_ => { 'data' => $dord->{$_} } } @columns };
916 my $ord_id = $dord->{id};
918 'raw_data' => $cgi->hidden('-name' => "trans_id_${idx}", '-value' => $ord_id)
919 . $cgi->checkbox('-name' => "multi_id_${idx}",' id' => "multi_id_id_".$ord_id, '-value' => 1, 'data-checkall' => 1, '-label' => ''),
920 'valign' => 'center',
924 $row->{donumber}->{link} = SL::DB::DeliveryOrder::TypeData::get3($dord->{order_type}, "show_menu", "new_controller")
925 ? SL::Controller::DeliveryOrder->url_for(action => "edit", id => $dord->{id}, type => $dord->{order_type})
926 : $edit_url . "&id=" . E($dord->{id}) . "&callback=${callback}";
927 $row->{ordnumber}->{link} = $edit_order_url . "&id=" . E($dord->{oe_id}) . "&callback=${callback}" if $dord->{oe_id};
928 $report->add_data($row);
933 setup_do_orders_action_bar();
935 $report->generate_with_headers();
937 $main::lxdebug->leave_sub();
941 $main::lxdebug->enter_sub();
945 check_do_access_for_edit();
947 my $form = $main::form;
948 my %myconfig = %main::myconfig;
949 my $locale = $main::locale;
951 $form->mtime_ischanged('delivery_orders');
953 $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
955 $form->isblank("transdate", $locale->text('Delivery Order Date missing!'));
957 $form->{donumber} =~ s/^\s*//g;
958 $form->{donumber} =~ s/\s*$//g;
960 my $msg = ucfirst $form->{vc};
961 $form->isblank($form->{vc} . "_id", $locale->text($msg . " missing!"));
963 # $locale->text('Customer missing!');
964 # $locale->text('Vendor missing!');
966 remove_emptied_rows();
969 # check for serial number if part needs one
970 for my $i (1 .. $form->{rowcount} - 1) {
971 next unless $form->{"has_sernumber_$i"};
972 $form->isblank("serialnumber_$i",
973 $locale->text('Serial Number missing in Row') . " $i");
975 # if the name changed get new values
976 my $vc = $form->{vc};
977 if (($form->{"previous_${vc}_id"} || $form->{"${vc}_id"}) != $form->{"${vc}_id"}) {
978 $::form->{salesman_id} = SL::DB::Manager::Employee->current->id if exists $::form->{salesman_id};
980 if ($vc eq 'customer') {
981 IS->get_customer(\%myconfig, $form);
982 $::form->{billing_address_id} = $::form->{default_billing_address_id};
984 IR->get_vendor(\%myconfig, $form);
988 $::dispatcher->end_request;
991 $form->{id} = 0 if $form->{saveasnew};
992 # we rely on converted_from_orderitems, if the workflow is used
993 # be sure that at least one position is linked to the original orderitem
994 if ($form->{convert_from_oe_ids}) {
996 for my $i (1 .. $form->{rowcount}) {
997 if ($form->{"converted_from_orderitems_id_$i"}) {
1002 if (!$has_linked_pos) {
1003 $form->error($locale->text('Need at least one original position for the workflow Order to Delivery Order!'));
1007 # saving the history
1008 if(!exists $form->{addition}) {
1009 $form->{snumbers} = qq|donumber_| . $form->{donumber};
1010 $form->{addition} = "SAVED";
1011 $form->save_history;
1013 # /saving the history
1015 $form->{simple_save} = 1;
1016 if (!$params{no_redirect} && !$form->{print_and_save}) {
1017 delete @{$form}{ary_diff([keys %{ $form }], [qw(login id script type cursor_fokus)])};
1019 $::dispatcher->end_request;
1021 $main::lxdebug->leave_sub();
1025 $main::lxdebug->enter_sub();
1027 check_do_access_for_edit();
1029 my $form = $main::form;
1030 my %myconfig = %main::myconfig;
1031 my $locale = $main::locale;
1033 if ($ret = DO->delete()) {
1034 # saving the history
1035 if(!exists $form->{addition}) {
1036 $form->{snumbers} = qq|donumber_| . $form->{donumber};
1037 $form->{addition} = "DELETED";
1038 $form->save_history;
1040 # /saving the history
1042 $form->info($locale->text('Delivery Order deleted!'));
1043 $::dispatcher->end_request;
1046 $form->error($locale->text('Cannot delete delivery order!') . $ret);
1048 $main::lxdebug->leave_sub();
1050 sub delete_transfers {
1051 $main::lxdebug->enter_sub();
1053 check_do_access_for_edit();
1055 my $form = $main::form;
1056 my %myconfig = %main::myconfig;
1057 my $locale = $main::locale;
1060 die "Invalid form type" unless $form->{type} =~ m/^(sales|purchase)_delivery_order$/;
1062 if ($ret = DO->delete_transfers()) {
1063 # saving the history
1064 if(!exists $form->{addition}) {
1065 $form->{snumbers} = qq|donumber_| . $form->{donumber};
1066 $form->{addition} = "UNDO TRANSFER";
1067 $form->save_history;
1069 # /saving the history
1071 flash_later('info', $locale->text("Transfer undone."));
1073 $form->{callback} = 'do.pl?action=edit&type=' . $form->{type} . '&id=' . $form->escape($form->{id});
1077 $form->error($locale->text('Cannot undo delivery order transfer!') . $ret);
1079 $main::lxdebug->leave_sub();
1083 $main::lxdebug->enter_sub();
1085 my $form = $main::form;
1086 my %myconfig = %main::myconfig;
1087 my $locale = $main::locale;
1090 $form->mtime_ischanged('delivery_orders');
1092 $main::auth->assert($form->{type} eq 'purchase_delivery_order' ? 'vendor_invoice_edit' : 'invoice_edit');
1094 $form->get_employee();
1096 $form->{convert_from_do_ids} = $form->{id};
1097 # if we have a reqdate (Liefertermin), this is definetely the preferred
1098 # deliverydate for invoices
1099 $form->{deliverydate} = $form->{reqdate} || $form->{transdate};
1100 $form->{transdate} = $form->{invdate} = $form->current_date(\%myconfig);
1101 $form->{duedate} = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
1102 $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
1104 $form->{rowcount}--;
1106 delete @{$form}{qw(id closed delivered)};
1108 my ($script, $buysell);
1109 if ($form->{type} eq 'purchase_delivery_order') {
1110 $form->{title} = $locale->text('Add Vendor Invoice');
1111 $form->{script} = 'ir.pl';
1116 $form->{title} = $locale->text('Add Sales Invoice');
1117 $form->{script} = 'is.pl';
1122 for my $i (1 .. $form->{rowcount}) {
1123 map { $form->{"${_}_${i}"} = $form->parse_amount(\%myconfig, $form->{"${_}_${i}"}) if $form->{"${_}_${i}"} } qw(ship qty sellprice lastcost basefactor discount);
1125 # adds a customer/vendor discount, unless we have a workflow case
1126 # CAVEAT: has to be done, after the above parse_amount
1127 unless ($form->{"ordnumber"}) {
1128 if ($form->{discount}) { # Falls wir einen Lieferanten-/Kundenrabatt haben
1129 # und rabattfähig sind, dann
1130 unless ($form->{"not_discountable_$i"}) {
1131 $form->{"discount_$i"} = $form->{discount}*100; # ... nehmen wir diesen Rabatt
1135 $form->{"donumber_$i"} = $form->{donumber};
1136 $form->{"converted_from_delivery_order_items_id_$i"} = delete $form->{"delivery_order_items_id_$i"};
1139 $form->{type} = "invoice";
1142 $main::locale = Locale->new("$myconfig{countrycode}", "$script");
1143 $locale = $main::locale;
1145 require "bin/mozilla/$form->{script}";
1147 my $currency = $form->{currency};
1150 if ($form->{ordnumber}) {
1151 require SL::DB::Order;
1152 my $vc_id = $form->{type} =~ /^sales/ ? 'customer_id' : 'vendor_id';
1153 if (my $order = SL::DB::Manager::Order->find_by(ordnumber => $form->{ordnumber}, $vc_id => $form->{"$vc_id"})) {
1155 $form->{orddate} = $order->transdate_as_date;
1156 $form->{$_} = $order->$_ for qw(payment_id salesman_id taxzone_id quonumber taxincluded);
1157 $form->{taxincluded_changed_by_user} = 1;
1161 $form->{currency} = $currency;
1162 $form->{exchangerate} = "";
1163 $form->{forex} = $form->check_exchangerate(\%myconfig, $form->{currency}, $form->{invdate}, $buysell);
1164 $form->{exchangerate} = $form->{forex} if ($form->{forex});
1169 for my $i (1 .. $form->{rowcount}) {
1170 $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"});
1172 my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
1174 my $decimalplaces = ($dec > 2) ? $dec : 2;
1176 # copy delivery date from reqdate for order -> invoice conversion
1177 $form->{"deliverydate_$i"} = $form->{"reqdate_$i"}
1178 unless $form->{"deliverydate_$i"};
1181 $form->{"sellprice_$i"} =
1182 $form->format_amount(\%myconfig, $form->{"sellprice_$i"},
1185 $form->{"lastcost_$i"} =
1186 $form->format_amount(\%myconfig, $form->{"lastcost_$i"},
1189 (my $dec_qty) = ($form->{"qty_$i"} =~ /\.(\d+)/);
1190 $dec_qty = length $dec_qty;
1192 $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
1198 $main::lxdebug->leave_sub();
1202 $main::lxdebug->enter_sub();
1204 my $form = $main::form;
1205 my %myconfig = %main::myconfig;
1206 my $locale = $main::locale;
1209 $main::auth->assert($form->{type} eq 'sales_delivery_order' ? 'invoice_edit' : 'vendor_invoice_edit');
1211 my @do_ids = map { $form->{"trans_id_$_"} } grep { $form->{"multi_id_$_"} } (1..$form->{rowcount});
1213 if (!scalar @do_ids) {
1214 $form->show_generic_error($locale->text('You have not selected any delivery order.'));
1217 map { delete $form->{$_} } grep { m/^(?:trans|multi)_id_\d+/ } keys %{ $form };
1219 if (!DO->retrieve('vc' => $form->{vc}, 'ids' => \@do_ids)) {
1220 $form->show_generic_error($form->{vc} eq 'customer' ?
1221 $locale->text('You cannot create an invoice for delivery orders for different customers.') :
1222 $locale->text('You cannot create an invoice for delivery orders from different vendors.'),
1223 'back_button' => 1);
1226 my $source_type = $form->{type};
1227 $form->{convert_from_do_ids} = join ' ', @do_ids;
1228 # bei der auswahl von mehreren Lieferscheinen fuer eine Rechnung, die einfach in donumber_array
1229 # zwischenspeichern (DO.pm) und als ' '-separierte Liste wieder zurueckschreiben
1230 # Hinweis: delete gibt den wert zurueck und loescht danach das element (nett und einfach)
1231 # $shell: perldoc perlunc; /delete EXPR
1232 $form->{donumber} = delete $form->{donumber_array};
1233 $form->{ordnumber} = delete $form->{ordnumber_array};
1234 $form->{cusordnumber} = delete $form->{cusordnumber_array};
1235 $form->{deliverydate} = $form->{transdate};
1236 $form->{transdate} = $form->current_date(\%myconfig);
1237 $form->{duedate} = $form->current_date(\%myconfig, $form->{invdate}, $form->{terms} * 1);
1238 $form->{type} = "invoice";
1239 $form->{closed} = 0;
1240 $form->{defaultcurrency} = $form->get_default_currency(\%myconfig);
1242 my ($script, $buysell);
1243 if ($source_type eq 'purchase_delivery_order') {
1244 $form->{title} = $locale->text('Add Vendor Invoice');
1245 $form->{script} = 'ir.pl';
1250 $form->{title} = $locale->text('Add Sales Invoice');
1251 $form->{script} = 'is.pl';
1256 map { delete $form->{$_} } qw(id subject message cc bcc printed emailed queued);
1258 # get vendor or customer discount
1260 my $saved_form = save_form();
1261 if ($form->{vc} eq 'vendor') {
1262 IR->get_vendor(\%myconfig, \%$form);
1263 $vc_discount = $form->{vendor_discount};
1265 IS->get_customer(\%myconfig, \%$form);
1266 $vc_discount = $form->{customer_discount};
1268 # use payment terms from customer or vendor
1269 restore_form($saved_form,0,qw(payment_id));
1271 $form->{rowcount} = 0;
1272 foreach my $ref (@{ $form->{form_details} }) {
1273 $form->{rowcount}++;
1274 $ref->{reqdate} ||= $ref->{dord_transdate}; # copy transdates into each invoice row
1275 map { $form->{"${_}_$form->{rowcount}"} = $ref->{$_} } keys %{ $ref };
1276 map { $form->{"${_}_$form->{rowcount}"} = $form->format_amount(\%myconfig, $ref->{$_}) } qw(qty sellprice lastcost);
1277 $form->{"converted_from_delivery_order_items_id_$form->{rowcount}"} = delete $form->{"delivery_order_items_id_$form->{rowcount}"};
1279 if ($vc_discount){ # falls wir einen Lieferanten/Kundenrabatt haben
1280 # und keinen anderen discount wert an $i ...
1281 $form->{"discount_$form->{rowcount}"} ||= $vc_discount; # ... nehmen wir diesen Rabatt
1284 $form->{"discount_$form->{rowcount}"} = $form->{"discount_$form->{rowcount}"} * 100; #s.a. Bug 1151
1285 # Anm.: Eine Änderung des discounts in der SL/DO.pm->retrieve (select (doi.discount * 100) as discount) ergibt in psql einen
1286 # Wert von 10.0000001490116. Ferner ist der Rabatt in der Rechnung dann bei 1.0 (?). Deswegen lasse ich das hier. jb 10.10.09
1288 $form->{"discount_$form->{rowcount}"} = $form->format_amount(\%myconfig, $form->{"discount_$form->{rowcount}"});
1290 delete $form->{form_details};
1292 $locale = Locale->new("$myconfig{countrycode}", "$script");
1294 require "bin/mozilla/$form->{script}";
1301 $main::lxdebug->leave_sub();
1305 $main::lxdebug->enter_sub();
1307 check_do_access_for_edit();
1309 my $form = $main::form;
1311 $form->{saveasnew} = 1;
1312 $form->{closed} = 0;
1313 $form->{delivered} = 0;
1314 map { delete $form->{$_} } qw(printed emailed queued);
1315 delete @{ $form }{ grep { m/^stock_(?:in|out)_\d+/ } keys %{ $form } };
1316 $form->{"converted_from_delivery_order_items_id_$_"} = delete $form->{"delivery_order_items_id_$_"} for 1 .. $form->{"rowcount"};
1317 # Let kivitendo assign a new order number if the user hasn't changed the
1318 # previous one. If it has been changed manually then use it as-is.
1319 $form->{donumber} =~ s/^\s*//g;
1320 $form->{donumber} =~ s/\s*$//g;
1321 if ($form->{saved_donumber} && ($form->{saved_donumber} eq $form->{donumber})) {
1322 delete($form->{donumber});
1327 $main::lxdebug->leave_sub();
1330 sub calculate_stock_in_out {
1331 $main::lxdebug->enter_sub();
1333 my $form = $main::form;
1337 if (!$form->{"id_${i}"}) {
1338 $main::lxdebug->leave_sub();
1342 my $all_units = AM->retrieve_all_units();
1344 my $in_out = $form->{type} =~ /^sales/ ? 'out' : 'in';
1345 my $sinfo = DO->unpack_stock_information('packed' => $form->{"stock_${in_out}_${i}"});
1347 my $do_qty = AM->sum_with_unit($::form->{"qty_$i"}, $::form->{"unit_$i"});
1348 my $sum = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $sinfo });
1349 my $matches = $do_qty == $sum;
1351 my $amount_unit = $all_units->{$form->{"partunit_$i"}}->{base_unit};
1352 my $content = $form->format_amount(\%::myconfig, AM->convert_unit($amount_unit, $form->{"unit_$i"}) * $sum * 1) . ' ' . $form->{"unit_$i"};
1354 $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="?">|;
1356 $main::lxdebug->leave_sub();
1361 sub get_basic_bin_wh_info {
1362 $main::lxdebug->enter_sub();
1364 my $stock_info = shift;
1366 my $form = $main::form;
1368 foreach my $sinfo (@{ $stock_info }) {
1369 next unless ($sinfo->{bin_id});
1371 my $bin_info = WH->get_basic_bin_info('id' => $sinfo->{bin_id});
1372 map { $sinfo->{"${_}_description"} = $sinfo->{"${_}description"} = $bin_info->{"${_}_description"} } qw(bin warehouse);
1375 $main::lxdebug->leave_sub();
1378 sub stock_in_out_form {
1379 $main::lxdebug->enter_sub();
1381 my $form = $main::form;
1383 if ($form->{in_out} eq 'out') {
1389 $main::lxdebug->leave_sub();
1392 sub redo_stock_info {
1393 $main::lxdebug->enter_sub();
1397 my $form = $main::form;
1399 my @non_empty = grep { $_->{qty} } @{ $params{stock_info} };
1401 if ($params{add_empty_row}) {
1403 'warehouse_id' => scalar(@non_empty) ? $non_empty[-1]->{warehouse_id} : undef,
1404 'bin_id' => scalar(@non_empty) ? $non_empty[-1]->{bin_id} : undef,
1408 @{ $params{stock_info} } = @non_empty;
1410 $main::lxdebug->leave_sub();
1413 sub update_stock_in {
1414 $main::lxdebug->enter_sub();
1416 my $form = $main::form;
1417 my %myconfig = %main::myconfig;
1419 my $stock_info = [];
1421 foreach my $i (1..$form->{rowcount}) {
1422 $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
1423 push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(warehouse_id bin_id chargenumber
1424 bestbefore qty unit delivery_order_items_stock_id) };
1427 display_stock_in_form($stock_info);
1429 $main::lxdebug->leave_sub();
1433 $main::lxdebug->enter_sub();
1435 my $form = $main::form;
1437 my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
1439 display_stock_in_form($stock_info);
1441 $main::lxdebug->leave_sub();
1444 sub display_stock_in_form {
1445 $main::lxdebug->enter_sub();
1447 my $stock_info = shift;
1449 my $form = $main::form;
1450 my %myconfig = %main::myconfig;
1451 my $locale = $main::locale;
1453 $form->{title} = $locale->text('Stock');
1455 my $part_info = IC->get_basic_part_info('id' => $form->{parts_id});
1457 # Standardlagerplatz für Standard-Auslagern verwenden, falls keiner für die Ware explizit definiert wurde
1458 if ($::instance_conf->get_transfer_default_use_master_default_bin) {
1459 $part_info->{warehouse_id} ||= $::instance_conf->get_warehouse_id;
1460 $part_info->{bin_id} ||= $::instance_conf->get_bin_id;
1463 my $units = AM->retrieve_units(\%myconfig, $form);
1464 # der zweite Parameter von unit_select_data gibt den default-Namen (selected) vor
1465 my $units_data = AM->unit_select_data($units, $form->{do_unit}, undef, $part_info->{unit});
1467 $form->get_lists('warehouses' => { 'key' => 'WAREHOUSES',
1468 'bins' => 'BINS' });
1470 redo_stock_info('stock_info' => $stock_info, 'add_empty_row' => !$form->{delivered});
1472 get_basic_bin_wh_info($stock_info);
1474 $form->header(no_layout => 1);
1475 print $form->parse_html_template('do/stock_in_form', { 'UNITS' => $units_data,
1476 'STOCK_INFO' => $stock_info,
1477 'PART_INFO' => $part_info, });
1479 $main::lxdebug->leave_sub();
1482 sub _stock_in_out_set_qty_display {
1483 my $stock_info = shift;
1485 my $all_units = AM->retrieve_all_units();
1486 my $sum = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
1487 my $amount_unit = $all_units->{$form->{"partunit"}}->{base_unit};
1488 $form->{qty_display} = $form->format_amount(\%::myconfig, AM->convert_unit($amount_unit, $form->{"do_unit"}) * $sum * 1) . ' ' . $form->{"do_unit"};
1492 $main::lxdebug->enter_sub();
1494 my $form = $main::form;
1495 my %myconfig = %main::myconfig;
1497 my $stock_info = [];
1499 foreach my $i (1..$form->{rowcount}) {
1500 $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
1502 next if ($form->{"qty_$i"} <= 0);
1504 push @{ $stock_info }, { map { $_ => $form->{"${_}_${i}"} } qw(delivery_order_items_stock_id warehouse_id bin_id chargenumber bestbefore qty unit) };
1507 $form->{stock} = SL::YAML::Dump($stock_info);
1509 _stock_in_out_set_qty_display($stock_info);
1511 my $do_qty = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
1512 my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
1515 print $form->parse_html_template('do/set_stock_in_out', {
1516 qty_matches => $do_qty == $transfer_qty,
1519 $main::lxdebug->leave_sub();
1522 sub stock_out_form {
1523 $main::lxdebug->enter_sub();
1525 my $form = $main::form;
1526 my %myconfig = %main::myconfig;
1527 my $locale = $main::locale;
1529 $form->{title} = $locale->text('Release From Stock');
1531 my $part_info = IC->get_basic_part_info('id' => $form->{parts_id});
1533 my $units = AM->retrieve_units(\%myconfig, $form);
1534 my $units_data = AM->unit_select_data($units, undef, undef, $part_info->{unit});
1536 my @contents = DO->get_item_availability('parts_id' => $form->{parts_id});
1538 my $stock_info = DO->unpack_stock_information('packed' => $form->{stock});
1540 if (!$form->{delivered}) {
1541 foreach my $row (@contents) {
1542 $row->{available_qty} = $form->format_amount(\%::myconfig, $row->{qty} * 1) . ' ' . $part_info->{unit};
1544 foreach my $sinfo (@{ $stock_info }) {
1545 next if (($row->{bin_id} != $sinfo->{bin_id}) ||
1546 ($row->{warehouse_id} != $sinfo->{warehouse_id}) ||
1547 ($row->{chargenumber} ne $sinfo->{chargenumber}) ||
1548 ($row->{bestbefore} ne $sinfo->{bestbefore}));
1550 map { $row->{"stock_$_"} = $sinfo->{$_} } qw(qty unit error delivery_order_items_stock_id);
1555 get_basic_bin_wh_info($stock_info);
1557 foreach my $sinfo (@{ $stock_info }) {
1558 map { $sinfo->{"stock_$_"} = $sinfo->{$_} } qw(qty unit);
1562 $form->header(no_layout => 1);
1563 print $form->parse_html_template('do/stock_out_form', { 'UNITS' => $units_data,
1564 'WHCONTENTS' => $form->{delivered} ? $stock_info : \@contents,
1565 'PART_INFO' => $part_info, });
1567 $main::lxdebug->leave_sub();
1571 $main::lxdebug->enter_sub();
1573 my $form = $main::form;
1574 my %myconfig = %main::myconfig;
1575 my $locale = $main::locale;
1577 my $stock_info = [];
1579 foreach my $i (1 .. $form->{rowcount}) {
1580 $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
1582 next if ($form->{"qty_$i"} <= 0);
1584 push @{ $stock_info }, {
1585 'warehouse_id' => $form->{"warehouse_id_$i"},
1586 'bin_id' => $form->{"bin_id_$i"},
1587 'chargenumber' => $form->{"chargenumber_$i"},
1588 'bestbefore' => $form->{"bestbefore_$i"},
1589 'qty' => $form->{"qty_$i"},
1590 'unit' => $form->{"unit_$i"},
1592 'delivery_order_items_stock_id' => $form->{"delivery_order_items_stock_id_$i"},
1596 my @errors = DO->check_stock_availability('requests' => $stock_info,
1597 'parts_id' => $form->{parts_id});
1599 $form->{stock} = SL::YAML::Dump($stock_info);
1602 $form->{ERRORS} = [];
1603 map { push @{ $form->{ERRORS} }, $locale->text('Error in row #1: The quantity you entered is bigger than the stocked quantity.', $_->{row}); } @errors;
1604 stock_in_out_form();
1607 _stock_in_out_set_qty_display($stock_info);
1609 my $do_qty = AM->sum_with_unit($::form->parse_amount(\%::myconfig, $::form->{do_qty}), $::form->{do_unit});
1610 my $transfer_qty = AM->sum_with_unit(map { $_->{qty}, $_->{unit} } @{ $stock_info });
1613 print $form->parse_html_template('do/set_stock_in_out', {
1614 qty_matches => $do_qty == $transfer_qty,
1618 $main::lxdebug->leave_sub();
1622 $main::lxdebug->enter_sub();
1624 my $form = $main::form;
1625 my %myconfig = %main::myconfig;
1626 my $locale = $main::locale;
1628 if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
1629 $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred in.'));
1632 save(no_redirect => 1);
1634 my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_in_${_}"} } (1 .. $form->{rowcount});
1638 my $units = AM->retrieve_units(\%myconfig, $form);
1639 my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
1642 $form->{ERRORS} = [];
1644 foreach my $i (1 .. $form->{rowcount}) {
1645 next unless ($form->{"id_$i"} && $form->{"stock_in_$i"});
1647 my $row_sum_base_qty = 0;
1648 my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
1650 foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_in_$i"}) }) {
1651 $request->{parts_id} = $form->{"id_$i"};
1652 $row_sum_base_qty += $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
1654 $request->{project_id} = $form->{"project_id_$i"} || $form->{globalproject_id};
1656 push @all_requests, $request;
1659 next if (0 == $row_sum_base_qty);
1661 my $do_base_qty = $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
1663 # if ($do_base_qty != $row_sum_base_qty) {
1664 # push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no stock at all or the full quantity of #2 #3.',
1665 # $i, $form->{"qty_$i"}, $form->{"unit_$i"});
1669 if (@{ $form->{ERRORS} }) {
1670 push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
1672 set_headings('edit');
1674 $main::lxdebug->leave_sub();
1676 $::dispatcher->end_request;
1680 DO->transfer_in_out('direction' => 'in',
1681 'requests' => \@all_requests);
1683 SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
1685 flash_later('info', $locale->text("Transfer successful"));
1686 $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id});
1689 $main::lxdebug->leave_sub();
1693 $main::lxdebug->enter_sub();
1695 my $form = $main::form;
1696 my %myconfig = %main::myconfig;
1697 my $locale = $main::locale;
1699 if ($form->{id} && DO->is_marked_as_delivered(id => $form->{id})) {
1700 $form->show_generic_error($locale->text('The parts for this delivery order have already been transferred out.'));
1703 save(no_redirect => 1);
1705 my @part_ids = map { $form->{"id_${_}"} } grep { $form->{"id_${_}"} && $form->{"stock_out_${_}"} } (1 .. $form->{rowcount});
1709 my $units = AM->retrieve_units(\%myconfig, $form);
1710 my %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
1713 $form->{ERRORS} = [];
1715 foreach my $i (1 .. $form->{rowcount}) {
1716 next unless ($form->{"id_$i"} && $form->{"stock_out_$i"});
1718 my $row_sum_base_qty = 0;
1719 my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
1721 foreach my $request (@{ DO->unpack_stock_information('packed' => $form->{"stock_out_$i"}) }) {
1722 $request->{parts_id} = $form->{"id_$i"};
1723 $request->{base_qty} = $request->{qty} * $units->{$request->{unit}}->{factor} / $base_unit_factor;
1724 $request->{project_id} = $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id};
1726 my $map_key = join '--', ($form->{"id_$i"}, @{$request}{qw(warehouse_id bin_id chargenumber bestbefore)});
1728 $request_map{$map_key} ||= $request;
1729 $request_map{$map_key}->{sum_base_qty} ||= 0;
1730 $request_map{$map_key}->{sum_base_qty} += $request->{base_qty};
1731 $row_sum_base_qty += $request->{base_qty};
1733 push @all_requests, $request;
1736 next if (0 == $row_sum_base_qty);
1738 my $do_base_qty = $form->{"qty_$i"} * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
1740 # if ($do_base_qty != $row_sum_base_qty) {
1741 # push @{ $form->{ERRORS} }, $locale->text('Error in position #1: You must either assign no transfer at all or the full quantity of #2 #3.',
1742 # $i, $form->{"qty_$i"}, $form->{"unit_$i"});
1747 my @bin_ids = map { $_->{bin_id} } values %request_map;
1748 my %bin_info_map = WH->get_basic_bin_info('id' => \@bin_ids);
1749 my @contents = DO->get_item_availability('parts_id' => \@part_ids);
1751 foreach my $inv (@contents) {
1752 my $map_key = join '--', @{$inv}{qw(parts_id warehouse_id bin_id chargenumber bestbefore)};
1754 next unless ($request_map{$map_key});
1756 my $request = $request_map{$map_key};
1757 $request->{ok} = $request->{sum_base_qty} <= $inv->{qty};
1760 foreach my $request (values %request_map) {
1761 next if ($request->{ok});
1763 my $pinfo = $part_info_map{$request->{parts_id}};
1764 my $binfo = $bin_info_map{$request->{bin_id}};
1766 if ($::instance_conf->get_show_bestbefore) {
1767 push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, #5, for the transfer of #6.",
1768 $pinfo->{description},
1769 $binfo->{warehouse_description},
1770 $binfo->{bin_description},
1771 $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
1772 $request->{bestbefore} ? $locale->text('bestbefore #1', $request->{bestbefore}) : $locale->text('no bestbefore'),
1773 $form->format_amount(\%::myconfig, $request->{sum_base_qty}) . ' ' . $pinfo->{unit});
1775 push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, for the transfer of #5.",
1776 $pinfo->{description},
1777 $binfo->{warehouse_description},
1778 $binfo->{bin_description},
1779 $request->{chargenumber} ? $locale->text('chargenumber #1', $request->{chargenumber}) : $locale->text('no chargenumber'),
1780 $form->format_amount(\%::myconfig, $request->{sum_base_qty}) . ' ' . $pinfo->{unit});
1785 if (@{ $form->{ERRORS} }) {
1786 push @{ $form->{ERRORS} }, $locale->text('The delivery order has not been marked as delivered. The warehouse contents have not changed.');
1788 set_headings('edit');
1790 $main::lxdebug->leave_sub();
1792 $::dispatcher->end_request;
1795 DO->transfer_in_out('direction' => 'out',
1796 'requests' => \@all_requests);
1798 SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
1800 flash_later('info', $locale->text("Transfer successful"));
1801 $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id});
1804 $main::lxdebug->leave_sub();
1808 $main::lxdebug->enter_sub();
1810 my $form = $main::form;
1812 DO->close_orders('ids' => [ $form->{id} ]);
1814 $form->{closed} = 1;
1818 $main::lxdebug->leave_sub();
1822 $::lxdebug->enter_sub;
1827 retrieve_partunits();
1829 my $new_rowcount = $::form->{"rowcount"} * 1 + 1;
1830 $::form->{"project_id_${new_rowcount}"} = $::form->{"globalproject_id"};
1832 $::form->language_payment(\%::myconfig);
1834 Common::webdav_folder($::form);
1837 display_row(++$::form->{rowcount});
1840 $::lxdebug->leave_sub;
1844 call_sub($main::form->{yes_nextsub});
1848 call_sub($main::form->{no_nextsub});
1852 call_sub($main::form->{update_nextsub} || $main::form->{nextsub} || 'update_delivery_order');
1856 my $form = $main::form;
1857 my $locale = $main::locale;
1859 foreach my $action (qw(update print save transfer_out transfer_out_default sort
1860 transfer_in transfer_in_default mark_closed save_as_new invoice delete)) {
1861 if ($form->{"action_${action}"}) {
1867 $form->error($locale->text('No action defined.'));
1870 sub transfer_out_default {
1871 $main::lxdebug->enter_sub();
1873 my $form = $main::form;
1875 transfer_in_out_default('direction' => 'out');
1877 $main::lxdebug->leave_sub();
1880 sub transfer_in_default {
1881 $main::lxdebug->enter_sub();
1883 my $form = $main::form;
1885 transfer_in_out_default('direction' => 'in');
1887 $main::lxdebug->leave_sub();
1890 # Falls das Standardlagerverfahren aktiv ist, wird
1891 # geprüft, ob alle Standardlagerplätze für die Auslager-
1892 # artikel vorhanden sind UND ob die Warenmenge ausreicht zum
1893 # Auslagern. Falls nicht wird entsprechend eine Fehlermeldung
1894 # generiert. Offen Chargennummer / bestbefore wird nicht berücksichtigt
1895 sub transfer_in_out_default {
1896 $main::lxdebug->enter_sub();
1898 my $form = $main::form;
1899 my %myconfig = %main::myconfig;
1900 my $locale = $main::locale;
1903 my (%missing_default_bins, %qty_parts, @all_requests, %part_info_map, $default_warehouse_id, $default_bin_id);
1905 Common::check_params(\%params, qw(direction));
1907 # entsprechende defaults holen, falls standardlagerplatz verwendet werden soll
1908 if ($::instance_conf->get_transfer_default_use_master_default_bin) {
1909 $default_warehouse_id = $::instance_conf->get_warehouse_id;
1910 $default_bin_id = $::instance_conf->get_bin_id;
1914 my @part_ids = map { $form->{"id_${_}"} } (1 .. $form->{rowcount});
1916 my $units = AM->retrieve_units(\%myconfig, $form);
1917 %part_info_map = IC->get_basic_part_info('id' => \@part_ids);
1918 foreach my $i (1 .. $form->{rowcount}) {
1919 next unless ($form->{"id_$i"});
1920 my $base_unit_factor = $units->{ $part_info_map{$form->{"id_$i"}}->{unit} }->{factor} || 1;
1921 my $qty = $form->parse_amount(\%myconfig, $form->{"qty_$i"}) * $units->{$form->{"unit_$i"}}->{factor} / $base_unit_factor;
1923 $form->show_generic_error($locale->text("Cannot transfer negative entries." )) if ($qty < 0);
1924 # if we do not want to transfer services and this part is a service, set qty to zero
1925 # ... and do not create a hash entry in %qty_parts below (will skip check for bins for the transfer == out case)
1926 # ... 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)
1928 $qty = 0 if (!$::instance_conf->get_transfer_default_services && $part_info_map{$form->{"id_$i"}}->{part_type} eq 'service');
1929 $qty_parts{$form->{"id_$i"}} += $qty;
1931 delete $qty_parts{$form->{"id_$i"}} unless $qty_parts{$form->{"id_$i"}};
1932 undef $form->{"stock_in_$i"};
1935 $part_info_map{$form->{"id_$i"}}{bin_id} ||= $default_bin_id;
1936 $part_info_map{$form->{"id_$i"}}{warehouse_id} ||= $default_warehouse_id;
1938 push @all_requests, ($qty == 0) ? { } : {
1939 'chargenumber' => '', #?? die müsste entsprechend geholt werden
1940 #'bestbefore' => undef, # TODO wird nicht berücksichtigt
1941 'bin_id' => $part_info_map{$form->{"id_$i"}}{bin_id},
1943 'parts_id' => $form->{"id_$i"},
1944 'comment' => $locale->text("Default transfer delivery order"),
1945 'unit' => $part_info_map{$form->{"id_$i"}}{unit},
1946 'warehouse_id' => $part_info_map{$form->{"id_$i"}}{warehouse_id},
1947 'oe_id' => $form->{id},
1948 'project_id' => $form->{"project_id_$i"} ? $form->{"project_id_$i"} : $form->{globalproject_id}
1952 # jetzt wird erst überprüft, ob die Stückzahl entsprechend stimmt.
1953 # check if bin (transfer in and transfer out and qty (transfer out) is correct
1954 foreach my $key (keys %qty_parts) {
1956 $missing_default_bins{$key}{missing_bin} = 1 unless ($part_info_map{$key}{bin_id});
1957 next unless ($part_info_map{$key}{bin_id}); # abbruch
1959 if ($params{direction} eq 'out') { # wird nur für ausgehende Mengen benötigt
1960 my ($max_qty, $error) = WH->get_max_qty_parts_bin(parts_id => $key, bin_id => $part_info_map{$key}{bin_id});
1962 # wir können nicht entscheiden, welche charge oder mhd (bestbefore) ausgewählt sein soll
1963 # deshalb rückmeldung nach oben geben, manuell auszulagern
1964 # TODO Bei nur einem Treffer mit Charge oder bestbefore wäre das noch möglich
1965 $missing_default_bins{$key}{chargenumber} = 1;
1967 if ($max_qty < $qty_parts{$key}){
1968 $missing_default_bins{$key}{missing_qty} = $max_qty - $qty_parts{$key};
1974 # Abfrage für Fehlerbehandlung (nur bei direction == out)
1975 if (scalar (keys %missing_default_bins)) {
1977 foreach my $fehler (keys %missing_default_bins) {
1979 my $ware = WH->get_part_description(parts_id => $fehler);
1980 if ($missing_default_bins{$fehler}{missing_bin}){
1981 $fehlertext .= "Kein Standardlagerplatz definiert bei $ware <br>";
1983 if ($missing_default_bins{$fehler}{missing_qty}) { # missing_qty
1984 $fehlertext .= "Es fehlen " . $missing_default_bins{$fehler}{missing_qty}*-1 .
1985 " von $ware auf dem Standard-Lagerplatz " . $part_info_map{$fehler}{bin} . " zum Auslagern<br>";
1987 if ($missing_default_bins{$fehler}{chargenumber}){
1988 $fehlertext .= "Die Ware hat eine Chargennummer oder eine Mindesthaltbarkeit definiert.
1989 Hier kann man nicht automatisch entscheiden.
1990 Bitte diesen Lieferschein manuell auslagern.
1993 # auslagern soll immer gehen, auch wenn nicht genügend auf lager ist.
1994 # der lagerplatz ist hier extra konfigurierbar, bspw. Lager-Korrektur mit
1995 # Lagerplatz Lagerplatz-Korrektur
1996 my $default_warehouse_id_ignore_onhand = $::instance_conf->get_warehouse_id_ignore_onhand;
1997 my $default_bin_id_ignore_onhand = $::instance_conf->get_bin_id_ignore_onhand;
1998 if ($::instance_conf->get_transfer_default_ignore_onhand && $default_bin_id_ignore_onhand) {
1999 # entsprechende defaults holen
2000 # falls chargenumber, bestbefore oder anzahl nicht stimmt, auf automatischen
2001 # lagerplatz wegbuchen!
2002 foreach (@all_requests) {
2003 if ($_->{parts_id} eq $fehler){
2004 $_->{bin_id} = $default_bin_id_ignore_onhand;
2005 $_->{warehouse_id} = $default_warehouse_id_ignore_onhand;
2009 #$main::lxdebug->message(0, 'Fehlertext: ' . $fehlertext);
2010 $form->show_generic_error($locale->text("Cannot transfer. <br> Reason:<br>#1", $fehlertext ));
2016 # hier der eigentliche fallunterschied für in oder out
2017 my $prefix = $params{direction} eq 'in' ? 'in' : 'out';
2019 # dieser array_ref ist für DO->save da:
2020 # einmal die all_requests in YAML verwandeln, damit delivery_order_items_stock
2021 # gefüllt werden kann.
2022 # could be dumped to the form in the first loop,
2023 # but maybe bin_id and warehouse_id has changed to the "korrekturlager" with
2024 # allowed negative qty ($::instance_conf->get_warehouse_id_ignore_onhand) ...
2026 foreach (@all_requests){
2028 next unless scalar(%{ $_ });
2029 $form->{"stock_${prefix}_$i"} = SL::YAML::Dump([$_]);
2032 save(no_redirect => 1); # Wir können auslagern, deshalb beleg speichern
2033 # und in delivery_order_items_stock speichern
2035 # ... and fill back the persistent dois_id for inventory fk
2036 undef (@all_requests);
2037 foreach my $i (1 .. $form->{rowcount}) {
2038 next unless ($form->{"id_$i"} && $form->{"stock_${prefix}_$i"});
2039 push @all_requests, @{ DO->unpack_stock_information('packed' => $form->{"stock_${prefix}_$i"}) };
2041 DO->transfer_in_out('direction' => $prefix,
2042 'requests' => \@all_requests);
2044 SL::DB::DeliveryOrder->new(id => $form->{id})->load->update_attributes(delivered => 1);
2046 $form->{callback} = 'do.pl?action=edit&type=sales_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'out';
2047 $form->{callback} = 'do.pl?action=edit&type=purchase_delivery_order&id=' . $form->escape($form->{id}) if $params{direction} eq 'in';
2053 $main::lxdebug->enter_sub();
2057 my $form = $main::form;
2060 save(no_redirect => 1); # has to be done, at least for newly added positions
2062 # hashify partnumbers, positions. key is delivery_order_items_id
2063 for my $i (1 .. ($form->{rowcount}) ) {
2064 $temp_hash{$form->{"delivery_order_items_id_$i"}} = { runningnumber => $form->{"runningnumber_$i"}, partnumber => $form->{"partnumber_$i"} };
2065 if ($form->{id} && $form->{"discount_$i"}) {
2066 # prepare_order assumes a db value if there is a form->id and multiplies *100
2067 # We hope for new controller code (no more format_amount/parse_amount distinction)
2068 $form->{"discount_$i"} /=100;
2071 # naturally sort partnumbers and get a sorted array of doi_ids
2072 my @sorted_doi_ids = sort { Sort::Naturally::ncmp($temp_hash{$a}->{"partnumber"}, $temp_hash{$b}->{"partnumber"}) } keys %temp_hash;
2077 for (@sorted_doi_ids) {
2078 $form->{"runningnumber_$temp_hash{$_}->{runningnumber}"} = $new_number;
2081 # all parse_amounts changes are in form (i.e. , to .) therefore we need
2082 # another format_amount to change it back, for the next save ;-(
2083 # works great except for row discounts (see above comment)
2087 $main::lxdebug->leave_sub();
2099 do.pl - Script for all calls to delivery order
2107 Sorts all position with Natural Sort. Can be activated in form_footer.html like this
2108 C<E<lt>input class="submit" type="submit" name="action_sort" id="sort_button" value="[% 'Sort and Save' | $T8 %]"E<gt>>
2114 Sort and Save can be implemented as an optional button if configuration ca be set by client config.
2115 Example coding for database scripts and templates in (git show af2f24b8), check also
2116 autogeneration for rose (scripts/rose_auto_create_model.pl --h)