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