dolibarr 25.0.0-alpha
card.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2003-2008 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2005-2016 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2005 Simon TOSSER <simon@kornog-computing.com>
5 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
6 * Copyright (C) 2011-2017 Juanjo Menent <jmenent@2byte.es>
7 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
8 * Copyright (C) 2013 Marcos García <marcosgdf@gmail.com>
9 * Copyright (C) 2014 Cedric GROSS <c.gross@kreiz-it.fr>
10 * Copyright (C) 2014-2017 Francis Appels <francis.appels@yahoo.com>
11 * Copyright (C) 2015 Claudio Aschieri <c.aschieri@19.coop>
12 * Copyright (C) 2016-2018 Ferran Marcet <fmarcet@2byte.es>
13 * Copyright (C) 2016 Yasser Carreón <yacasia@gmail.com>
14 * Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
15 * Copyright (C) 2020 Lenin Rivas <lenin@leninrivas.com>
16 * Copyright (C) 2022 Josep Lluís Amador <joseplluis@lliuretic.cat>
17 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
18 * Copyright (C) 2025 Nick Fragoulis
19 * Copyright (C) 2025 Charlene Benke <charlene@patas-monkey.com>
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 3 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program. If not, see <https://www.gnu.org/licenses/>.
33 */
34
41// Load Dolibarr environment
42require '../main.inc.php';
52require_once DOL_DOCUMENT_ROOT . '/core/class/html.formfile.class.php';
53require_once DOL_DOCUMENT_ROOT . '/expedition/class/expedition.class.php';
54require_once DOL_DOCUMENT_ROOT . '/product/class/html.formproduct.class.php';
55require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
56require_once DOL_DOCUMENT_ROOT . '/core/lib/sendings.lib.php';
57require_once DOL_DOCUMENT_ROOT . '/core/modules/expedition/modules_expedition.php';
58require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
59require_once DOL_DOCUMENT_ROOT . '/product/stock/class/entrepot.class.php';
60require_once DOL_DOCUMENT_ROOT . '/product/stock/class/productlot.class.php';
61require_once DOL_DOCUMENT_ROOT . '/commande/class/commande.class.php';
62if (isModEnabled("product") || isModEnabled("service")) {
63 require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
64}
65if (isModEnabled("propal")) {
66 require_once DOL_DOCUMENT_ROOT . '/comm/propal/class/propal.class.php';
67}
68if (isModEnabled('productbatch')) {
69 require_once DOL_DOCUMENT_ROOT . '/product/class/productbatch.class.php';
70}
71if (isModEnabled('project')) {
72 require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
73 require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
74}
75
76// Load translation files required by the page
77$langs->loadLangs(array("sendings", "companies", "bills", 'orders', 'stocks', 'other', 'propal', 'productbatch'));
78
79if (isModEnabled('incoterm')) {
80 $langs->load('incoterm');
81}
82if (isModEnabled('productbatch')) {
83 $langs->load('productbatch');
84}
85
86
87$origin = GETPOST('origin', 'alpha'); // Example: commande, propal
88$origin_id = GETPOSTINT('origin_id');
89$id = GETPOSTINT('id');
90
91
92if (empty($origin_id) && !empty($origin)) {
93 $origin_id = GETPOSTINT('origin_id'); // Id of order or propal
94}
95if (empty($origin_id) && !empty($origin)) {
96 $origin_id = GETPOSTINT('object_id'); // Id of order or propal
97}
98$socid = GETPOSTINT('socid');
99$ref = GETPOST('ref', 'alpha');
100$line_id = GETPOSTINT('lineid');
101$facid = GETPOSTINT('facid');
102$contactid = GETPOSTINT('contactid');
103$projectid = GETPOSTINT('projectid');
104$action = GETPOST('action', 'alpha');
105$confirm = GETPOST('confirm', 'alpha');
106$cancel = GETPOST('cancel', 'alpha');
107$rank = (GETPOSTINT('rank') > 0) ? GETPOSTINT('rank') : -1;
108$lineid = GETPOSTINT('lineid');
109$backtopage = GETPOST('backtopage', 'alpha');
110
111//PDF
112$hidedetails = (GETPOSTINT('hidedetails') ? GETPOSTINT('hidedetails') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0));
113$hidedesc = (GETPOSTINT('hidedesc') ? GETPOSTINT('hidedesc') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0));
114$hideref = (GETPOSTINT('hideref') ? GETPOSTINT('hideref') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0));
115
116$object = new Expedition($db);
117$objectorder = new Commande($db);
118
119// fetch optionals attributes and labels
120$extrafields->fetch_name_optionals_label($object->table_element);
121$extrafields->fetch_name_optionals_label($object->table_element_line);
122$extrafields->fetch_name_optionals_label($objectorder->table_element_line);
123
124// Load object. Make an object->fetch
125include DOL_DOCUMENT_ROOT . '/core/actions_fetchobject.inc.php'; // Must be 'include', not 'include_once'
126
127if (empty($origin) && !empty($object->origin_type) && $object->origin_id > 0) {
128 $origin = $object->origin_type;
129} elseif (empty($origin) && is_string($object->origin) && $object->origin !== '' && $object->origin_id > 0) {
130 $origin = $object->origin;
131}
132if (empty($origin_id) && $object->origin_id > 0) {
133 $origin_id = $object->origin_id;
134}
135$origin_type = is_string($origin) ? $origin : '';
136
137// Permissions / Rights
138$usercanread = $user->hasRight("expedition", "lire");
139$usercancreate = $user->hasRight("expedition", "creer");
140$usercandelete = $user->hasRight("expedition", "supprimer");
141
142// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
143$hookmanager->initHooks(array('expeditioncard', 'globalcard'));
144
145$date_delivery = dol_mktime(GETPOSTINT('date_deliveryhour'), GETPOSTINT('date_deliverymin'), 0, GETPOSTINT('date_deliverymonth'), GETPOSTINT('date_deliveryday'), GETPOSTINT('date_deliveryyear'));
146
147$date_shipping = dol_mktime(GETPOSTINT('date_shippinghour'), GETPOSTINT('date_shippingmin'), 0, GETPOSTINT('date_shippingmonth'), GETPOSTINT('date_shippingday'), GETPOSTINT('date_shippingyear'));
148
149
150// Security check
151
152if ($user->socid) {
153 $socid = $user->socid;
154}
155
156$result = restrictedArea($user, 'expedition', $object->id, '');
157
158$permissiondellink = $user->hasRight('expedition', 'delivery', 'creer'); // Used by the include of actions_dellink.inc.php
159$permissiontoadd = $user->hasRight('expedition', 'creer');
160$permissiontoedit = $usercancreate; // Used by the include of actions_lineupdown.inc.php
161$permissiontoeditextra = $permissiontoadd;
162if (GETPOST('attribute', 'aZ09') && isset($extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')])) {
163 // For action 'update_extras', is there a specific permission set for the attribute to update
164 $permissiontoeditextra = dol_eval((string) $extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')]);
165}
166
167$upload_dir = $conf->expedition->dir_output . '/sending';
168
169$editColspan = 0;
170$objectsrc = null;
171$typeobject = null;
172$ref_customer = null;
173$shipping_method_id = null;
174$warehouse_id = null;
175$note_public = null;
176$note_private = null;
177
178/*
179 * Actions
180 */
181
182$error = 0;
183$parameters = array();
184$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
185if ($reshook < 0) {
186 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
187}
188
189if (empty($reshook)) {
190 $backurlforlist = DOL_URL_ROOT.'/expedition/list.php';
191
192 if (empty($backtopage) || ($cancel && empty($id))) {
193 if (empty($backtopage) || ($cancel && strpos($backtopage, '__ID__'))) {
194 if (empty($id) && (($action != 'add' && $action != 'create') || $cancel)) {
195 $backtopage = $backurlforlist;
196 } else {
197 $backtopage = DOL_URL_ROOT.'/expedition/card.php?id='.((!empty($id) && $id > 0) ? $id : '__ID__');
198 }
199 }
200 }
201
202 if ($cancel) {
203 if (!empty($backtopageforcancel)) {
204 header("Location: ".$backtopageforcancel);
205 exit;
206 } elseif (!empty($backtopage)) {
207 header("Location: ".$backtopage);
208 exit;
209 }
210 $action = '';
211 }
212
213
214 if ($cancel) {
215 if ($origin_id > 0) {
216 if ($origin == 'commande') {
217 header("Location: " . DOL_URL_ROOT . '/expedition/shipment.php?id=' . ((int) $origin_id));
218 exit;
219 }
220 } else {
221 $action = '';
222 $object->fetch($object->id); // show shipment also after canceling modification
223 }
224 }
225
226 include DOL_DOCUMENT_ROOT . '/core/actions_dellink.inc.php'; // Must be 'include', not 'include_once'
227
228 // Actions to build doc
229 include DOL_DOCUMENT_ROOT . '/core/actions_builddoc.inc.php';
230
231 // Back to draft
232 if ($action == 'setdraft' && $permissiontoadd) {
233 $object->fetch($id);
234 $result = $object->setDraft($user, 0);
235 if ($result < 0) {
236 setEventMessages($object->error, $object->errors, 'errors');
237 } else {
238 header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id);
239 exit;
240 }
241 }
242 // Reopen
243 if ($action == 'reopen' && $permissiontoadd) {
244 $object->fetch($id);
245 $result = $object->reOpen();
246 if ($result < 0) {
247 setEventMessages($object->error, $object->errors, 'errors');
248 } else {
249 header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id);
250 exit;
251 }
252 }
253
254 // Set incoterm
255 if ($action == 'set_incoterms' && isModEnabled('incoterm') && $permissiontoadd) {
256 $result = $object->setIncoterms(GETPOSTINT('incoterm_id'), GETPOST('location_incoterms'));
257 }
258
259 if ($action == 'classin' && $permissiontoadd) {
260 // Link to a project
261 $result = $object->setProject(GETPOSTINT('projectid'));
262 if ($result < 0) {
263 setEventMessages($object->error, $object->errors, 'errors');
264 }
265 }
266
267 if ($action == 'setref_customer' && $permissiontoadd) {
268 $result = $object->fetch($id);
269 if ($result < 0) {
270 setEventMessages($object->error, $object->errors, 'errors');
271 }
272
273 $result = $object->setValueFrom('ref_customer', GETPOST('ref_customer', 'alpha'), '', null, 'text', '', $user, 'SHIPPING_MODIFY');
274 if ($result < 0) {
275 setEventMessages($object->error, $object->errors, 'errors');
276 $action = 'editref_customer';
277 } else {
278 header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id);
279 exit;
280 }
281 }
282
283 if ($action == 'update_extras' && $permissiontoeditextra) {
284 $object->oldcopy = dol_clone($object, 2);
285
286 $attribute_name = GETPOST('attribute', 'aZ09');
287
288 // Fill array 'array_options' with data from update form
289 $ret = $extrafields->setOptionalsFromPost(null, $object, $attribute_name);
290 if ($ret < 0) {
291 $error++;
292 }
293
294 if (!$error) {
295 // Actions on extra fields
296 $result = $object->updateExtraField($attribute_name, 'SHIPPING_MODIFY');
297 if ($result < 0) {
298 setEventMessages($object->error, $object->errors, 'errors');
299 $error++;
300 }
301 }
302
303 if ($error) {
304 $action = 'edit_extras';
305 }
306 }
307
308 // Create shipment
309 if ($action == 'add' && $permissiontoadd) {
310 $db->begin();
311
312 if ($origin && $origin_id > 0) {
313 // We will loop on each line of the original document to complete the shipping object with various info and quantity to deliver
314 $classname = ucfirst($origin);
315 $objectsrc = new $classname($db);
316 '@phan-var-force Facture|Commande $objectsrc';
317 $objectsrc->fetch($origin_id);
318 $object->socid = $objectsrc->socid;
319 }
320
321 if (GETPOSTINT('socid') < 1 && $object->socid < 1) {
322 $error++;
323 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ThirdParty")), null, 'errors');
324 $action = 'create';
325 }
326
327 if (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
328 $object->socid = GETPOSTINT('socid');
329 $object->fetch_thirdparty();
330
331 $object->origin = $origin; // deprecated
332 $object->origin_type = $origin_type;
333 $object->origin_id = $origin_id;
334 $object->fk_project = GETPOSTINT('projectid');
335 $object->weight = GETPOST('weight') == '' ? '' : GETPOSTFLOAT('weight');
336 $object->sizeH = GETPOST('sizeH') == '' ? '' : GETPOSTFLOAT('sizeH');
337 $object->sizeW = GETPOST('sizeW') == '' ? '' : GETPOSTFLOAT('sizeW');
338 $object->sizeS = GETPOST('sizeS') == '' ? '' : GETPOSTFLOAT('sizeS');
339 $object->size_units = GETPOSTINT('size_units');
340 $object->weight_units = GETPOSTINT('weight_units');
341 $object->ref_customer = GETPOST('ref_customer', 'alpha');
342 $object->model_pdf = GETPOST('model');
343 $object->date_delivery = $date_delivery; // Date delivery planned
344 $object->date_shipping = $date_shipping; // Sending date
345 $object->shipping_method_id = GETPOSTINT('shipping_method_id');
346 $object->tracking_number = GETPOST('tracking_number', 'alpha');
347 $object->note = GETPOST('note_private', 'restricthtml'); // deprecated
348 $object->note_private = GETPOST('note_private', 'restricthtml');
349 $object->note_public = GETPOST('note_public', 'restricthtml');
350 $object->fk_incoterms = GETPOSTINT('incoterm_id');
351 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
352
353 $product = new Product($db);
354
355 // Fill array 'array_options' with data from add form
356 $ret = $extrafields->setOptionalsFromPost(null, $object);
357 if ($ret < 0) {
358 $error++;
359 }
360
361 if (!$error) {
362 $ret = $object->create($user);
363 if ($ret <= 0) {
364 setEventMessages($object->error, $object->errors, 'errors');
365 $error++;
366 }
367 }
368 }
369
370 if ($origin && $origin_id > 0) {
371 $object->origin = $origin; // deprecated
372 $object->origin_type = $origin_type;
373 $object->origin_id = $origin_id;
374 $object->fk_project = GETPOSTINT('projectid');
375 $object->weight = GETPOST('weight') == '' ? '' : GETPOSTFLOAT('weight');
376 $object->sizeH = GETPOST('sizeH') == '' ? '' : GETPOSTFLOAT('sizeH');
377 $object->sizeW = GETPOST('sizeW') == '' ? '' : GETPOSTFLOAT('sizeW');
378 $object->sizeS = GETPOST('sizeS') == '' ? '' : GETPOSTFLOAT('sizeS');
379 $object->size_units = GETPOSTINT('size_units');
380 $object->weight_units = GETPOSTINT('weight_units');
381 $object->ref_customer = GETPOST('ref_customer', 'alpha');
382 $object->model_pdf = GETPOST('model');
383 $object->date_delivery = $date_delivery; // Date delivery planned
384 $object->date_shipping = $date_shipping; // Sending date
385 $object->shipping_method_id = GETPOSTINT('shipping_method_id');
386 $object->tracking_number = GETPOST('tracking_number', 'alpha');
387 $object->note = GETPOST('note_private', 'restricthtml'); // deprecated
388 $object->note_private = GETPOST('note_private', 'restricthtml');
389 $object->note_public = GETPOST('note_public', 'restricthtml');
390 $object->fk_incoterms = GETPOSTINT('incoterm_id');
391 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
392
393 // We will loop on each line of the original document to complete the shipping object with various info and quantity to deliver
394 $classname = ucfirst($object->origin_type);
395 $objectsrc = new $classname($db);
396 '@phan-var-force Facture|Commande $objectsrc';
397 $objectsrc->fetch($object->origin_id);
398
399 $object->socid = $objectsrc->socid;
400 $object->fk_delivery_address = $objectsrc->fk_delivery_address;
401
402 $product = new Product($db);
403
404 $batch_line = array();
405 $stockLine = array();
406 $array_options = array();
407
408 $num = count($objectsrc->lines);
409 $totalqty = 0;
410
411 $product_batch_used = array();
412
413 for ($i = 0; $i < $num; $i++) {
414 $idl = "idl" . $i;
415
416 $sub_qty = array();
417 $subtotalqty = 0;
418
419 $j = 0;
420
421 $batch = "batchl" . $i . "_0";
422 $stockLocation = "ent1" . $i . "_0";
423 $qty = "qtyl" . $i;
424
425 $is_batch_or_serial = 0;
426 if (!empty($objectsrc->lines[$i]->fk_product)) {
427 $resultFetch = $product->fetch($objectsrc->lines[$i]->fk_product, '', '', '', 1, 1, 1);
428 if ($resultFetch < 0) {
429 setEventMessages($product->error, $product->errors, 'errors');
430 }
431 $is_batch_or_serial = $product->status_batch;
432 }
433
434 // If product need a batch or serial number
435 if (isModEnabled('productbatch') && $objectsrc->lines[$i]->product_tobatch) {
436 if (GETPOSTISSET($batch)) {
437 //shipment line with batch-enable product
438 $qty .= '_' . $j;
439 while (GETPOSTISSET($batch)) {
440 // save line of detail into sub_qty
441 $sub_qty[$j]['q'] = price2num(GETPOST($qty, 'alpha'), 'MS'); // the qty we want to move for this stock record
442 $sub_qty[$j]['id_batch'] = GETPOSTINT($batch); // the id into llx_product_batch of stock record to move
443 $subtotalqty += $sub_qty[$j]['q'];
444
445 if ($is_batch_or_serial == 2 && ($sub_qty[$j]['q'] > 1 || ($sub_qty[$j]['q'] > 0 && in_array($sub_qty[$j]['id_batch'], $product_batch_used)))) {
446 setEventMessages($langs->trans("TooManyQtyForSerialNumber", $product->ref, ''), null, 'errors');
447 $totalqty = 0;
448 break 2;
449 }
450
451 if ($is_batch_or_serial == 2 && $sub_qty[$j]['q'] > 0) {
452 // we stock the batch id to test later if the same serial is shipped on another line for the same product
453 $product_batch_used[$j] = $sub_qty[$j]['id_batch'];
454 }
455
456 $j++;
457 $batch = "batchl" . $i . "_" . $j;
458 $qty = "qtyl" . $i . '_' . $j;
459 }
460
461 $batch_line[$i]['detail'] = $sub_qty; // array of details
462 $batch_line[$i]['qty'] = $subtotalqty;
463 $batch_line[$i]['ix_l'] = GETPOSTINT($idl);
464
465 $totalqty += $subtotalqty;
466 } else {
467 // No detail were provided for lots, so if a qty was provided, we can throw an error.
468 if (GETPOST($qty)) {
469 // We try to set an amount
470 // Case we don't use the list of available qty for each warehouse/lot
471 // GUI does not allow this yet
472 setEventMessages($langs->trans("StockIsRequiredToChooseWhichLotToUse") . ' (' . $langs->trans("Line") . ' ' . GETPOSTINT($idl) . ')', null, 'errors');
473 $error++;
474 }
475 }
476 } elseif (GETPOSTISSET($stockLocation)) {
477 //shipment line from multiple stock locations
478 $qty .= '_' . $j;
479 while (GETPOSTISSET($stockLocation)) {
480 // save sub line of warehouse
481 $stockLine[$i][$j]['qty'] = price2num(GETPOST($qty, 'alpha'), 'MS');
482 $stockLine[$i][$j]['warehouse_id'] = GETPOSTINT($stockLocation);
483 $stockLine[$i][$j]['ix_l'] = GETPOSTINT($idl);
484
485 $totalqty += price2num(GETPOST($qty, 'alpha'), 'MS');
486 $subtotalqty += price2num(GETPOST($qty, 'alpha'), 'MS');
487
488 $j++;
489 $stockLocation = "ent1" . $i . "_" . $j;
490 $qty = "qtyl" . $i . '_' . $j;
491 }
492 } else {
493 //shipment line for product with no batch management and no multiple stock location
494 if (GETPOSTFLOAT($qty) > 0) {
495 $totalqty += price2num(GETPOST($qty, 'alpha'), 'MS');
496 $subtotalqty = price2num(GETPOST($qty, 'alpha'), 'MS');
497 }
498 }
499
500 // check qty shipped not greater than ordered
501 if (getDolGlobalInt("MAIN_DONT_SHIP_MORE_THAN_ORDERED") && $subtotalqty > $objectsrc->lines[$i]->qty) {
502 setEventMessages($langs->trans("ErrorTooMuchShipped", $i + 1), null, 'errors');
503 $error++;
504 continue;
505 }
506
507 // Extrafields
508 $array_options[$i] = $extrafields->getOptionalsFromPost($object->table_element_line, (string) $i);
509 // Unset extrafield
510 if (isset($extrafields->attributes[$object->table_element_line]['label']) && is_array($extrafields->attributes[$object->table_element_line]['label'])) {
511 // Get extra fields
512 foreach ($extrafields->attributes[$object->table_element_line]['label'] as $key => $value) {
513 unset($_POST["options_" . $key]);
514 }
515 }
516 }
517
518 if (($totalqty > 0 || getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) && !$error) { // There is at least one thing to ship and no error
519 $selected_subtotal_lines = GETPOST('subtotal_toselect', 'array:int');
520 for ($i = 0; $i < $num; $i++) {
521 $qty = "qtyl" . $i;
522
523 if (!isset($batch_line[$i])) {
524 // not batch mode
525 if (isset($stockLine[$i])) {
526 //shipment from multiple stock locations
527 $nbstockline = count($stockLine[$i]);
528 for ($j = 0; $j < $nbstockline; $j++) {
529 if ($stockLine[$i][$j]['qty'] > 0 || ($stockLine[$i][$j]['qty'] == 0 && getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS'))) {
530 $ret = $object->addline($stockLine[$i][$j]['warehouse_id'], $stockLine[$i][$j]['ix_l'], (float) $stockLine[$i][$j]['qty'], $array_options[$i]);
531 if ($ret < 0) {
532 setEventMessages($object->error, $object->errors, 'errors');
533 $error++;
534 }
535 }
536 }
537 } else {
538 if (GETPOSTFLOAT($qty) > 0 || getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
539 $ent = "entl" . $i;
540 $idl = "idl" . $i;
541 $entrepot_id = is_numeric(GETPOSTINT($ent)) ? GETPOSTINT($ent) : GETPOSTINT('entrepot_id');
542 if ($entrepot_id < 0) {
543 $entrepot_id = '';
544 }
545 if (!($objectsrc->lines[$i]->fk_product > 0)) {
546 $entrepot_id = 0;
547 }
548
549
550 $ret = $object->addline($entrepot_id, GETPOSTINT($idl), (float) price2num(GETPOSTFLOAT($qty), 'MS'), $array_options[$i]);
551 if ($ret < 0) {
552 setEventMessages($object->error, $object->errors, 'errors');
553 $error++;
554 }
555 }
556 if (isModEnabled('subtotals') && $objectsrc->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE && in_array($objectsrc->lines[$i]->id, $selected_subtotal_lines)) {
557 $object->addSubtotalLine($langs, $objectsrc->lines[$i]->desc, (int) $objectsrc->lines[$i]->qty, $objectsrc->lines[$i]->extraparams, $objectsrc->lines[$i]->id);
558 }
559 }
560 } else {
561 // batch mode
562 if ($batch_line[$i]['qty'] > 0 || ($batch_line[$i]['qty'] == 0 && getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS'))) {
563 $origin_line_id = (int) $batch_line[$i]['ix_l'];
564 $origin_line = new OrderLine($db);
565 $res = $origin_line->fetch($origin_line_id);
566 if ($res <= 0) {
567 $error++;
568 setEventMessages($origin_line->error, $origin_line->errors, 'errors');
569 }
570 $ret = $object->addline_batch($batch_line[$i], $array_options[$i], $origin_line);
571 if ($ret < 0) {
572 setEventMessages($object->error, $object->errors, 'errors');
573 $error++;
574 }
575 }
576 }
577 }
578 // Fill array 'array_options' with data from add form
579 $ret = $extrafields->setOptionalsFromPost(null, $object);
580 if ($ret < 0) {
581 $error++;
582 }
583
584 if (!$error) {
585 $ret = $object->create($user); // This create shipment (like Odoo picking) and lines of shipments. Stock movement will be done when validating or closing shipment.
586 if ($ret <= 0) {
587 setEventMessages($object->error, $object->errors, 'errors');
588 $error++;
589 }
590 }
591 } elseif (!$error) {
592 $labelfieldmissing = $langs->transnoentitiesnoconv("QtyToShip");
593 if (isModEnabled('stock')) {
594 $labelfieldmissing .= '/' . $langs->transnoentitiesnoconv("Warehouse");
595 }
596 setEventMessages($langs->trans("ErrorFieldRequired", $labelfieldmissing), null, 'errors');
597 $error++;
598 }
599 }
600 if (!$error) {
601 $db->commit();
602 $object->fetch_lines();
603 foreach ($object->lines as $line) {
604 $objectsrc_line = new $objectsrc->class_element_line($db);
605 '@phan-var-force CommonObjectLine $objectsrc_line';
606 $objectsrc_line->fetch($line->origin_line_id);
607 $line->extraparams = $objectsrc_line->extraparams;
608 $line->setExtraParameters();
609 }
610 header("Location: card.php?id=" . $object->id);
611 exit;
612 } else {
613 $db->rollback();
614 //$_GET["commande_id"] = GETPOSTINT('commande_id');
615 $action = 'create';
616 }
617 } elseif ($action == 'create_delivery' && getDolGlobalInt('MAIN_SUBMODULE_DELIVERY') && $user->hasRight('expedition', 'delivery', 'creer')) {
618 // Build a receiving receipt
619 $db->begin();
620
621 $result = $object->create_delivery($user);
622 if ($result > 0) {
623 $db->commit();
624
625 header("Location: " . DOL_URL_ROOT . '/delivery/card.php?action=create_delivery&token=' . newToken() . '&id=' . $result);
626 exit;
627 } else {
628 $db->rollback();
629
630 setEventMessages($object->error, $object->errors, 'errors');
631 }
632 } elseif (
633 $action == 'confirm_valid' && $confirm == 'yes' && ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'creer'))
634 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'shipping_advance', 'validate')))
635 ) {
636 $object->fetch_thirdparty();
637
638 $result = $object->valid($user);
639
640 if ($result < 0) {
641 setEventMessages($object->error, $object->errors, 'errors');
642 } else {
643 // Define output language
644 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
645 $outputlangs = $langs;
646 $newlang = '';
647 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
648 $newlang = GETPOST('lang_id', 'aZ09');
649 }
650 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
651 $newlang = $object->thirdparty->default_lang;
652 }
653 if (!empty($newlang)) {
654 $outputlangs = new Translate("", $conf);
655 $outputlangs->setDefaultLang($newlang);
656 }
657 $model = $object->model_pdf;
658 $ret = $object->fetch($id); // Reload to get new records
659
660 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
661 if ($result < 0) {
662 dol_print_error($db, $object->error, $object->errors);
663 }
664 }
665 }
666 } elseif ($action == 'confirm_cancel' && $confirm == 'yes' && $user->hasRight('expedition', 'creer')) {
667 $also_update_stock = (GETPOST('alsoUpdateStock', 'alpha') ? 1 : 0);
668 $result = $object->cancel($user, 0, (bool) $also_update_stock);
669 if ($result > 0) {
670 $result = $object->setStatut(Expedition::STATUS_CANCELED);
671 } else {
672 setEventMessages($object->error, $object->errors, 'errors');
673 }
674 } elseif ($action == 'confirm_delete' && $confirm == 'yes' && $user->hasRight('expedition', 'supprimer')) {
675 $also_update_stock = (GETPOST('alsoUpdateStock', 'alpha') ? 1 : 0);
676 $object->oldcopy = dol_clone($object, 2); // @phan-suppress-current-line PhanTypeMismatchProperty
677 $result = $object->delete($user, 0, (bool) $also_update_stock);
678 if ($result > 0) {
679 header("Location: " . DOL_URL_ROOT . '/expedition/index.php');
680 exit;
681 } else {
682 setEventMessages($object->error, $object->errors, 'errors');
683 }
684 // TODO add alternative status
685 //} elseif ($action == 'reopen' && ($user->hasRight('expedition', 'creer') || $user->hasRight('expedition', 'shipping_advance', 'validate')))
686 //{
687 // $result = $object->setStatut(Expedition::STATUS_DRAFT);
688 // if ($result < 0)
689 // {
690 // setEventMessages($object->error, $object->errors, 'errors');
691 // }
692 //}
693 } elseif ($action == 'confirm_delete_subtotalline' && $confirm == 'yes' && $permissiontoadd) {
694 $result = $object->deleteSubtotalLine($langs, GETPOSTINT('lineid'), (bool) GETPOST('deletecorrespondingsubtotalline'), $user);
695 if ($result > 0) {
696 // reorder lines
697 $object->line_order(true, 'ASC', false);
698 // Define output language
699 $outputlangs = $langs;
700 $newlang = '';
701 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
702 $newlang = GETPOST('lang_id', 'aZ09');
703 }
704 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
705 $newlang = $object->thirdparty->default_lang;
706 }
707 if (!empty($newlang)) {
708 $outputlangs = new Translate("", $conf);
709 $outputlangs->setDefaultLang($newlang);
710 }
711 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
712 $ret = $object->fetch($object->id); // Reload to get new records
713 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
714 }
715
716 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
717 exit;
718 } else {
719 setEventMessages($object->error, $object->errors, 'errors');
720 }
721 } elseif ($action == 'confirm_deleteline' && $confirm == 'yes' && $usercancreate) {
722 // Remove a product line
723 $result = $object->deleteLine($user, $lineid);
724 if ($result > 0) {
725 // reorder lines
726 $object->line_order(true);
727 // Define output language
728 $outputlangs = $langs;
729 $newlang = '';
730 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
731 $newlang = GETPOST('lang_id', 'aZ09');
732 }
733 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
734 $newlang = $object->thirdparty->default_lang;
735 }
736 if (!empty($newlang)) {
737 $outputlangs = new Translate("", $conf);
738 $outputlangs->setDefaultLang($newlang);
739 }
740 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
741 $ret = $object->fetch($object->id); // Reload to get new records
742 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
743 }
744
745 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
746 exit;
747 } else {
748 setEventMessages($object->error, $object->errors, 'errors');
749 }
750 } elseif ($action == 'confirm_sign' && $confirm == 'yes' && $user->hasRight('expedition', 'creer')) {
751 $result = $object->setSignedStatus($user, GETPOSTINT('signed_status'), 0, 'SHIPPING_MODIFY');
752 if ($result >= 0) {
753 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
754 exit;
755 } else {
756 setEventMessages($object->error, $object->errors, 'errors');
757 }
758 } elseif ($action == 'confirm_unsign' && $confirm == 'yes' && $user->hasRight('expedition', 'creer')) {
759 $result = $object->setSignedStatus($user, Expedition::$SIGNED_STATUSES['STATUS_NO_SIGNATURE'], 0, 'SHIPPING_MODIFY');
760 if ($result >= 0) {
761 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
762 exit;
763 } else {
764 setEventMessages($object->error, $object->errors, 'errors');
765 }
766 } elseif ($action == 'setdate_livraison' && $user->hasRight('expedition', 'creer')) {
767 $datedelivery = dol_mktime(GETPOSTINT('liv_hour'), GETPOSTINT('liv_min'), 0, GETPOSTINT('liv_month'), GETPOSTINT('liv_day'), GETPOSTINT('liv_year'));
768
769 $object->fetch($id);
770 $result = $object->setDeliveryDate($user, $datedelivery); // Set the planned delivery date
771 if ($result < 0) {
772 setEventMessages($object->error, $object->errors, 'errors');
773 }
774 } elseif ($action == 'setdate_shipping' && $user->hasRight('expedition', 'creer')) {
775 $dateshipping = dol_mktime(GETPOSTINT('ship_hour'), GETPOSTINT('ship_min'), 0, GETPOSTINT('ship_month'), GETPOSTINT('ship_day'), GETPOSTINT('ship_year'));
776
777 $object->fetch($id);
778 $result = $object->setShippingDate($user, $dateshipping);
779 if ($result < 0) {
780 setEventMessages($object->error, $object->errors, 'errors');
781 }
782 } elseif (in_array($action, array('settracking_number', 'settracking_url', 'settrueWeight', 'settrueWidth', 'settrueHeight', 'settrueDepth', 'setshipping_method_id')) && $user->hasRight('expedition', 'creer')) {
783 // Action update
784 $error = 0;
785
786 if ($action == 'settracking_number') { // Test on permission not required
787 $object->tracking_number = trim(GETPOST('tracking_number', 'alpha'));
788 $object->tracking_number = preg_replace('/\s+/', '', $object->tracking_number);
789 }
790 if ($action == 'settracking_url') { // Test on permission not required
791 $object->tracking_url = trim(GETPOST('tracking_url', 'restricthtml'));
792 }
793
794 if ($action == 'settrueWeight') { // Test on permission not required
795 $object->trueWeight = GETPOSTFLOAT('trueWeight');
796 $object->weight_units = GETPOSTINT('weight_units');
797 }
798 if ($action == 'settrueWidth') { // Test on permission not required
799 $object->trueWidth = GETPOSTFLOAT('trueWidth');
800 }
801 if ($action == 'settrueHeight') { // Test on permission not required
802 $object->trueHeight = GETPOSTFLOAT('trueHeight');
803 $object->size_units = GETPOSTINT('size_units');
804 }
805 if ($action == 'settrueDepth') { // Test on permission not required
806 $object->trueDepth = GETPOSTFLOAT('trueDepth');
807 }
808 if ($action == 'setshipping_method_id') { // Test on permission not required
809 $object->shipping_method_id = GETPOSTINT('shipping_method_id');
810 }
811
812 if ($object->update($user) >= 0) {
813 header("Location: card.php?id=" . $object->id);
814 exit;
815 }
816 setEventMessages($object->error, $object->errors, 'errors');
817 $action = "";
818 } elseif ($action == 'classifybilled' && $permissiontoadd) {
819 $object->fetch($id);
820 $result = $object->setBilled();
821 if ($result >= 0) {
822 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
823 exit();
824 }
825 setEventMessages($object->error, $object->errors, 'errors');
826 } elseif ($action == 'classifyclosed' && $permissiontoadd) {
827 $object->fetch($id);
828 $result = $object->setClosed();
829 if ($result >= 0) {
830 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
831 exit();
832 }
833 setEventMessages($object->error, $object->errors, 'errors');
834 } elseif ($action == 'deleteline' && !empty($line_id) && $permissiontoadd) {
835 // delete a line
836 $error = 0;
837 $object->fetch($id);
838 $lines = $object->lines;
839 $line = new ExpeditionLigne($db);
840 $line->fk_expedition = $object->id;
841
842 $num_prod = count($lines);
843 for ($i = 0; $i < $num_prod; $i++) {
844 if ($lines[$i]->id == $line_id) {
845 // A standalone shipment (SHIPMENT_STANDALONE) loads its lines with fetch_lines_free(),
846 // which does not set details_entrepot, so count() must not be called on it blindly.
847 if (is_array($lines[$i]->details_entrepot) && count($lines[$i]->details_entrepot) > 1) {
848 // delete multi warehouse lines
849 foreach ($lines[$i]->details_entrepot as $details_entrepot) {
850 $line->id = $details_entrepot->line_id;
851 if (!$error && $line->delete($user) < 0) {
852 $error++;
853 }
854 }
855 } else {
856 // delete single warehouse line
857 $line->id = $line_id;
858 if (!$error && $line->delete($user) < 0) {
859 $error++;
860 }
861 }
862 }
863 unset($_POST["lineid"]);
864 }
865
866 if (!$error) {
867 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
868 exit();
869 } else {
870 setEventMessages($line->error, $line->errors, 'errors');
871 }
872 } elseif ($action == 'updateline' && $permissiontoadd && GETPOST('cancel', 'alpha') != $langs->trans("Cancel")) {
873 if (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
874 // Update a line
875 // Clean parameters
876
877 if (!$object->fetch($id) > 0) {
878 dol_print_error($db);
879 }
880 $object->fetch_thirdparty();
881
882 $qty = GETPOST('qty', 'alpha');
883 $description = '';
884 $fk_parent = 0;
885 $element_type = 'shipping';
886 $fk_unit = '';
887 $fk_product = 0;
888 $rang = 0;
889
890 // Extrafields
891 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
892 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
893 // Unset extrafield
894 if (is_array($extralabelsline)) {
895 // Get extra fields
896 foreach ($extralabelsline as $key => $value) {
897 unset($_POST["options_" . $key]);
898 }
899 }
900
901 $shipline = new ExpeditionLigne($db);
902 $shipline->fetch(GETPOSTINT('lineid'));
903
904
905 if (!$error) {
906 $result = $object->updatelinefree(GETPOSTINT('lineid'), (float) $qty, $element_type, $fk_product, GETPOSTINT('units'), $rang, $description, $fk_parent, 0, $array_options);
907
908 if ($result >= 0) {
909 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
910 // Define output language
911 $outputlangs = $langs;
912 $newlang = '';
913 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
914 $newlang = GETPOST('lang_id', 'aZ09');
915 }
916 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
917 $newlang = $object->thirdparty->default_lang;
918 }
919 if (!empty($newlang)) {
920 $outputlangs = new Translate("", $conf);
921 $outputlangs->setDefaultLang($newlang);
922 $outputlangs->load('products');
923 }
924
925 $ret = $object->fetch($object->id); // Reload to get new records
926 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
927 }
928 unset($_POST['qty']);
929 unset($_POST['units']);
930 } else {
931 setEventMessages($object->error, $object->errors, 'errors');
932 }
933 }
934 } elseif ($origin && $origin_id > 0) {
935 // Update a line
936 // Clean parameters
937 $qty = 0;
938 $entrepot_id = 0;
939 $batch_id = 0;
940
941 $lines = $object->lines;
942 $num_prod = count($lines);
943 for ($i = 0; $i < $num_prod; $i++) {
944 if ($lines[$i]->id == $line_id) { // we have found line to update
945 $update_done = false;
946 $line = new ExpeditionLigne($db);
947 $line->fk_expedition = $object->id;
948
949 // Extrafields Lines
950 $line->array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
951 // Unset extrafield POST Data
952 if (is_array($extrafields->attributes[$object->table_element_line]['label'])) {
953 foreach ($extrafields->attributes[$object->table_element_line]['label'] as $key => $value) {
954 unset($_POST["options_" . $key]);
955 }
956 }
957 $line->fk_product = $lines[$i]->fk_product;
958 if (is_array($lines[$i]->detail_batch) && count($lines[$i]->detail_batch) > 0) {
959 // line with lot
960 foreach ($lines[$i]->detail_batch as $detail_batch) {
961 $lotStock = new Productbatch($db);
962 $batch = "batchl" . $detail_batch->fk_expeditiondet . "_" . $detail_batch->fk_origin_stock;
963 $qty = "qtyl" . $detail_batch->fk_expeditiondet . '_' . $detail_batch->id;
964 $batch_id = GETPOSTINT($batch);
965 $batch_qty = GETPOSTFLOAT($qty);
966 if (!empty($batch_id)) {
967 if ($lotStock->fetch($batch_id) > 0 && $line->fetch($detail_batch->fk_expeditiondet) > 0) { // $line is ExpeditionLine
968 if ($lines[$i]->entrepot_id != 0) {
969 // allow update line entrepot_id if not multi warehouse shipping
970 $line->entrepot_id = $lotStock->warehouseid;
971 }
972
973 // detail_batch can be an object with keys, or an array of ExpeditionLineBatch
974 if (empty($line->detail_batch)) {
975 $line->detail_batch = new stdClass();
976 }
977
978 $line->detail_batch->fk_origin_stock = $batch_id;
979 $line->detail_batch->batch = $lotStock->batch;
980 $line->detail_batch->id = $detail_batch->id;
981 $line->detail_batch->entrepot_id = $lotStock->warehouseid;
982 $line->detail_batch->qty = $batch_qty;
983 if ($line->update($user) < 0) {
984 setEventMessages($line->error, $line->errors, 'errors');
985 $error++;
986 } else {
987 $update_done = true;
988 }
989 } else {
990 setEventMessages($lotStock->error, $lotStock->errors, 'errors');
991 $error++;
992 }
993 }
994 unset($_POST[$batch]);
995 unset($_POST[$qty]);
996 }
997 // add new batch
998 $lotStock = new Productbatch($db);
999 $batch = "batchl" . $line_id . "_0";
1000 $qty = "qtyl" . $line_id . "_0";
1001 $batch_id = GETPOSTINT($batch);
1002 $batch_qty = GETPOSTFLOAT($qty);
1003 $lineIdToAddLot = 0;
1004 if ($batch_qty > 0 && !empty($batch_id)) {
1005 if ($lotStock->fetch($batch_id) > 0) {
1006 // check if lotStock warehouse id is same as line warehouse id
1007 if ($lines[$i]->entrepot_id > 0) {
1008 // single warehouse shipment line
1009 if ($lines[$i]->entrepot_id == $lotStock->warehouseid) {
1010 $lineIdToAddLot = $line_id;
1011 }
1012 } elseif (count($lines[$i]->details_entrepot) > 1) {
1013 // multi warehouse shipment lines
1014 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
1015 if ($detail_entrepot->entrepot_id == $lotStock->warehouseid) {
1016 $lineIdToAddLot = $detail_entrepot->line_id;
1017 }
1018 }
1019 }
1020 if ($lineIdToAddLot) {
1021 // add lot to existing line
1022 if ($line->fetch($lineIdToAddLot) > 0) {
1023 $line->detail_batch->fk_origin_stock = $batch_id;
1024 $line->detail_batch->batch = $lotStock->batch;
1025 $line->detail_batch->entrepot_id = $lotStock->warehouseid;
1026 $line->detail_batch->qty = $batch_qty;
1027 if ($line->update($user) < 0) {
1028 setEventMessages($line->error, $line->errors, 'errors');
1029 $error++;
1030 } else {
1031 $update_done = true;
1032 }
1033 } else {
1034 setEventMessages($line->error, $line->errors, 'errors');
1035 $error++;
1036 }
1037 } else {
1038 // create new line with new lot
1039 $line->origin_line_id = $lines[$i]->origin_line_id;
1040 $line->entrepot_id = $lotStock->warehouseid;
1041 $line->detail_batch[0] = new ExpeditionLineBatch($db);
1042 $line->detail_batch[0]->fk_origin_stock = $batch_id;
1043 $line->detail_batch[0]->batch = $lotStock->batch;
1044 $line->detail_batch[0]->entrepot_id = $lotStock->warehouseid;
1045 $line->detail_batch[0]->qty = $batch_qty;
1046 if ($object->create_line_batch($line, $line->array_options) < 0) {
1047 setEventMessages($object->error, $object->errors, 'errors');
1048 $error++;
1049 } else {
1050 $update_done = true;
1051 }
1052 }
1053 } else {
1054 setEventMessages($lotStock->error, $lotStock->errors, 'errors');
1055 $error++;
1056 }
1057 }
1058 } else {
1059 if ($lines[$i]->fk_product > 0) {
1060 // line without lot
1061 if ($lines[$i]->entrepot_id == 0) {
1062 // single warehouse shipment line or line in several warehouses context but with warehouse not defined
1063 $stockLocation = "entl" . $line_id;
1064 $qty = "qtyl" . $line_id;
1065 $line->id = $line_id;
1066 $line->entrepot_id = GETPOSTINT((string) $stockLocation);
1067 $line->qty = GETPOSTFLOAT($qty);
1068 if ($line->update($user) < 0) {
1069 setEventMessages($line->error, $line->errors, 'errors');
1070 $error++;
1071 } else {
1072 $update_done = true;
1073 }
1074 unset($_POST[$stockLocation]);
1075 unset($_POST[$qty]);
1076 } elseif ($lines[$i]->entrepot_id > 0) {
1077 // single warehouse shipment line
1078 $stockLocation = "entl" . $line_id;
1079 $qty = "qtyl" . $line_id;
1080 $line->id = $line_id;
1081 $line->entrepot_id = GETPOSTINT($stockLocation);
1082 $line->qty = GETPOSTFLOAT($qty);
1083 if ($line->update($user) < 0) {
1084 setEventMessages($line->error, $line->errors, 'errors');
1085 $error++;
1086 } else {
1087 $update_done = true;
1088 }
1089 unset($_POST[$stockLocation]);
1090 unset($_POST[$qty]);
1091 } elseif (count($lines[$i]->details_entrepot) > 1) {
1092 // multi warehouse shipment lines
1093 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
1094 if (!$error) {
1095 $stockLocation = "entl" . $detail_entrepot->line_id;
1096 $qty = "qtyl" . $detail_entrepot->line_id;
1097 $warehouse = GETPOSTINT($stockLocation);
1098 if (!empty($warehouse)) {
1099 $line->id = $detail_entrepot->line_id;
1100 $line->entrepot_id = $warehouse;
1101 $line->qty = GETPOSTFLOAT($qty);
1102 if ($line->update($user) < 0) {
1103 setEventMessages($line->error, $line->errors, 'errors');
1104 $error++;
1105 } else {
1106 $update_done = true;
1107 }
1108 }
1109 unset($_POST[$stockLocation]);
1110 unset($_POST[$qty]);
1111 }
1112 }
1113 } elseif (!isModEnabled('stock') && !isModEnabled('productbatch')) { // both product batch and stock are not activated.
1114 $qty = "qtyl" . $line_id;
1115 $line->id = $line_id;
1116 $line->qty = GETPOSTFLOAT($qty);
1117 $line->entrepot_id = 0;
1118 if ($line->update($user) < 0) {
1119 setEventMessages($line->error, $line->errors, 'errors');
1120 $error++;
1121 } else {
1122 $update_done = true;
1123 }
1124 unset($_POST[$qty]);
1125 }
1126 } else {
1127 // Product no predefined
1128 $qty = "qtyl" . $line_id;
1129 $line->id = $line_id;
1130 $line->qty = GETPOSTFLOAT($qty);
1131 $line->entrepot_id = 0;
1132 if ($line->update($user) < 0) {
1133 setEventMessages($line->error, $line->errors, 'errors');
1134 $error++;
1135 } else {
1136 $update_done = true;
1137 }
1138 unset($_POST[$qty]);
1139 }
1140 }
1141
1142 if (empty($update_done)) {
1143 $line->id = $lines[$i]->id;
1144 $resultInsertExtraFields = $line->insertExtraFields();
1145 if ($resultInsertExtraFields < 0) {
1146 setEventMessages($line->error, $line->errors, 'errors');
1147 $error++;
1148 } elseif ($resultInsertExtraFields > 0) {
1149 $update_done = true;
1150 }
1151 }
1152
1153 if (empty($update_done) && !$error) {
1154 setEventMessages($langs->trans('NoShipmentLineChangeDetected'), null, 'warnings');
1155 }
1156 }
1157 }
1158
1159 unset($_POST["lineid"]);
1160
1161 if (!$error) {
1162 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1163 // Define output language
1164 $outputlangs = $langs;
1165 $newlang = '';
1166 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
1167 $newlang = GETPOST('lang_id', 'aZ09');
1168 }
1169 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1170 $newlang = $object->thirdparty->default_lang;
1171 }
1172 if (!empty($newlang)) {
1173 $outputlangs = new Translate("", $conf);
1174 $outputlangs->setDefaultLang($newlang);
1175 }
1176
1177 $ret = $object->fetch($object->id); // Reload to get new records
1178 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1179 }
1180 } else {
1181 header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $object->id); // To redisplay the form being edited
1182 exit();
1183 }
1184 }
1185 } elseif ($action == 'updateline' && $permissiontoadd && GETPOST('cancel', 'alpha') == $langs->trans("Cancel")) {
1186 header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $object->id); // To redisplay the form being edited
1187 exit();
1188 } elseif ($action == 'addline' && !$origin && getDolGlobalString('SHIPMENT_STANDALONE') && $usercancreate) { // Add a new line
1189 $langs->load('errors');
1190 $error = 0;
1191 $line_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : '');
1192 $predef = '';
1193 $description = '';
1194 $fk_parent = 0;
1195 $fk_elementdet = '';
1196 $element_type = 'shipping';
1197 $fk_unit = '';
1198 $idprod = 0;
1199 $fk_product = 0;
1200 $fk_entrepot = '';
1201 $rang = '';
1202 $prod_entry_mode = GETPOST('prod_entry_mode', 'aZ09');
1203 if ($prod_entry_mode == 'free') {
1204 $idprod = 0;
1205 } else {
1206 $idprod = GETPOSTINT('idprod');
1207 if (getDolGlobalString('MAIN_DISABLE_FREE_LINES') && $idprod <= 0) {
1208 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ProductOrService")), null, 'errors');
1209 $error++;
1210 }
1211 }
1212
1213 $qty = price2num(GETPOST('qty'.$predef, 'alpha'), 'MS', 2);
1214
1215 // Extrafields
1216 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
1217 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line, $predef);
1218 // Unset extrafield
1219 if (is_array($extralabelsline)) {
1220 // Get extra fields
1221 foreach ($extralabelsline as $key => $value) {
1222 unset($_POST["options_".$key]);
1223 }
1224 }
1225
1226 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && GETPOST('type') < 0) {
1227 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors');
1228 $error++;
1229 }
1230
1231 if ($qty == '') {
1232 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
1233 $error++;
1234 }
1235 if ($qty < 0) {
1236 setEventMessages($langs->trans('FieldCannotBeNegative', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
1237 $error++;
1238 }
1239 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && empty($line_desc)) {
1240 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors');
1241 $error++;
1242 }
1243
1244 if (!$error && isModEnabled('variants') && $prod_entry_mode != 'free') {
1245 if ($combinations = GETPOST('combinations', 'array')) {
1246 // Check if there is a product with the given combination
1247 $prodcomb = new ProductCombination($db);
1248
1249 if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
1250 $idprod = $res->fk_product_child;
1251 $fk_product = $idprod; // Update $fk_product with the fetched child product
1252 } else {
1253 setEventMessages($langs->trans('ErrorProductCombinationNotFound'), null, 'errors');
1254 $error++;
1255 }
1256 }
1257 }
1258
1259 if (!$error && ($qty >= 0) && (!empty($line_desc) || (!empty($idprod) && $idprod > 0))) {
1260 // Clean parameters
1261 if (!empty($idprod) && $idprod > 0) {
1262 $prod = new Product($db);
1263 $prod->fetch($idprod);
1264 $desc = $prod->label;
1265 $description = $desc;
1266 // Define output language
1267 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
1268 $outputlangs = $langs;
1269 $newlang = '';
1270 if (GETPOST('lang_id', 'aZ09')) {
1271 $newlang = GETPOST('lang_id', 'aZ09');
1272 }
1273 if (empty($newlang)) {
1274 $newlang = $object->thirdparty->default_lang;
1275 }
1276 if (!empty($newlang)) {
1277 $outputlangs = new Translate("", $conf);
1278 $outputlangs->setDefaultLang($newlang);
1279 }
1280
1281 $description = (!empty($prod->multilangs[$outputlangs->defaultlang]["description"])) ? $prod->multilangs[$outputlangs->defaultlang]["description"] : $prod->description;
1282 } else {
1283 $description = $prod->description;
1284 }
1285 if (getDolGlobalInt('PRODUIT_AUTOFILL_DESC') == 0) {
1286 $description = dol_concatdesc($desc, $line_desc, false, getDolGlobalString('MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION') ? true : false);
1287 } else {
1288 $description = $line_desc;
1289 }
1290
1291 // Add custom code and origin country into description
1292 if (!getDolGlobalString('MAIN_PRODUCT_DISABLE_CUSTOMCOUNTRYCODE') && (!empty($prod->customcode) || !empty($prod->country_code))) {
1293 $tmptxt = '(';
1294 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
1295 $outputlangs = $langs;
1296 $newlang = '';
1297 if (GETPOST('lang_id', 'alpha')) {
1298 $newlang = GETPOST('lang_id', 'alpha');
1299 }
1300 if (empty($newlang)) {
1301 $newlang = $object->thirdparty->default_lang;
1302 }
1303 if (!empty($newlang)) {
1304 $outputlangs = new Translate("", $conf);
1305 $outputlangs->setDefaultLang($newlang);
1306 $outputlangs->load('products');
1307 }
1308 if (!empty($prod->customcode)) {
1309 $tmptxt .= $outputlangs->transnoentitiesnoconv("CustomsCode").': '.$prod->customcode;
1310 }
1311 if (!empty($prod->customcode) && !empty($prod->country_code)) {
1312 $tmptxt .= ' - ';
1313 }
1314 if (!empty($prod->country_code)) {
1315 $tmptxt .= $outputlangs->transnoentitiesnoconv("CountryOrigin").': '.getCountry($prod->country_code, '', $db, $outputlangs, 0);
1316 }
1317 } else {
1318 if (!empty($prod->customcode)) {
1319 $tmptxt .= $langs->transnoentitiesnoconv("CustomsCode").': '.$prod->customcode;
1320 }
1321 if (!empty($prod->customcode) && !empty($prod->country_code)) {
1322 $tmptxt .= ' - ';
1323 }
1324 if (!empty($prod->country_code)) {
1325 $tmptxt .= $langs->transnoentitiesnoconv("CountryOrigin").': '.getCountry($prod->country_code, '', $db, $langs, 0);
1326 }
1327 }
1328 $tmptxt .= ')';
1329 $description = dol_concatdesc($desc, $tmptxt);
1330 }
1331 $type = $prod->type;
1332 $fk_unit = $prod->fk_unit;
1333 } else {
1334 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
1335 $desc = $line_desc;
1336 $type = GETPOST('type');
1337 $fk_unit = GETPOST('units', 'alpha');
1338 $description = $desc;
1339 $fk_elementdet = '';
1340 }
1341 $desc = dol_htmlcleanlastbr($desc);
1342
1343 // Insert line
1344 $result = $object->addlinefree((float) $qty, $element_type, $idprod, $fk_unit, min($rank, count($object->lines) + 1), $description, $fk_parent, $array_options);
1345
1346 if ($result > 0) {
1347 $ret = $object->fetch($object->id); // Reload to get new records
1348 $object->fetch_thirdparty();
1349 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1350 // Define output language
1351 $outputlangs = $langs;
1352 $newlang = GETPOST('lang_id', 'alpha');
1353 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1354 $newlang = $object->thirdparty->default_lang;
1355 }
1356 if (!empty($newlang)) {
1357 $outputlangs = new Translate("", $conf);
1358 $outputlangs->setDefaultLang($newlang);
1359 }
1360 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1361 }
1362 } else {
1363 header('Location: '.$_SERVER['PHP_SELF'].'?id='.$object->id); // To redisplay the form being edited
1364 exit();
1365 }
1366 }
1367 } elseif ($action == 'confirm_sign' && $confirm == 'yes' && $permissiontoadd) {
1368 $result = $object->setSignedStatus($user, GETPOSTINT('signed_status'), 0, 'SHIPPING_MODIFY');
1369 if ($result >= 0) {
1370 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1371 // Define output language
1372 $outputlangs = $langs;
1373 $newlang = '';
1374 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
1375 $newlang = GETPOST('lang_id', 'aZ09');
1376 }
1377 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1378 $newlang = $object->thirdparty->default_lang;
1379 }
1380 if (!empty($newlang)) {
1381 $outputlangs = new Translate("", $conf);
1382 $outputlangs->setDefaultLang($newlang);
1383 }
1384 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1385 }
1386
1387 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
1388 exit;
1389 } else {
1390 $mesg = $object->error;
1391 }
1392 } elseif ($action == 'confirm_unsign' && $confirm == 'yes' && $permissiontoadd) {
1393 $result = $object->setSignedStatus($user, Expedition::$SIGNED_STATUSES['STATUS_NO_SIGNATURE'], 0, 'SHIPPING_MODIFY');
1394 if ($result >= 0) {
1395 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1396 // Define output language
1397 $outputlangs = $langs;
1398 $newlang = '';
1399 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
1400 $newlang = GETPOST('lang_id', 'aZ09');
1401 }
1402 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1403 $newlang = $object->thirdparty->default_lang;
1404 }
1405 if (!empty($newlang)) {
1406 $outputlangs = new Translate("", $conf);
1407 $outputlangs->setDefaultLang($newlang);
1408 }
1409 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1410 }
1411
1412 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
1413 exit;
1414 } else {
1415 $mesg = $object->error;
1416 }
1417 }
1418
1419 include DOL_DOCUMENT_ROOT . '/core/actions_printing.inc.php';
1420
1421 // Actions to send emails
1422 if (empty($id)) {
1423 $id = $facid;
1424 }
1425 $triggersendname = 'SHIPPING_SENTBYMAIL';
1426 $paramname = 'id';
1427 $autocopy = 'MAIN_MAIL_AUTOCOPY_SHIPMENT_TO';
1428 $mode = 'emailfromshipment';
1429 $trackid = 'shi' . $object->id;
1430 include DOL_DOCUMENT_ROOT . '/core/actions_sendmails.inc.php';
1431}
1432
1433/*
1434 * View
1435 */
1436
1437$title = $object->ref . ' - ' . $langs->trans("Card");
1438if ($action == 'create') {
1439 $title = $langs->trans("NewSending");
1440}
1441
1442$help_url = 'EN:Module_Shipments|FR:Module_Expéditions|ES:M&oacute;dulo_Expediciones|DE:Modul_Lieferungen';
1443
1444llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-expedition page-card');
1445
1446if (empty($action)) {
1447 $action = 'view';
1448}
1449
1450$form = new Form($db);
1451$formfile = new FormFile($db);
1452$formproduct = new FormProduct($db);
1453if (isModEnabled('project')) {
1454 $formproject = new FormProjets($db);
1455} else {
1456 $formproject = null;
1457}
1458
1459$product_static = new Product($db);
1460$shipment_static = new Expedition($db);
1461$warehousestatic = new Entrepot($db);
1462
1463if (!$origin && $action == 'create' && !getDolGlobalString('SHIPMENT_STANDALONE')) {
1464 print load_fiche_titre($langs->trans("CreateShipment"), '', 'dolly');
1465
1466 print '<br>' .$langs->trans("ShipmentCreationIsDoneFromOrder");
1467 $action = '';
1468 $id = '';
1469 $ref = '';
1470}
1471
1472// Mode creation.
1473if ($action == 'create' && $usercancreate) {
1474 $expe = new Expedition($db);
1475
1476 print load_fiche_titre($langs->trans("NewSending"), '', 'dolly');
1477
1478 if (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
1479 $soc = new Societe($db);
1480 if ($socid > 0) {
1481 $res = $soc->fetch($socid);
1482 }
1483
1484 $shipping_method_id = $soc->shipping_method_id;
1485 $note_private = $object->getDefaultCreateValueFor('note_private');
1486 $note_public = $object->getDefaultCreateValueFor('note_public');
1487
1488 print '<form name="crea_expedition" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
1489 print '<input type="hidden" name="token" value="' . newToken() . '">';
1490 print '<input type="hidden" name="action" value="add">';
1491 print '<input type="hidden" name="changecompany" value="0">'; // will be set to 1 by javascript so we know post is done after a company change
1492 print '<input type="hidden" name="originid" value="' . $id . '">';
1493 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
1494
1495 print dol_get_fiche_head([]);
1496
1497 print '<table class="border centpercent">';
1498
1499 // Thirdparty
1500 print '<tr>';
1501 print '<td class="fieldrequired">' . $langs->trans('ThirdParty') . '</td>';
1502 if ($socid > 0) {
1503 print '<td>';
1504 print $soc->getNomUrl(1);
1505 print '<input type="hidden" name="socid" value="' . $soc->id . '">';
1506 print '</td>';
1507 } else {
1508 print '<td class="valuefieldcreate">';
1509 print img_picto('', 'company', 'class="pictofixedwidth"').$form->select_company('', 'socid', '', 'SelectThirdParty', 1, 0, array(), 0, 'minwidth175 maxwidth500 widthcentpercentminusxx');
1510 // reload page to retrieve customer information
1511 if (!getDolGlobalString('RELOAD_PAGE_ON_CUSTOMER_CHANGE_DISABLED')) {
1512 print '<script>
1513 $(document).ready(function() {
1514 $("#socid").change(function() {
1515 console.log("We have changed the company - Reload page");
1516 var socid = $(this).val();
1517 // reload page
1518 $("input[name=action]").val("create");
1519 $("input[name=changecompany]").val("1");
1520 $("form[name=crea_expedition]").submit();
1521 });
1522 });
1523 </script>';
1524 }
1525 print ' <a href="'.DOL_URL_ROOT.'/societe/card.php?action=create&client=3&fournisseur=0&backtopage='.urlencode($_SERVER["PHP_SELF"].'?action=create').'"><span class="fa fa-plus-circle valignmiddle paddingleft" title="'.$langs->trans("AddThirdParty").'"></span></a>';
1526 print '</td>';
1527 }
1528 print '</tr>'."\n";
1529
1530 // Project
1531 if (isModEnabled('project') && is_object($formproject)) {
1532 $projectid = GETPOSTINT('projectid');
1533 if (empty($projectid) && !empty($object->fk_project)) {
1534 $projectid = (int) $object->fk_project;
1535 }
1536
1537 $langs->load("projects");
1538 print '<tr>';
1539 print '<td>' . $langs->trans("Project") . '</td><td colspan="2">';
1540 print img_picto('', 'project', 'class="pictofixedwidth"');
1541 print $formproject->select_projects($soc->id, $projectid, 'projectid', 0, 0, 1, 0, 0, 0, 0, '', 1, 0, 'widthcentpercentminusxx');
1542 print ' <a class="paddingleft" href="' . DOL_URL_ROOT . '/projet/card.php?socid=' . $soc->id . '&action=create&status=1&backtopage=' . urlencode($_SERVER["PHP_SELF"] . '?action=create&socid=' . $soc->id) . '"><span class="fa fa-plus-circle valignmiddle"></span></a>';
1543 print '</td>';
1544 print '</tr>';
1545 }
1546
1547 // Date sending
1548 print '<tr><td>'.$langs->trans("DateShipping").'</td>';
1549 print '<td colspan="3">';
1550 print img_picto('', 'action', 'class="pictofixedwidth"');
1551 $date_shipping = ($date_shipping ? $date_shipping : $object->date_shipping); // $date_shipping comes from GETPOST
1552 print $form->selectDate($date_shipping ? $date_shipping : -1, 'date_shipping', 1, 1, 1);
1553 print "</td>\n";
1554 print '</tr>';
1555
1556 // Date delivery planned
1557 print '<tr><td>'.$langs->trans("DateDeliveryPlanned").'</td>';
1558 print '<td colspan="3">';
1559 print img_picto('', 'action', 'class="pictofixedwidth"');
1560 $date_delivery = ($date_delivery ? $date_delivery : $object->delivery_date); // $date_delivery comes from GETPOST
1561 print $form->selectDate($date_delivery ? $date_delivery : -1, 'date_delivery', 1, 1, 1);
1562 print "</td>\n";
1563 print '</tr>';
1564
1565 // Weight
1566 print '<tr><td>';
1567 print $langs->trans("Weight");
1568 print '</td><td colspan="3">';
1569 print img_picto('', 'fa-balance-scale', 'class="pictofixedwidth"');
1570 print '<input name="weight" size="4" value="' . GETPOST('weight') . '"> '; // Do not use GETPOSTINT here, we must accept '' also.
1571 $text = $formproduct->selectMeasuringUnits("weight_units", "weight", GETPOST('weight_units'), 0, 2);
1572 $htmltext = $langs->trans("KeepEmptyForAutoCalculation");
1573 print $form->textwithpicto($text, $htmltext);
1574 print '</td></tr>';
1575
1576 // Dim width x height x depth
1577 print '<tr><td>';
1578 print $langs->trans("Width") . ' x ' . $langs->trans("Height") . ' x ' . $langs->trans("Depth");
1579 print ' </td><td colspan="3">';
1580 print img_picto('', 'fa-ruler', 'class="pictofixedwidth"');
1581 print '<input name="sizeW" size="4" value="' . GETPOST('sizeW') . '">';
1582 print ' x <input name="sizeH" size="4" value="' . GETPOST('sizeH') . '">';
1583 print ' x <input name="sizeS" size="4" value="' . GETPOST('sizeS') . '">';
1584 print ' ';
1585 $text = $formproduct->selectMeasuringUnits("size_units", "size", GETPOST('size_units'), 0, 2);
1586 $htmltext = $langs->trans("KeepEmptyForAutoCalculation");
1587 print $form->textwithpicto($text, $htmltext);
1588 print '</td></tr>';
1589
1590 // Delivery method
1591 print "<tr><td>".$langs->trans("DeliveryMethod")."</td>";
1592 print '<td colspan="3">';
1593 $expe->fetch_delivery_methods();
1594 print img_picto('', 'dolly', 'class="pictofixedwidth"');
1595 print $form->selectarray("shipping_method_id", $expe->meths, GETPOSTINT('shipping_method_id'), 1, 0, 0, "", 1, 0, 0, '', 'widthcentpercentminusxx');
1596 if ($user->admin) {
1597 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1598 }
1599 print "</td></tr>\n";
1600
1601 // Tracking number
1602 print "<tr><td>".$langs->trans("TrackingNumber")."</td>";
1603 print '<td colspan="3">';
1604 print img_picto('', 'barcode', 'class="pictofixedwidth"');
1605 print '<input name="tracking_number" size="20" value="'.GETPOST('tracking_number', 'alpha').'">';
1606 print "</td></tr>\n";
1607
1608 // Other attributes
1609 $parameters = array('objectsrc' => isset($objectsrc) ? $objectsrc : '', 'colspan' => ' colspan="3"', 'cols' => '3', 'socid' => $socid);
1610 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $expe, $action); // Note that $action and $object may have been modified by hook
1611 print $hookmanager->resPrint;
1612
1613 if (empty($reshook)) {
1614 // copy from order
1615 if ($object->fetch_optionals() > 0) {
1616 $expe->array_options = array_merge($expe->array_options, $object->array_options);
1617 }
1618 print $expe->showOptionals($extrafields, 'edit', $parameters);
1619 }
1620
1621 // Incoterms
1622 if (isModEnabled('incoterm')) {
1623 print '<tr>';
1624 print '<td><label for="incoterm_id">'.$form->textwithpicto($langs->trans("IncotermLabel"), $object->label_incoterms, 1).'</label></td>';
1625 print '<td colspan="3" class="maxwidthonsmartphone">';
1626 print img_picto('', 'incoterm', 'class="pictofixedwidth"');
1627 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''));
1628 print '</td></tr>';
1629 }
1630
1631 // Document model
1632 include_once DOL_DOCUMENT_ROOT.'/core/modules/expedition/modules_expedition.php';
1634 if (is_array($list) && count($list) > 1) {
1635 print "<tr><td>".$langs->trans("DefaultModel")."</td>";
1636 print '<td colspan="3">';
1637 print img_picto('', 'pdf', 'class="pictofixedwidth"');
1638 print $form->selectarray('model', $list, getDolGlobalString('EXPEDITION_ADDON_PDF'), 0, 0, 0, '', 0, 0, 0, '', 'widthcentpercentminusx');
1639 print "</td></tr>\n";
1640 }
1641
1642 // Note Public
1643 $htmltext = '';
1644 print '<tr>';
1645 print '<td class="tdtop">';
1646 print $form->textwithpicto($langs->trans('NotePublic'), $htmltext);
1647 print '</td>';
1648 print '<td valign="top" colspan="2">';
1649 $doleditor = new DolEditor('note_public', (string) $note_public, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
1650 print $doleditor->Create(1);
1651
1652 // Note Private
1653 if (!$user->socid) {
1654 print '<tr>';
1655 print '<td class="tdtop">';
1656 print $form->textwithpicto($langs->trans('NotePrivate'), $htmltext);
1657 print '</td>';
1658 print '<td valign="top" colspan="2">';
1659 $doleditor = new DolEditor('note_private', (string) $note_private, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
1660 print $doleditor->Create(1);
1661 print "</td></tr>";
1662 }
1663
1664 print "</table>\n";
1665
1666 print dol_get_fiche_end();
1667
1668 print $form->buttonsSaveCancel("CreateDraft");
1669
1670 print "</form>\n";
1671 } elseif ($origin) {
1672 $classname = ucfirst($origin);
1673
1674 $object = new $classname($db);
1675 /* @var Commande|Facture $object */
1676 '@phan-var-force Commande|Facture $object';
1677
1678
1679 if ($object->fetch($origin_id)) { // This include the fetch_lines
1680 $soc = new Societe($db);
1681 $soc->fetch($object->socid);
1682
1683 $author = new User($db);
1684 $author->fetch($object->user_author_id);
1685
1686 if (isModEnabled('stock')) {
1687 $entrepot = new Entrepot($db);
1688 }
1689
1690 print '<form action="' . $_SERVER["PHP_SELF"] . '" method="post">';
1691 print '<input type="hidden" name="token" value="' . newToken() . '">';
1692 print '<input type="hidden" name="action" value="add">';
1693 print '<input type="hidden" name="origin" value="' . $origin . '">';
1694 print '<input type="hidden" name="origin_id" value="' . $object->id . '">';
1695 if (GETPOSTINT('entrepot_id')) {
1696 print '<input type="hidden" name="entrepot_id" value="' . GETPOSTINT('entrepot_id') . '">';
1697 }
1698
1699 print dol_get_fiche_head([]);
1700
1701 print '<table class="border centpercent">';
1702
1703 // Ref
1704 print '<tr><td class="titlefieldcreate fieldrequired">';
1705 if ($origin == 'commande' && isModEnabled('order')) {
1706 print $langs->trans("RefOrder");
1707 }
1708 if ($origin == 'propal' && isModEnabled("propal")) {
1709 print $langs->trans("RefProposal");
1710 }
1711 print '</td><td colspan="3">';
1712 print $object->getNomUrl(1);
1713 print '</td>';
1714 print "</tr>\n";
1715
1716 // Ref client
1717 print '<tr><td>';
1718 if ($origin == 'commande') {
1719 print $langs->trans('RefCustomerOrder');
1720 } elseif ($origin == 'propal') {
1721 print $langs->trans('RefCustomerOrder');
1722 } else {
1723 print $langs->trans('RefCustomer');
1724 }
1725 print '</td><td colspan="3">';
1726 print '<input type="text" name="ref_customer" value="' . $object->ref_client . '" />';
1727 print '</td>';
1728 print '</tr>';
1729
1730 // Thirdparty
1731 print '<tr><td class="titlefieldcreate fieldrequired">' . $langs->trans('Company') . '</td>';
1732 print '<td colspan="3">' . $soc->getNomUrl(1) . '</td>';
1733 print '<input type="hidden" name="socid" value="' . $soc->id . '">';
1734 print '</tr>';
1735
1736 // Project
1737 if (isModEnabled('project') && is_object($formproject)) {
1738 $projectid = GETPOSTINT('projectid');
1739 if (empty($projectid) && !empty($object->fk_project)) {
1740 $projectid = (int) $object->fk_project;
1741 }
1742 if ($origin == 'project') {
1743 $projectid = ($object->id ? $object->id : 0);
1744 }
1745 $langs->load("projects");
1746 print '<tr>';
1747 print '<td>' . $langs->trans("Project") . '</td><td colspan="2">';
1748 print img_picto('', 'project', 'class="pictofixedwidth"');
1749 print $formproject->select_projects($soc->id, $projectid, 'projectid', 0, 0, 1, 0, 0, 0, 0, '', 1, 0, 'widthcentpercentminusxx');
1750 print ' <a class="paddingleft" href="' . DOL_URL_ROOT . '/projet/card.php?socid=' . $soc->id . '&action=create&status=1&backtopage=' . urlencode($_SERVER["PHP_SELF"] . '?action=create&socid=' . $soc->id) . '"><span class="fa fa-plus-circle valignmiddle"></span></a>';
1751 print '</td>';
1752 print '</tr>';
1753 }
1754
1755 // Date delivery planned
1756 print '<tr><td>' . $langs->trans("DateDeliveryPlanned") . '</td>';
1757 print '<td colspan="3">';
1758 print img_picto('', 'action', 'class="pictofixedwidth"');
1759 $date_delivery = ($date_delivery ? $date_delivery : $object->delivery_date); // $date_delivery comes from GETPOST
1760 print $form->selectDate($date_delivery ? $date_delivery : -1, 'date_delivery', 1, 1, 1);
1761 print "</td>\n";
1762 print '</tr>';
1763
1764 // Date shipment (sending)
1765 print '<tr><td>' . $langs->trans("DateShipping") . '</td>';
1766 print '<td colspan="3">';
1767 print img_picto('', 'action', 'class="pictofixedwidth"');
1768 //$date_shipping = ($date_shipping ? $date_shipping : $object->date_shipping); // $date_shipping comes from GETPOST
1769 print $form->selectDate($date_shipping ? $date_shipping : -1, 'date_shipping', 1, 1, 1);
1770 print "</td>\n";
1771 print '</tr>';
1772
1773 // Note Public
1774 print '<tr><td>' . $langs->trans("NotePublic") . '</td>';
1775 print '<td colspan="3">';
1776 $doleditor = new DolEditor('note_public', $object->note_public, '', 60, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
1777 print $doleditor->Create(1);
1778 print "</td></tr>";
1779
1780 // Note Private
1781 if ($object->note_private && !$user->socid) {
1782 print '<tr><td>' . $langs->trans("NotePrivate") . '</td>';
1783 print '<td colspan="3">';
1784 $doleditor = new DolEditor('note_private', $object->note_private, '', 60, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
1785 print $doleditor->Create(1);
1786 print "</td></tr>";
1787 }
1788
1789 // Weight
1790 print '<tr><td>';
1791 print $langs->trans("Weight");
1792 print '</td><td colspan="3">';
1793 print img_picto('', 'fa-balance-scale', 'class="pictofixedwidth"');
1794
1795 print '<input name="weight" size="4" value="' . dol_escape_htmltag(GETPOST('weight')) . '"> '; // Do not use GETPOSTINT here, we must accept '' also.
1796 $text = $formproduct->selectMeasuringUnits("weight_units", "weight", (string) GETPOST('weight_units'), 0, 2);
1797 $htmltext = $langs->trans("KeepEmptyForAutoCalculation");
1798 print $form->textwithpicto($text, $htmltext);
1799 print '</td></tr>';
1800
1801 // Dim Width x Height x Depth
1802 print '<tr><td>';
1803 print $langs->trans("Width") . ' x ' . $langs->trans("Height") . ' x ' . $langs->trans("Depth");
1804 print ' </td><td colspan="3">';
1805 print img_picto('', 'fa-ruler', 'class="pictofixedwidth"');
1806 print '<input name="sizeW" size="4" value="' . GETPOST('sizeW') . '">';
1807 print ' x <input name="sizeH" size="4" value="' . GETPOST('sizeH') . '">';
1808 print ' x <input name="sizeS" size="4" value="' . GETPOST('sizeS') . '">';
1809 print ' ';
1810 $text = $formproduct->selectMeasuringUnits("size_units", "size", GETPOST('size_units'), 0, 2);
1811 $htmltext = $langs->trans("KeepEmptyForAutoCalculation");
1812 print $form->textwithpicto($text, $htmltext);
1813 print '</td></tr>';
1814
1815 // Delivery method
1816 print "<tr><td>" . $langs->trans("DeliveryMethod") . "</td>";
1817 print '<td colspan="3">';
1818 $expe->fetch_delivery_methods();
1819 print img_picto('', 'dolly', 'class="pictofixedwidth"');
1820 print $form->selectarray("shipping_method_id", $expe->meths, GETPOSTINT('shipping_method_id'), 1, 0, 0, "", 1, 0, 0, '', 'widthcentpercentminusxx');
1821 if ($user->admin) {
1822 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1823 }
1824 print "</td></tr>\n";
1825
1826 // Tracking number
1827 print "<tr><td>" . $langs->trans("TrackingNumber") . "</td>";
1828 print '<td colspan="3">';
1829 print img_picto('', 'barcode', 'class="pictofixedwidth"');
1830 print '<input name="tracking_number" size="20" value="' . GETPOST('tracking_number', 'alpha') . '">';
1831 print "</td></tr>\n";
1832
1833 // Incoterms
1834 if (isModEnabled('incoterm')) {
1835 print '<tr>';
1836 print '<td><label for="incoterm_id">' . $form->textwithpicto($langs->trans("IncotermLabel"), $object->label_incoterms, 1) . '</label></td>';
1837 print '<td colspan="3" class="maxwidthonsmartphone">';
1838 print img_picto('', 'incoterm', 'class="pictofixedwidth"');
1839 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''));
1840 print '</td></tr>';
1841 }
1842
1843 // Document model
1844 include_once DOL_DOCUMENT_ROOT . '/core/modules/expedition/modules_expedition.php';
1846 if (is_array($list) && count($list) > 1) {
1847 print "<tr><td>" . $langs->trans("DefaultModel") . "</td>";
1848 print '<td colspan="3">';
1849 print img_picto('', 'pdf', 'class="pictofixedwidth"');
1850 print $form->selectarray('model', $list, getDolGlobalString('EXPEDITION_ADDON_PDF'), 0, 0, 0, '', 0, 0, 0, '', 'widthcentpercentminusx');
1851 print "</td></tr>\n";
1852 }
1853
1854 // Other attributes. Fields from hook formObjectOptions and Extrafields.
1855 // $objectsrc is Commande|Facture
1856 $objectsav = $object; // Because Expedition is $expe and not $object that is wrongly a duplicate of $objectsrc.
1857 $object = $expe;
1858 // Propagate extrafieldsvalue from source object to shipment object
1859 if (isset($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && !empty($extrafields->attributes[$object->table_element]['label'])) {
1860 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) {
1861 if (array_key_exists('options_'.$key, $objectsav->array_options)) { // We take value from order only if extrafield has the same name/key.
1862 $object->array_options['options_'.$key] = $objectsav->array_options['options_'.$key];
1863 }
1864 }
1865 }
1866 $parameters = array(
1867 'objectsrc' => isset($objectsrc) ? $objectsrc : '',
1868 'cols' => '3',
1869 'socid' => $socid
1870 );
1871 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php';
1872 $object = $objectsav;
1873
1874 print "</table>";
1875
1876 print dol_get_fiche_end();
1877
1878
1879 // Shipment lines
1880
1881 $numAsked = count($object->lines);
1882
1883 print '<script type="text/javascript">' . "\n";
1884 print 'jQuery(document).ready(function() {' . "\n";
1885 print 'jQuery("#autofill").click(function() {';
1886 $i = 0;
1887 while ($i < $numAsked) {
1888 print 'jQuery("#qtyl' . $i . '").val(jQuery("#qtyasked' . $i . '").val() - jQuery("#qtydelivered' . $i . '").val());' . "\n";
1889 if (isModEnabled('productbatch')) {
1890 print 'jQuery("#qtyl' . $i . '_' . $i . '").val(jQuery("#qtyasked' . $i . '").val() - jQuery("#qtydelivered' . $i . '").val());' . "\n";
1891 }
1892 $i++;
1893 }
1894 print 'return false; });' . "\n";
1895 print 'jQuery("#autoreset").click(function() { console.log("Reset values to 0"); jQuery(".qtyl").val(0);' . "\n";
1896 print 'return false; });' . "\n";
1897 print '});' . "\n";
1898 print '</script>' . "\n";
1899
1900 print '<br>';
1901
1902 print '<div class="div-table-responsive-no-min">';
1903 print '<table class="noborder centpercent">';
1904
1905 // Load shipments already done for same order
1906 $object->loadExpeditions();
1907
1908 $alreadyQtyBatchSetted = $alreadyQtySetted = array();
1909
1910 $title_lines_to_disable = array();
1911
1912 if ($numAsked) {
1913 if (isModEnabled('subtotals')) {
1914 if (!(getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES') || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
1915 $title_lines_to_disable = $object->getDisabledShippmentSubtotalLines();
1916 }
1917 $selectedLines = GETPOST('subtotal_toselect', 'array:int');
1918 foreach ($object->lines as $line) {
1919 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1920 $show_check_add_buttons = true;
1921 break;
1922 }
1923 }
1924 }
1925 print '<tr class="liste_titre">';
1926 print '<td>';
1927 if (isset($show_check_add_buttons)) {
1928 print $form->showCheckAddButtons('checkforselect');
1929 }
1930 print $langs->trans("Description");
1931 print '</td>';
1932 print '<td class="center">' . $langs->trans("QtyOrdered") . '</td>';
1933 print '<td class="center">' . $langs->trans("QtyShipped") . '</td>';
1934 print '<td class="center">' . $langs->trans("QtyToShip");
1935 if (!isModEnabled('productbatch')) {
1936 print '<br><a href="#" id="autofill" class="opacitymedium link cursor cursorpointer">' . img_picto($langs->trans("Autofill"), 'autofill', 'class="paddingrightonly"') . '</a>';
1937 print ' / ';
1938 } else {
1939 print '<br>';
1940 }
1941 print '<span id="autoreset" class="opacitymedium link cursor cursorpointer">' . img_picto($langs->trans("Reset"), 'eraser') . '</span>';
1942 print '</td>';
1943 if (isModEnabled('stock')) {
1944 if (!isModEnabled('productbatch')) {
1945 print '<td class="left">' . $langs->trans("Warehouse") . ' (' . $langs->trans("Stock") . ')</td>';
1946 } else {
1947 print '<td class="left">' . $langs->trans("Warehouse") . ' / ' . $langs->trans("Batch") . ' (' . $langs->trans("Stock") . ')</td>';
1948 }
1949 }
1950 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1951 print '<td class="left">' . $langs->trans('StockEntryDate') . '</td>';
1952 }
1953 print "</tr>\n";
1954 }
1955
1956 $warehouse_id = GETPOSTINT('entrepot_id');
1957 $warehousePicking = array();
1958 // get all warehouse children for picking
1959 if ($warehouse_id > 0) {
1960 $warehousePicking[] = $warehouse_id;
1961 $warehouseObj = new Entrepot($db);
1962 $warehouseObj->get_children_warehouses($warehouse_id, $warehousePicking);
1963 }
1964
1965 $indiceAsked = 0;
1966 while ($indiceAsked < $numAsked) {
1967 $product = new Product($db);
1968
1969 $line = $object->lines[$indiceAsked];
1970
1971 $parameters = array('i' => $indiceAsked, 'line' => $line, 'num' => $numAsked);
1972 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $object, $action);
1973 if ($reshook < 0) {
1974 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1975 }
1976
1977 if (empty($reshook) && $line->special_code != SUBTOTALS_SPECIAL_CODE) {
1978 // Show product and description
1979 $type = $line->product_type ? $line->product_type : $line->fk_product_type;
1980 // Try to enhance type detection using date_start and date_end for free lines where type was not saved.
1981 if (!empty($line->date_start)) {
1982 $type = 1;
1983 }
1984 if (!empty($line->date_end)) {
1985 $type = 1;
1986 }
1987
1988 print '<!-- line for order line ' . $line->id . ' -->' . "\n";
1989 print '<tr class="oddeven" id="row-' . $line->id . '">' . "\n";
1990
1991 $qtyProdCom = $line->qty;
1992 $productChildrenNb = 0;
1993 // Product label
1994 if ($line->fk_product > 0) { // If predefined product
1995 $res = $product->fetch($line->fk_product);
1996 if ($res < 0) {
1997 dol_print_error($db, $product->error, $product->errors);
1998 }
1999 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
2000 $productChildrenNb = $product->hasFatherOrChild(1);
2001 }
2002 if ($productChildrenNb > 0) {
2003 $product->loadStockForVirtualProduct('warehouseopen', $qtyProdCom);
2004 } else {
2005 $product->load_stock('warehouseopen'); // Load all $product->stock_warehouse[idwarehouse]->detail_batch
2006 }
2007 //var_dump($product->stock_warehouse[1]);
2008
2009 print '<td>';
2010 print '<a name="' . $line->id . '"></a>'; // Anchor to retor to the line
2011
2012 // Show product and description
2013 $product_static->type = $line->fk_product_type;
2014 $product_static->id = $line->fk_product;
2015 $product_static->ref = $line->ref;
2016 $product_static->status = $line->product_tosell;
2017 $product_static->status_buy = $line->product_tobuy;
2018 $product_static->status_batch = $line->product_tobatch;
2019
2020 $showdescinproductdesc = getDolGlobalString('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE');
2021
2022 $text = $product_static->getNomUrl(1);
2023 $text .= ' - ' . (!empty($line->label) ? $line->label : $line->product_label);
2024 $description = ($showdescinproductdesc ? '' : dol_htmlentitiesbr($line->desc));
2025 $description .= empty($product->stockable_product) ? $langs->trans('StockDisabled') : $langs->trans('StockEnabled');
2026 print $form->textwithtooltip($text, $description, 3, 0, '', (string) $i);
2027
2028 // Show range
2029 print_date_range($db->jdate($line->date_start), $db->jdate($line->date_end));
2030
2031 // Add description in form
2032 if ($showdescinproductdesc) {
2033 print ($line->desc && $line->desc != $line->product_label) ? '<br>' . dol_htmlentitiesbr($line->desc) : '';
2034 }
2035
2036 $expLine = new ExpeditionLigne($db);
2037 $srcLine = new OrderLine($db);
2038 $srcLine->id = $line->id;
2039 $srcLine->fetch_optionals(); // Fetch source line extrafields so shipment line creation matches order UI.
2040 $expLine->array_options = array_merge($expLine->array_options, $srcLine->array_options);
2041 $lineExtrafieldsHtml = $expLine->showOptionals($extrafields, 'edit', array(), (string) $indiceAsked, '', '1', 'line');
2042 if (!empty($lineExtrafieldsHtml)) {
2043 print '<div style="padding-top: 10px" id="extrafield_lines_area_create_' . $indiceAsked . '" name="extrafield_lines_area_create_' . $indiceAsked . '">';
2044 print $lineExtrafieldsHtml;
2045 print '</div>';
2046 }
2047
2048 print '</td>';
2049 } else {
2050 print "<td>";
2051 if ($type == 1) {
2052 $text = img_object($langs->trans('Service'), 'service');
2053 } else {
2054 $text = img_object($langs->trans('Product'), 'product');
2055 }
2056
2057 if (!empty($line->label)) {
2058 $text .= ' <strong>' . $line->label . '</strong>';
2059 print $form->textwithtooltip($text, $line->desc, 3, 0, '', (string) $i);
2060 } else {
2061 print $text . ' ' . nl2br($line->desc);
2062 }
2063
2064 // Show range
2065 print_date_range($db->jdate($line->date_start), $db->jdate($line->date_end));
2066
2067 $expLine = new ExpeditionLigne($db);
2068 $srcLine = new OrderLine($db);
2069 $srcLine->id = $line->id;
2070 $srcLine->fetch_optionals(); // Fetch source line extrafields so shipment line creation matches order UI.
2071 $expLine->array_options = array_merge($expLine->array_options, $srcLine->array_options);
2072 $lineExtrafieldsHtml = $expLine->showOptionals($extrafields, 'edit', array(), (string) $indiceAsked, '', '1', 'line');
2073 if (!empty($lineExtrafieldsHtml)) {
2074 print '<div style="padding-top: 10px" id="extrafield_lines_area_create_' . $indiceAsked . '" name="extrafield_lines_area_create_' . $indiceAsked . '">';
2075 print $lineExtrafieldsHtml;
2076 print '</div>';
2077 }
2078 print "</td>\n";
2079 }
2080
2081 // unit of order
2082 $unit_order = '';
2083 if (getDolGlobalString('PRODUCT_USE_UNITS')) {
2084 $unit_order = measuringUnitString($line->fk_unit);
2085 }
2086
2087 // Qty
2088 print '<td class="center">' . $line->qty;
2089 print '<input name="qtyasked' . $indiceAsked . '" id="qtyasked' . $indiceAsked . '" type="hidden" value="' . $line->qty . '">';
2090 if ($line->qty) {
2091 print ' ' . $unit_order . '</td>';
2092 }
2093
2094 // Qty already shipped
2095 print '<td class="center">';
2096 $quantityDelivered = isset($object->expeditions[$line->id]) ? $object->expeditions[$line->id] : '';
2097 print $quantityDelivered;
2098 print '<input name="qtydelivered' . $indiceAsked . '" id="qtydelivered' . $indiceAsked . '" type="hidden" value="' . $quantityDelivered . '">';
2099 if ($quantityDelivered) {
2100 print ' ' . $unit_order . '</td>';
2101 }
2102
2103 // Qty to ship
2104 $quantityAsked = $line->qty;
2105 if ($line->product_type == Product::TYPE_SERVICE && !getDolGlobalString('STOCK_SUPPORTS_SERVICES') && !getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
2106 $quantityToBeDelivered = 0;
2107 } else {
2108 if (is_numeric($quantityDelivered)) {
2109 $quantityToBeDelivered = $quantityAsked - $quantityDelivered;
2110 } else {
2111 $quantityToBeDelivered = $quantityAsked;
2112 }
2113 }
2114
2115 $warehouseObject = null;
2116 if (count($warehousePicking) == 1 || !($line->fk_product > 0) || !isModEnabled('stock')) { // If warehouse was already selected or if product is not a predefined, we go into this part with no multiwarehouse selection
2117 print '<!-- Case warehouse already known or product not a predefined product -->';
2118 //ship from preselected location
2119 $stock = + (isset($product->stock_warehouse[$warehouse_id]->real) ? $product->stock_warehouse[$warehouse_id]->real : 0); // Convert to number
2120 if (($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) || $product->stockable_product == Product::DISABLED_STOCK) {
2121 $deliverableQty = $quantityToBeDelivered;
2122 } else {
2123 $deliverableQty = min($quantityToBeDelivered, $stock);
2124 }
2125 if ($deliverableQty < 0) {
2126 $deliverableQty = 0;
2127 }
2128 if (!isModEnabled('productbatch') || !$product->hasbatch()) {
2129 // Quantity to send
2130 print '<td class="center">';
2131 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || ($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES'))) {
2132 if (GETPOSTISSET('qtyl'.$indiceAsked) && GETPOST('qtyl'.$indiceAsked) !== '') {
2133 $deliverableQty = GETPOSTFLOAT('qtyl'.$indiceAsked, 'MS');
2134 }
2135 print '<input name="idl' . $indiceAsked . '" type="hidden" value="' . $line->id . '">';
2136 $qtylValue = $deliverableQty;
2137 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
2138 $qtylValue = '';
2139 } elseif (is_numeric($qtylValue)) {
2140 // Render as locale number so GETPOSTFLOAT() on submit reads it back via price2num option=2 without confusing '.' for a thousand separator in es_ES.
2141 $qtylValue = price($qtylValue);
2142 }
2143 print '<input name="qtyl' . $indiceAsked . '" id="qtyl' . $indiceAsked . '" class="qtyl right" type="text" size="4" value="' . $qtylValue . '">';
2144 } else {
2145 if (getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
2146 print '<input name="idl' . $indiceAsked . '" type="hidden" value="' . $line->id . '">';
2147 print '<input name="qtyl' . $indiceAsked . '" id="qtyl' . $indiceAsked . '" type="hidden" value="0">';
2148 }
2149
2150 print $langs->trans("NA");
2151 }
2152 print '</td>';
2153
2154 // Stock
2155 if (isModEnabled('stock')) {
2156 print '<td class="left">';
2157 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) { // Type of product need stock change ?
2158 // Show warehouse combo list
2159 $ent = "entl" . $indiceAsked;
2160 $idl = "idl" . $indiceAsked;
2161 $tmpentrepot_id = is_numeric(GETPOST($ent)) ? GETPOSTINT($ent) : $warehouse_id;
2162 if ($line->fk_product > 0) {
2163 print '<!-- Show warehouse selection -->';
2164
2165 $stockMin = false;
2166 if (getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
2167 $stockMin = 0;
2168 }
2169 if ($productChildrenNb > 0) {
2170 print $formproduct->selectWarehouses($tmpentrepot_id, 'entl' . $indiceAsked, '', 1, 0, 0, '', 0, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
2171 } else {
2172 if ($product->stockable_product == Product::ENABLED_STOCK) {
2173 print $formproduct->selectWarehouses($tmpentrepot_id, 'entl' . $indiceAsked, '', 1, 0, $line->fk_product, '', 1, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
2174 } else {
2175 print img_warning() . ' ' . $langs->trans('StockDisabled');
2176 }
2177 }
2178
2179 if ($tmpentrepot_id > 0 && $tmpentrepot_id == $warehouse_id) {
2180 //print $stock.' '.$quantityToBeDelivered;
2181 if ($stock < $quantityToBeDelivered) {
2182 print ' ' . img_warning($langs->trans("StockTooLow")); // Stock too low for this $warehouse_id but you can change warehouse
2183 }
2184 }
2185 }
2186 } else {
2187 print '<span class="opacitymedium">(' . $langs->trans("Service") . ')</span><input name="entl' . $indiceAsked . '" id="entl' . $indiceAsked . '" type="hidden" value="0">';
2188 }
2189 print '</td>';
2190 }
2191 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2192 print '<td></td>';
2193 } //StockEntrydate
2194 print "</tr>\n";
2195
2196 // Show subproducts of product
2197 if (getDolGlobalString('PRODUIT_SOUSPRODUITS') && $line->fk_product > 0) {
2198 $product->get_sousproduits_arbo();
2199 $prods_arbo = $product->get_arbo_each_prod($qtyProdCom);
2200 if (count($prods_arbo) > 0) {
2201 foreach ($prods_arbo as $key => $value) {
2202 //print $value[0];
2203 $img = '';
2204 if ($value['stock'] < $value['stock_alert']) {
2205 $img = img_warning($langs->trans("StockTooLow"));
2206 }
2207 print "<tr class=\"oddeven\"><td>&nbsp; &nbsp; &nbsp; ->
2208 <a href=\"" . DOL_URL_ROOT . "/product/card.php?id=" . $value['id'] . "\">" . $value['fullpath'] . "
2209 </a> (" . $value['nb'] . ")</td><td class=\"center\"> " . $value['nb_total'] . "</td><td>&nbsp;</td><td>&nbsp;</td>
2210 <td class=\"center\">" . $value['stock'] . " " . $img . "</td>";
2211 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2212 print '<td></td>';
2213 } //StockEntrydate
2214 print "</tr>";
2215 }
2216 }
2217 }
2218 } else {
2219 // Product need lot
2220 print '<td></td><td></td>';
2221 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2222 print '<td></td>';
2223 } //StockEntrydate
2224 print '</tr>'; // end line and start a new one for lot/serial
2225 print '<!-- Case product need lot -->';
2226
2227 $staticwarehouse = new Entrepot($db);
2228 if ($warehouse_id > 0) {
2229 $staticwarehouse->fetch($warehouse_id);
2230 }
2231
2232 $subj = 0;
2233 // Define nb of lines suggested for this order line
2234 $nbofsuggested = 0;
2235 if (is_object($product->stock_warehouse[$warehouse_id]) && !empty($product->stock_warehouse[$warehouse_id]->detail_batch)) {
2236 foreach ($product->stock_warehouse[$warehouse_id]->detail_batch as $dbatch) {
2237 $nbofsuggested++;
2238 }
2239 }
2240 print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
2241 if (is_object($product->stock_warehouse[$warehouse_id]) && !empty($product->stock_warehouse[$warehouse_id]->detail_batch)) {
2242 foreach ($product->stock_warehouse[$warehouse_id]->detail_batch as $dbatch) { // $dbatch is instance of Productbatch
2243 //var_dump($dbatch);
2244 $batchStock = +$dbatch->qty; // To get a numeric
2245 $deliverableQty = min($quantityToBeDelivered, $batchStock);
2246
2247 // Now we will check if we have to reduce the deliverableQty by taking into account the qty already suggested in previous line
2248 if (isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
2249 $deliverableQty = min($quantityToBeDelivered, $batchStock - $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)]);
2250 } else {
2251 if (!isset($alreadyQtyBatchSetted[$line->fk_product])) {
2252 $alreadyQtyBatchSetted[$line->fk_product] = array();
2253 }
2254
2255 if (!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])) {
2256 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch] = array();
2257 }
2258
2259 $deliverableQty = min($quantityToBeDelivered, $batchStock);
2260 }
2261
2262 if ($deliverableQty < 0) {
2263 $deliverableQty = 0;
2264 }
2265
2266 $inputName = 'qtyl' . $indiceAsked . '_' . $subj;
2267 if (GETPOSTISSET($inputName)) {
2268 $deliverableQty = GETPOST($inputName, 'int');
2269 }
2270
2271 $tooltipClass = $tooltipTitle = '';
2272 if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
2273 $tooltipClass = ' classfortooltip';
2274 $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines') . ' : ' . $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
2275 } else {
2276 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = 0;
2277 }
2278 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = $deliverableQty + $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
2279
2280 print '<!-- subj=' . $subj . '/' . $nbofsuggested . ' --><tr ' . ((($subj + 1) == $nbofsuggested) ? 'oddeven' : '') . '>';
2281 print '<td colspan="3" ></td><td class="center">';
2282 $qtylValue = $deliverableQty;
2283 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
2284 $qtylValue = '';
2285 } elseif (is_numeric($qtylValue)) {
2286 $qtylValue = price($qtylValue);
2287 }
2288 print '<input class="qtyl ' . $tooltipClass . ' right" title="' . $tooltipTitle . '" name="qtyl' . $indiceAsked . '_' . $subj . '" id="qtyl' . $indiceAsked . '_' . $subj . '" type="text" size="4" value="' . $qtylValue . '">';
2289 print '</td>';
2290
2291 print '<!-- Show details of lot -->';
2292 print '<td class="left">';
2293
2294 print $staticwarehouse->getNomUrl(0) . ' / ';
2295
2296 print '<input name="batchl' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $dbatch->id . '">';
2297
2298 $detail = '';
2299 $detail .= $langs->trans("Batch") . ': ' . $dbatch->batch;
2300 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY') && !empty($dbatch->sellby)) {
2301 $detail .= ' - ' . $langs->trans("SellByDate") . ': ' . dol_print_date($dbatch->sellby, "day");
2302 }
2303 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY') && !empty($dbatch->eatby)) {
2304 $detail .= ' - ' . $langs->trans("EatByDate") . ': ' . dol_print_date($dbatch->eatby, "day");
2305 }
2306 $detail .= ' - ' . $langs->trans("Qty") . ': ' . $dbatch->qty;
2307 $detail .= '<br>';
2308 print $detail;
2309
2310 $quantityToBeDelivered -= $deliverableQty;
2311 if ($quantityToBeDelivered < 0) {
2312 $quantityToBeDelivered = 0;
2313 }
2314 $subj++;
2315 print '</td>';
2316 if (getDolGlobalInt('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2317 print '<td>' . dol_print_date($dbatch->context['stock_entry_date'], 'day') . '</td>'; //StockEntrydate
2318 }
2319 print '</tr>';
2320 }
2321 } else {
2322 print '<!-- Case there is no details of lot at all -->';
2323 print '<tr class="oddeven"><td colspan="3"></td><td class="center">';
2324 print '<input class="qtyl right" name="qtyl' . $indiceAsked . '_' . $subj . '" id="qtyl' . $indiceAsked . '_' . $subj . '" type="text" size="4" value="0" disabled="disabled"> ';
2325 print '</td>';
2326
2327 print '<td class="left">';
2328 print img_warning() . ' ' . $langs->trans("NoProductToShipFoundIntoStock", $staticwarehouse->label);
2329 print '</td>';
2330 if (getDolGlobalInt('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2331 print '<td></td>';
2332 } //StockEntrydate
2333 print '</tr>';
2334 }
2335 }
2336 } else {
2337 // ship from multiple locations
2338 if (!isModEnabled('productbatch') || !$product->hasbatch()) {
2339 print '<!-- Case warehouse not already known and product does not need lot -->';
2340 print '<td></td><td></td>';
2341 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2342 print '<td></td>';
2343 } //StockEntrydate
2344 print '</tr>' . "\n"; // end line and start a new one for each warehouse
2345
2346 print '<input name="idl' . $indiceAsked . '" type="hidden" value="' . $line->id . '">';
2347 $subj = 0;
2348 // Define nb of lines suggested for this order line
2349 $nbofsuggested = 0;
2350
2351 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
2352 if ($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
2353 $nbofsuggested++;
2354 }
2355 }
2356 $tmpwarehouseObject = new Entrepot($db);
2357 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) { // $stock_warehouse is product_stock
2358 $var = $subj % 2;
2359 if (!empty($warehousePicking) && !in_array($warehouse_id, $warehousePicking)) {
2360 // if a warehouse was selected by user, picking is limited to this warehouse and his children
2361 continue;
2362 }
2363
2364 $tmpwarehouseObject->fetch($warehouse_id);
2365 if ($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
2366 $stock = +$stock_warehouse->real; // Convert it to number
2367 $deliverableQty = min($quantityToBeDelivered, $stock);
2368 $deliverableQty = max(0, $deliverableQty);
2369 // Quantity to send
2370 print '<!-- subj=' . $subj . '/' . $nbofsuggested . ' --><tr ' . ((($subj + 1) == $nbofsuggested) ? 'oddeven' : '') . '>';
2371 print '<td colspan="3" ></td><td class="center"><!-- qty to ship (no lot management for product line indiceAsked=' . $indiceAsked . ') -->';
2372 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
2373 // For a virtual product (kit), $deliverableQty is already min($quantityToBeDelivered, $stock) computed above
2374 // and the per-warehouse "stock" returned by loadStockForVirtualProduct() is $qtyWish, not a shared physical
2375 // stock. The cross-line $alreadyQtySetted cap must not be applied for kits, otherwise the 2nd and next order
2376 // lines of the same kit would be wrongly pre-filled with 0.
2377 $tooltipClass = $tooltipTitle = '';
2378 if ($productChildrenNb <= 0) {
2379 if (isset($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])) {
2380 $deliverableQty = min($quantityToBeDelivered, $stock - $alreadyQtySetted[$line->fk_product][intval($warehouse_id)]);
2381 } else {
2382 if (!isset($alreadyQtySetted[$line->fk_product])) {
2383 $alreadyQtySetted[$line->fk_product] = array();
2384 }
2385
2386 $deliverableQty = min($quantityToBeDelivered, $stock);
2387 }
2388
2389 if ($deliverableQty < 0) {
2390 $deliverableQty = 0;
2391 }
2392
2393 if (!empty($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])) {
2394 $tooltipClass = ' classfortooltip';
2395 $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$alreadyQtySetted[$line->fk_product][intval($warehouse_id)];
2396 } else {
2397 $alreadyQtySetted[$line->fk_product][intval($warehouse_id)] = 0;
2398 }
2399
2400 $alreadyQtySetted[$line->fk_product][intval($warehouse_id)] = $deliverableQty + $alreadyQtySetted[$line->fk_product][intval($warehouse_id)];
2401 }
2402
2403 $inputName = 'qtyl'.$indiceAsked.'_'.$subj;
2404 if (GETPOSTISSET($inputName)) {
2405 $deliverableQty = GETPOSTINT($inputName);
2406 }
2407 $qtylValue = $deliverableQty;
2408 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
2409 $qtylValue = '';
2410 } elseif (is_numeric($qtylValue)) {
2411 $qtylValue = price($qtylValue);
2412 }
2413 print '<input class="qtyl' . $tooltipClass . ' right" title="' . $tooltipTitle . '" name="qtyl' . $indiceAsked . '_' . $subj . '" id="qtyl' . $indiceAsked . '" type="text" size="4" value="' . $qtylValue . '">';
2414 print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $warehouse_id . '">';
2415 } else {
2416 if (getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
2417 print '<input name="qtyl' . $indiceAsked . '_' . $subj . '" id="qtyl' . $indiceAsked . '" type="hidden" value="0">';
2418 }
2419
2420 print $langs->trans("NA");
2421 }
2422 print '</td>';
2423
2424 // Stock
2425 print '<td class="left">';
2426 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
2427 if ($product->stockable_product == Product::ENABLED_STOCK || $productChildrenNb > 0) {
2428 print $tmpwarehouseObject->getNomUrl(0) . ' ';
2429 if ($productChildrenNb <= 0) {
2430 print '<!-- Show details of stock -->';
2431 print '(' . $stock . ')';
2432 }
2433 } else {
2434 print img_warning() . ' ' . $langs->trans('StockDisabled');
2435 }
2436 } else {
2437 print '<span class="opacitymedium">(' . $langs->trans("Service") . ')</span>';
2438 }
2439 print '</td>';
2440 $quantityToBeDelivered -= $deliverableQty;
2441 if ($quantityToBeDelivered < 0) {
2442 $quantityToBeDelivered = 0;
2443 }
2444 $subj++;
2445 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2446 print '<td></td>';
2447 } //StockEntrydate
2448 print "</tr>\n";
2449 }
2450 }
2451 // Show subproducts of product (not recommended)
2452 if (getDolGlobalString('PRODUIT_SOUSPRODUITS') && $line->fk_product > 0) {
2453 $product->get_sousproduits_arbo();
2454 $prods_arbo = $product->get_arbo_each_prod($qtyProdCom);
2455 if (count($prods_arbo) > 0) {
2456 foreach ($prods_arbo as $key => $value) {
2457 //print $value[0];
2458 $img = '';
2459 if ($value['stock'] < $value['stock_alert']) {
2460 $img = img_warning($langs->trans("StockTooLow"));
2461 }
2462 print '<tr class"oddeven"><td>';
2463 print "&nbsp; &nbsp; &nbsp; ->
2464 <a href=\"" . DOL_URL_ROOT . "/product/card.php?id=" . $value['id'] . "\">" . $value['fullpath'] . "
2465 </a> (" . $value['nb'] . ")</td><td class=\"center\"> " . $value['nb_total'] . "</td><td>&nbsp;</td><td>&nbsp;</td>
2466 <td class=\"center\">" . $value['stock'] . " " . $img . "</td>";
2467 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2468 print '<td></td>';
2469 } //StockEntrydate
2470 print "</tr>";
2471 }
2472 }
2473 }
2474 } else {
2475 print '<!-- Case warehouse not already known and product need lot -->';
2476 print '<td></td><td></td>';
2477 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2478 print '<td></td>';
2479 } //StockEntrydate
2480 print '</tr>'; // end line and start a new one for lot/serial
2481
2482 $subj = 0;
2483 print '<input name="idl' . $indiceAsked . '" type="hidden" value="' . $line->id . '">';
2484
2485 $tmpwarehouseObject = new Entrepot($db);
2486 $productlotObject = new Productlot($db);
2487
2488 // Define nb of lines suggested for this order line
2489 $nbofsuggested = 0;
2490 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
2491 if (($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) && (!empty($stock_warehouse->detail_batch))) {
2492 $nbofsuggested += count($stock_warehouse->detail_batch);
2493 }
2494 }
2495
2496 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
2497 $var = $subj % 2;
2498 if (!empty($warehousePicking) && !in_array($warehouse_id, $warehousePicking)) {
2499 // if a warehouse was selected by user, picking is limited to this warehouse and his children
2500 continue;
2501 }
2502
2503 $tmpwarehouseObject->fetch($warehouse_id);
2504 if (($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) && (!empty($stock_warehouse->detail_batch))) {
2505 foreach ($stock_warehouse->detail_batch as $dbatch) {
2506 $batchStock = +$dbatch->qty; // To get a numeric
2507 if (isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
2508 $deliverableQty = min($quantityToBeDelivered, $batchStock - $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)]);
2509 } else {
2510 if (!isset($alreadyQtyBatchSetted[$line->fk_product])) {
2511 $alreadyQtyBatchSetted[$line->fk_product] = array();
2512 }
2513
2514 if (!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])) {
2515 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch] = array();
2516 }
2517
2518 $deliverableQty = min($quantityToBeDelivered, $batchStock);
2519 }
2520
2521 if ($deliverableQty < 0) {
2522 $deliverableQty = 0;
2523 }
2524
2525 $inputName = 'qtyl' . $indiceAsked . '_' . $subj;
2526 if (GETPOSTISSET($inputName)) {
2527 $deliverableQty = GETPOSTINT($inputName);
2528 }
2529
2530 $tooltipClass = $tooltipTitle = '';
2531 if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
2532 $tooltipClass = ' classfortooltip';
2533 $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines') . ' : ' . $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
2534 } else {
2535 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = 0;
2536 }
2537 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = $deliverableQty + $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
2538
2539 print '<!-- subj=' . $subj . '/' . $nbofsuggested . ' --><tr ' . ((($subj + 1) == $nbofsuggested) ? 'oddeven' : '') . '><td colspan="3"></td><td class="center">';
2540 $qtylValue = $deliverableQty;
2541 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
2542 $qtylValue = '';
2543 } elseif (is_numeric($qtylValue)) {
2544 $qtylValue = price($qtylValue);
2545 }
2546 print '<input class="qtyl right ' . $tooltipClass . '" title="' . $tooltipTitle . '" name="' . $inputName . '" id="' . $inputName . '" type="text" size="4" value="' . $qtylValue . '">';
2547 print '</td>';
2548
2549 print '<td class="left">';
2550
2551 print $tmpwarehouseObject->getNomUrl(0) . ' / ';
2552
2553 print '<!-- Show details of lot -->';
2554 print '<input name="batchl' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $dbatch->id . '">';
2555 print '<input name="entl' . $indiceAsked . '_'.$subj.'" type="hidden" value="' . $tmpwarehouseObject->id . '">';
2556
2557 //print '|'.$line->fk_product.'|'.$dbatch->batch.'|<br>';
2558 print $langs->trans("Batch") . ': ';
2559 $result = $productlotObject->fetch(0, $line->fk_product, $dbatch->batch);
2560 if ($result > 0) {
2561 print $productlotObject->getNomUrl(1);
2562 } else {
2563 print $langs->trans("TableLotIncompleteRunRepairWithParamStandardEqualConfirmed");
2564 }
2565 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY') && !empty($dbatch->sellby)) {
2566 print ' - ' . $langs->trans("SellByDate") . ': ' . dol_print_date($dbatch->sellby, "day");
2567 }
2568 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY') && !empty($dbatch->eatby)) {
2569 print ' - ' . $langs->trans("EatByDate") . ': ' . dol_print_date($dbatch->eatby, "day");
2570 }
2571 print ' (' . $dbatch->qty . ')';
2572 $quantityToBeDelivered -= $deliverableQty;
2573 if ($quantityToBeDelivered < 0) {
2574 $quantityToBeDelivered = 0;
2575 }
2576 //dol_syslog('deliverableQty = '.$deliverableQty.' batchStock = '.$batchStock);
2577 $subj++;
2578 print '</td>';
2579 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2580 print '<td class="left">' . dol_print_date($dbatch->context['stock_entry_date'], 'day') . '</td>';
2581 }
2582 print '</tr>';
2583 }
2584 }
2585 }
2586 }
2587 if ($subj == 0) { // Line not shown yet, we show it
2588 $warehouse_selected_id = GETPOSTINT('entrepot_id');
2589
2590 print '<!-- line not shown yet, we show it -->';
2591 print '<tr class="oddeven"><td colspan="3"></td><td class="center">';
2592
2593 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
2594 $disabled = '';
2595 $alt = '';
2596 if (isModEnabled('productbatch') && $product->hasbatch()) {
2597 $disabled = 'disabled="disabled"';
2598 $alt = 'Product need serial or batch number';
2599 }
2600 if ($warehouse_selected_id <= 0) { // We did not force a given warehouse, so we won't have no warehouse to change qty.
2601 $disabled = 'disabled="disabled"';
2602 }
2603 // finally we overwrite the input with the product status stockable_product if it's disabled
2604 if ($product->stockable_product == Product::ENABLED_STOCK) {
2605 $disabled = '';
2606 }
2607 print '<input class="qtyl right" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="0"'.($disabled ? ' '.$disabled : '').($alt ? ' title="'.dolPrintHTMLForAttribute($alt).'"' : '').'> ';
2608 if (empty($disabled) && (!getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER') || $product->stockable_product == Product::DISABLED_STOCK)) {
2609 print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $warehouse_selected_id . '">';
2610 }
2611 } elseif ($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
2612 // If we are here, it means STOCK_SUPPORTS_SERVICES is off (otherwise the previous test would be true)
2613 // So a service has no stock here, so neither a preselected warehouse nor STOCK_DISALLOW_NEGATIVE_TRANSFER is relevant here.
2614 $disabled = '';
2615 if (isModEnabled('productbatch') && $product->hasbatch()) {
2616 $disabled = 'disabled="disabled"';
2617 }
2618 print '<input class="qtyl right" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="'.$quantityToBeDelivered.'"'.($disabled ? ' '.$disabled : '').'> ';
2619 // This hidden field must always be output: it is the marker the POST handler uses to detect that the quantities
2620 // of this line are named qtyl<i>_<subj>. Without it the handler looks for qtyl<i> instead, finds nothing and
2621 // silently drops the line. Value 0 = no warehouse, same as entl<i> in the single warehouse case above.
2622 print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="0">';
2623 } else {
2624 print $langs->trans("NA");
2625 }
2626 print '</td>';
2627
2628 print '<td class="left">';
2629 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
2630 // product may use stock management
2631 if (($warehouse_selected_id > 0 && $product->stockable_product == Product::ENABLED_STOCK)) {
2632 $warehouseObject = new Entrepot($db);
2633 $warehouseObject->fetch($warehouse_selected_id);
2634 print img_warning() . ' ' . $langs->trans("NoProductToShipFoundIntoStock", $warehouseObject->label);
2635 } else {
2636 if ($line->fk_product) {
2637 if ($product->stockable_product != Product::ENABLED_STOCK) {
2638 print img_warning() . ' ' . $langs->trans('StockDisabled');
2639 } else {
2640 print img_warning() . ' ' . $langs->trans('StockTooLow');
2641 }
2642 } else {
2643 print '';
2644 }
2645 }
2646 } else {
2647 print '<span class="opacitymedium">(' . $langs->trans("Service") . ')</span>';
2648 }
2649 print '</td>';
2650 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2651 print '<td></td>';
2652 } //StockEntrydate
2653 print '</tr>';
2654 }
2655 }
2656 } elseif (empty($reshook) && $line->special_code == SUBTOTALS_SPECIAL_CODE && !in_array($line->id, $title_lines_to_disable)) {
2657 require dol_buildpath('/core/tpl/subtotalline_select.tpl.php');
2658 }
2659
2660 $indiceAsked++;
2661 }
2662
2663 print "</table>";
2664 print '</div>';
2665
2666 print '<br>';
2667
2668 print $form->buttonsSaveCancel("Create");
2669
2670 print '</form>';
2671
2672 print '<br>';
2673 } else {
2674 dol_print_error($db);
2675 }
2676 }
2677} elseif ($object->id > 0) {
2678 '@phan-var-force Expedition $object'; // Need to force it (type overridden earlier)
2679
2680 // Edit and view mode
2681
2682 $lines = $object->lines;
2683
2684 $num_prod = count($lines);
2685
2686 if (!empty($object->origin) && $object->origin_id > 0) {
2687 $typeobject = $object->origin;
2688 $origin = (string) $object->origin; // Cast to string because origin is type hinted as object too.
2689 $origin_id = $object->origin_id;
2690
2691 $object->fetch_origin(); // Load property $object->origin_object (old $object->commande, $object->propal, ...)
2692 }
2693
2694 $soc = new Societe($db);
2695 $soc->fetch($object->socid);
2696
2697 $res = $object->fetch_optionals();
2698
2699 $head = shipping_prepare_head($object);
2700 print dol_get_fiche_head($head, 'shipping', $langs->trans("Shipment"), -1, $object->picto, 0, '', '', 0, '', 1);
2701
2702 $formconfirm = '';
2703
2704 // Confirm deletion
2705 if ($action == 'delete') {
2706 $formquestion = array();
2707 if ($object->status == Expedition::STATUS_CLOSED && getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) {
2708 $formquestion = array(
2709 array(
2710 'type' => 'onecolumn',
2711 'value' => img_warning().' <strong>'.$langs->trans('WarningShipmentDeleteStockMovement').'</strong>',
2712 ),
2713 array(
2714 'label' => $langs->trans('ShipmentIncrementStockOnDelete'),
2715 'name' => 'alsoUpdateStock',
2716 'type' => 'checkbox',
2717 'value' => 0 /* Default must be "No stock change". Deletion is rarely used to cancel a shipment (not the correct business event for that). Re-open + Cancel is better for this. Deletion is usually used to clean db and should not impact the stock */
2718 ),
2719 );
2720 }
2721 $formconfirm = $form->formconfirm(
2722 $_SERVER['PHP_SELF'] . '?id=' . $object->id,
2723 $langs->trans('DeleteSending'),
2724 $langs->trans("ConfirmDeleteSending", $object->ref),
2725 'confirm_delete',
2726 $formquestion,
2727 0,
2728 1,
2729 'auto'
2730 );
2731 }
2732
2733 // Confirm delete of subtotal line
2734 if ($action == 'ask_subtotal_deleteline') {
2735 $lineid = GETPOSTINT('lineid');
2736 $langs->load("subtotals");
2737 $title = "DeleteSubtotalLine";
2738 $question = "ConfirmDeleteSubtotalLine";
2739 if (GETPOST('type') == 'title') {
2740 $formconfirm = array(array('type' => 'checkbox', 'name' => 'deletecorrespondingsubtotalline', 'label' => $langs->trans("DeleteCorrespondingSubtotalLine"), 'value' => 0));
2741 $title = "DeleteTitleLine";
2742 $question = "ConfirmDeleteTitleLine";
2743 }
2744 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id . '&lineid=' . $lineid, $langs->trans($title), $langs->trans($question), 'confirm_delete_subtotalline', $formconfirm, 'no', 1);
2745 }
2746
2747 // Confirm validation
2748 if ($action == 'valid') {
2749 $objectref = substr($object->ref, 1, 4);
2750 if ($objectref == 'PROV') {
2751 $numref = $object->getNextNumRef($soc);
2752 } else {
2753 $numref = (string) $object->ref;
2754 }
2755
2756 $text = $langs->trans("ConfirmValidateSending", $numref);
2757 if (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) {
2758 $text .= '<br>' . img_picto('', 'movement', 'class="pictofixedwidth"') . $langs->trans("StockMovementWillBeRecorded") . '.';
2759 } elseif (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) {
2760 $text .= '<br>' . img_picto('', 'movement', 'class="pictofixedwidth"') . $langs->trans("StockMovementNotYetRecorded") . '.';
2761 }
2762
2763 if (isModEnabled('notification')) {
2764 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2765 $notify = new Notify($db);
2766 $text .= '<br>';
2767 $text .= $notify->confirmMessage('SHIPPING_VALIDATE', $object->socid, $object);
2768 }
2769
2770 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id, $langs->trans('ValidateSending'), $text, 'confirm_valid', '', 0, 1, 260);
2771 }
2772
2773 // Confirm cancellation
2774 if ($action == 'cancel') {
2775 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('CancelSending'), $langs->trans("ConfirmCancelSending", $object->ref), 'confirm_cancel', '', 0, 1);
2776 }
2777
2778 // Confirm sign
2779 if ($action == 'sign') {
2780 $text = $langs->trans('ConfirmSignShipping');
2781 if (isModEnabled('notification')) {
2782 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2783 $notify = new Notify($db);
2784 $text .= '<br>';
2785 $text .= $notify->confirmMessage('SHIPPING_MODIFY', $object->socid, $object);
2786 }
2787 $formquestion = [];
2788 $formquestion[] = [
2789 'type' => 'select',
2790 'name' => 'signed_status',
2791 'select_show_empty' => 0,
2792 'label' => '<span class="fieldrequired">' . $langs->trans('SignStatus') . '</span>',
2793 'values' => $object->getSignedStatusLocalisedArray()
2794 ];
2795 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('SignShipping'), $text, 'confirm_sign', $formquestion, 0, 1);
2796 }
2797
2798 // Confirm unsign
2799 if ($action == 'unsign') {
2800 $text = $langs->trans('ConfirmUnsignShipping');
2801 if (isModEnabled('notification')) {
2802 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2803 $notify = new Notify($db);
2804 $text .= '<br>';
2805 $text .= $notify->confirmMessage('SHIPPING_MODIFY', $object->socid, $object);
2806 }
2807 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('UnsignShipping'), $text, 'confirm_unsign', '', 0, 1);
2808 }
2809
2810 // Call Hook formConfirm
2811 $parameters = array('formConfirm' => $formconfirm);
2812 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2813 if (empty($reshook)) {
2814 $formconfirm .= $hookmanager->resPrint;
2815 } elseif ($reshook > 0) {
2816 $formconfirm = $hookmanager->resPrint;
2817 }
2818
2819 // Print form confirm
2820 print $formconfirm;
2821
2822 // Calculate totalWeight and totalVolume for all products
2823 // by adding weight and volume of each product line.
2824 $tmparray = $object->getTotalWeightVolume();
2825 $totalWeight = $tmparray['weight'];
2826 $totalVolume = $tmparray['volume'];
2827
2828 if (!empty($typeobject) && $typeobject === 'commande' && is_object($object->origin_object) && $object->origin_object->id && isModEnabled('order')) {
2829 $objectsrc = new Commande($db);
2830 $objectsrc->fetch($object->origin_object->id);
2831 }
2832 if (!empty($typeobject) && $typeobject === 'propal' && is_object($object->origin_object) && $object->origin_object->id && isModEnabled("propal")) {
2833 $objectsrc = new Propal($db);
2834 $objectsrc->fetch($object->origin_object->id);
2835 }
2836
2837 // Shipment card
2838 $linkback = '<a href="' . DOL_URL_ROOT . '/expedition/list.php?restore_lastsearch_values=1' . (!empty($socid) ? '&socid=' . $socid : '') . '">' . $langs->trans("BackToList") . '</a>';
2839 $morehtmlref = '<div class="refidno">';
2840 // Ref customer shipment
2841 $morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_customer', $object->ref_customer, $object, $user->hasRight('expedition', 'creer'), 'string', '', 0, 1);
2842 $morehtmlref .= $form->editfieldval("RefCustomer", 'ref_customer', $object->ref_customer, $object, $user->hasRight('expedition', 'creer'), 'string' . (isset($conf->global->THIRDPARTY_REF_INPUT_SIZE) ? ':' . getDolGlobalString('THIRDPARTY_REF_INPUT_SIZE') : ''), '', null, null, '', 1);
2843
2844 // Thirdparty
2845 $morehtmlref .= '<br>' . $object->thirdparty->getNomUrl(1);
2846
2847 // Project
2848 if ($origin && $origin_id > 0) {
2849 if (isModEnabled('project')) {
2850 $langs->load("projects");
2851 $morehtmlref .= '<br>';
2852 if (0) { // @phpstan-ignore-line Do not change on shipment
2853 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
2854 if ($action != 'classify') {
2855 $morehtmlref .= '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=classify&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
2856 }
2857 $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'].'?id='.$object->id, $objectsrc->socid, (string) $objectsrc->fk_project, ($action == 'classify' ? 'projectid' : 'none'), 0, 0, 0, 1, '', 'maxwidth300');
2858 } else {
2859 if (!empty($objectsrc) && !empty($objectsrc->fk_project)) {
2860 $proj = new Project($db);
2861 $proj->fetch($objectsrc->fk_project);
2862 $morehtmlref .= $proj->getNomUrl(1);
2863 if ($proj->title) {
2864 $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
2865 }
2866 }
2867 }
2868 }
2869 } elseif (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
2870 // Project
2871 if (isModEnabled('project')) {
2872 $langs->load("projects");
2873 $morehtmlref .= '<br>';
2874 if ($permissiontoadd) {
2875 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
2876 if ($action != 'classify') {
2877 $morehtmlref .= '<a class="editfielda" href="' . $_SERVER['PHP_SELF'] . '?action=classify&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetProject')) . '</a> ';
2878 }
2879 $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, (string) $object->fk_project, ($action == 'classify' ? 'projectid' : 'none'), 0, 0, 0, 1, '', 'maxwidth300');
2880 } else {
2881 if (!empty($object->fk_project)) {
2882 $proj = new Project($db);
2883 $proj->fetch($object->fk_project);
2884 $morehtmlref .= $proj->getNomUrl(1);
2885 if ($proj->title) {
2886 $morehtmlref .= '<span class="opacitymedium"> - ' . dol_escape_htmltag($proj->title) . '</span>';
2887 }
2888 }
2889 }
2890 }
2891 }
2892
2893 $morehtmlref .= '</div>';
2894 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
2895
2896 print '<div class="fichecenter">';
2897 print '<div class="fichehalfleft">';
2898 print '<div class="underbanner clearboth"></div>';
2899
2900 print '<table class="border tableforfield centpercent">';
2901
2902 // Linked documents
2903 if (!empty($typeobject) && $typeobject == 'commande' && $object->origin_object->id && isModEnabled('order')) {
2904 print '<tr><td>';
2905 print $langs->trans("RefOrder") . '</td>';
2906 print '<td>';
2907 print $objectsrc->getNomUrl(1, 'commande');
2908 print "</td>\n";
2909 print '</tr>';
2910 }
2911 if (!empty($typeobject) && $typeobject == 'propal' && $object->origin_object->id && isModEnabled("propal")) {
2912 print '<tr><td>';
2913 print $langs->trans("RefProposal") . '</td>';
2914 print '<td>';
2915 print $objectsrc->getNomUrl(1, 'expedition');
2916 print "</td>\n";
2917 print '</tr>';
2918 }
2919
2920 // Date creation
2921 print '<tr><td class="titlefieldmiddle">' . $langs->trans("DateCreation") . '</td>';
2922 print '<td>' . dol_print_date($object->date_creation, "dayhour") . "</td>\n";
2923 print '</tr>';
2924
2925 // Delivery date planned
2926 print '<tr><td height="10">';
2927 print '<table class="nobordernopadding centpercent"><tr><td>';
2928 print $langs->trans('DateDeliveryPlanned');
2929 print '</td>';
2930 if ($action != 'editdate_livraison') {
2931 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editdate_livraison&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->trans('SetDeliveryDate'), 1) . '</a></td>';
2932 }
2933 print '</tr></table>';
2934 print '</td><td>';
2935 if ($action == 'editdate_livraison') {
2936 print '<form name="setdate_livraison" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
2937 print '<input type="hidden" name="token" value="' . newToken() . '">';
2938 print '<input type="hidden" name="action" value="setdate_livraison">';
2939 print $form->selectDate($object->date_delivery ? $object->date_delivery : -1, 'liv_', 1, 1, 0, "setdate_livraison", 1, 0);
2940 print '<input type="submit" class="button button-edit smallpaddingimp" value="' . $langs->trans('Modify') . '">';
2941 print '</form>';
2942 } else {
2943 print $object->date_delivery ? dol_print_date($object->date_delivery, 'dayhour') : '&nbsp;';
2944 }
2945 print '</td>';
2946 print '</tr>';
2947
2948 // Delivery sending date
2949 print '<tr><td height="10">';
2950 print '<table class="nobordernopadding centpercent"><tr><td>';
2951 print $langs->trans('DateShipping');
2952 print '</td>';
2953 if ($action != 'editdate_shipping') {
2954 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editdate_shipping&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->trans('SetShippingDate'), 1) . '</a></td>';
2955 }
2956 print '</tr></table>';
2957 print '</td><td>';
2958 if ($action == 'editdate_shipping') {
2959 print '<form name="setdate_shipping" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
2960 print '<input type="hidden" name="token" value="' . newToken() . '">';
2961 print '<input type="hidden" name="action" value="setdate_shipping">';
2962 print $form->selectDate($object->date_shipping ? $object->date_shipping : -1, 'ship_', 1, 1, 0, "setdate_shipping", 1, 0);
2963 print '<input type="submit" class="button button-edit smallpaddingimp" value="' . $langs->trans('Modify') . '">';
2964 print '</form>';
2965 } else {
2966 print $object->date_shipping ? dol_print_date($object->date_shipping, 'dayhour') : '&nbsp;';
2967 }
2968 print '</td>';
2969 print '</tr>';
2970
2971 // Weight
2972 print '<tr><td>';
2973 print $form->editfieldkey("Weight", 'trueWeight', $object->trueWeight, $object, $user->hasRight('expedition', 'creer'));
2974 print '</td><td>';
2975
2976 if ($action == 'edittrueWeight') {
2977 print '<form name="settrueweight" action="' . $_SERVER["PHP_SELF"] . '" method="post">';
2978 print '<input name="action" value="settrueWeight" type="hidden">';
2979 print '<input name="id" value="' . $object->id . '" type="hidden">';
2980 print '<input type="hidden" name="token" value="' . newToken() . '">';
2981 print '<input id="trueWeight" name="trueWeight" value="' . $object->trueWeight . '" type="text" class="width50 valignmiddle">';
2982 print $formproduct->selectMeasuringUnits("weight_units", "weight", (string) $object->weight_units, 0, 2, 'maxwidth125 valignmiddle');
2983 print ' <input class="button smallpaddingimp valignmiddle" name="modify" value="' . $langs->trans("Modify") . '" type="submit">';
2984 print ' <input class="button button-cancel smallpaddingimp valignmiddle" name="cancel" value="' . $langs->trans("Cancel") . '" type="submit">';
2985 print '</form>';
2986 } else {
2987 print $object->trueWeight;
2988 print ($object->trueWeight && $object->weight_units != '') ? ' ' . measuringUnitString(0, "weight", $object->weight_units) : '';
2989 }
2990
2991 // Calculated
2992 if ($totalWeight > 0) {
2993 if (!empty($object->trueWeight)) {
2994 print ' (' . $langs->trans("SumOfProductWeights") . ': ';
2995 }
2996 print showDimensionInBestUnit($totalWeight, 0, "weight", $langs, getDolGlobalInt('MAIN_WEIGHT_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_WEIGHT_DEFAULT_UNIT', 'no'));
2997 if (!empty($object->trueWeight)) {
2998 print ')';
2999 }
3000 }
3001 print '</td></tr>';
3002
3003 // Width
3004 print '<tr><td>' . $form->editfieldkey("Width", 'trueWidth', $object->trueWidth, $object, $user->hasRight('expedition', 'creer')) . '</td><td>';
3005 print $form->editfieldval("Width", 'trueWidth', $object->trueWidth, $object, $user->hasRight('expedition', 'creer'));
3006 print ($object->trueWidth && $object->width_units != '') ? ' ' . measuringUnitString(0, "size", $object->width_units) : '';
3007 print '</td></tr>';
3008
3009 // Height
3010 print '<tr><td>' . $form->editfieldkey("Height", 'trueHeight', $object->trueHeight, $object, $user->hasRight('expedition', 'creer')) . '</td><td>';
3011 if ($action == 'edittrueHeight') {
3012 print '<form name="settrueHeight" action="' . $_SERVER["PHP_SELF"] . '" method="post">';
3013 print '<input name="action" value="settrueHeight" type="hidden">';
3014 print '<input name="id" value="' . $object->id . '" type="hidden">';
3015 print '<input type="hidden" name="token" value="' . newToken() . '">';
3016 print '<input id="trueHeight" name="trueHeight" value="' . $object->trueHeight . '" type="text" class="width50">';
3017 print $formproduct->selectMeasuringUnits("size_units", "size", $object->size_units, 0, 2);
3018 print ' <input class="button smallpaddingimp" name="modify" value="' . $langs->trans("Modify") . '" type="submit">';
3019 print ' <input class="button button-cancel smallpaddingimp" name="cancel" value="' . $langs->trans("Cancel") . '" type="submit">';
3020 print '</form>';
3021 } else {
3022 print $object->trueHeight;
3023 print ($object->trueHeight && $object->height_units != '') ? ' ' . measuringUnitString(0, "size", $object->height_units) : '';
3024 }
3025
3026 print '</td></tr>';
3027
3028 // Depth
3029 print '<tr><td>' . $form->editfieldkey("Depth", 'trueDepth', $object->trueDepth, $object, $user->hasRight('expedition', 'creer')) . '</td><td>';
3030 print $form->editfieldval("Depth", 'trueDepth', $object->trueDepth, $object, $user->hasRight('expedition', 'creer'));
3031 print ($object->trueDepth && $object->depth_units != '') ? ' ' . measuringUnitString(0, "size", $object->depth_units) : '';
3032 print '</td></tr>';
3033
3034 // Volume
3035 print '<tr><td>';
3036 print $langs->trans("Volume");
3037 print '</td>';
3038 print '<td>';
3039 $calculatedVolume = 0;
3040 $volumeUnit = 0;
3041 if ($object->trueWidth && $object->trueHeight && $object->trueDepth) {
3042 $calculatedVolume = ($object->trueWidth * $object->trueHeight * $object->trueDepth);
3043 $volumeUnit = $object->size_units * 3;
3044 }
3045 // If sending volume not defined we use sum of products
3046 if ($calculatedVolume > 0) {
3047 if ($volumeUnit < 50) {
3048 print showDimensionInBestUnit($calculatedVolume, $volumeUnit, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'));
3049 } else {
3050 print $calculatedVolume . ' ' . measuringUnitString(0, "volume", $volumeUnit);
3051 }
3052 }
3053 if ($totalVolume > 0) {
3054 if ($calculatedVolume) {
3055 print ' (' . $langs->trans("SumOfProductVolumes") . ': ';
3056 }
3057 print showDimensionInBestUnit($totalVolume, 0, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'));
3058 //if (empty($calculatedVolume)) print ' ('.$langs->trans("Calculated").')';
3059 if ($calculatedVolume) {
3060 print ')';
3061 }
3062 }
3063 print "</td>\n";
3064 print '</tr>';
3065
3066 // Other attributes
3067 //$cols = 2;
3068 include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php';
3069
3070 print '</table>';
3071
3072 print '</div>';
3073 print '<div class="fichehalfright">';
3074 print '<div class="underbanner clearboth"></div>';
3075
3076 print '<table class="border centpercent tableforfield">';
3077
3078 // Sending method
3079 print '<tr><td>';
3080 print '<table class="nobordernopadding centpercent"><tr><td>';
3081 print $langs->trans('SendingMethod');
3082 print '</td>';
3083
3084 if ($action != 'editshipping_method_id' && $permissiontoadd) {
3085 print '<td class="right"><a class="editfielda" href="' . $_SERVER["PHP_SELF"] . '?action=editshipping_method_id&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->trans('SetSendingMethod'), 1) . '</a></td>';
3086 }
3087 print '</tr></table>';
3088 print '</td><td>';
3089 if ($action == 'editshipping_method_id') {
3090 print '<form name="setshipping_method_id" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
3091 print '<input type="hidden" name="token" value="' . newToken() . '">';
3092 print '<input type="hidden" name="action" value="setshipping_method_id">';
3093 $object->fetch_delivery_methods();
3094 print $form->selectarray("shipping_method_id", $object->meths, $object->shipping_method_id, 1, 0, 0, "", 1);
3095 if ($user->admin) {
3096 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3097 }
3098 print '<input type="submit" class="button button-edit smallpaddingimp" value="' . $langs->trans('Modify') . '">';
3099 print '</form>';
3100 } else {
3101 if ($object->shipping_method_id > 0) {
3102 // Get code using getLabelFromKey
3103 $code = $langs->getLabelFromKey($db, (string) $object->shipping_method_id, 'c_shipment_mode', 'rowid', 'code');
3104 print $langs->trans("SendingMethod" . strtoupper($code));
3105 }
3106 }
3107 print '</td>';
3108 print '</tr>';
3109
3110 // Tracking Number
3111 print '<tr><td class="titlefieldmiddle">' . $form->editfieldkey("TrackingNumber", 'tracking_number', $object->tracking_number, $object, $user->hasRight('expedition', 'creer')) . '</td><td>';
3112 print $form->editfieldval("TrackingNumber", 'tracking_number', $object->tracking_url, $object, $user->hasRight('expedition', 'creer'), 'safehtmlstring', $object->tracking_number);
3113 print '</td></tr>';
3114
3115 // Incoterms
3116 if (isModEnabled('incoterm')) {
3117 print '<tr><td>';
3118 print '<table class="nobordernopadding centpercent"><tr><td>';
3119 print $langs->trans('IncotermLabel');
3120 print '<td><td class="right">';
3121 if ($permissiontoadd) {
3122 print '<a class="editfielda" href="' . DOL_URL_ROOT . '/expedition/card.php?id=' . $object->id . '&action=editincoterm&token=' . newToken() . '">' . img_edit() . '</a>';
3123 } else {
3124 print '&nbsp;';
3125 }
3126 print '</td></tr></table>';
3127 print '</td>';
3128 print '<td>';
3129 if ($action != 'editincoterm') {
3130 print $form->textwithpicto($object->display_incoterms(), $object->label_incoterms, 1);
3131 } else {
3132 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''), $_SERVER['PHP_SELF'] . '?id=' . $object->id);
3133 }
3134 print '</td></tr>';
3135 }
3136
3137 // Other attributes
3138 $parameters = array();
3139 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3140 print $hookmanager->resPrint;
3141
3142 print "</table>";
3143
3144 print '</div>';
3145 print '</div>';
3146
3147 print '<div class="clearboth"></div>';
3148
3149 print dol_get_fiche_end();
3150
3151
3152 /*
3153 * Lines of simple shipment
3154 */
3155 if (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
3156 if (!empty($object->table_element_line)) {
3157 // Show object lines
3158 $result = $object->getLinesArray();
3159
3160 print ' <form name="addproduct" id="addproduct" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.(($action != 'editline') ? '' : '#line_'.GETPOSTINT('lineid')).'" method="POST">
3161 <input type="hidden" name="token" value="' . newToken().'">
3162 <input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline').'">
3163 <input type="hidden" name="mode" value="">
3164 <input type="hidden" name="page_y" value="">
3165 <input type="hidden" name="id" value="' . $object->id.'">
3166 ';
3167
3168 if (!empty($conf->use_javascript_ajax) && $object->status == 0) {
3169 include DOL_DOCUMENT_ROOT.'/core/tpl/ajaxrow.tpl.php';
3170 }
3171
3172 print '<div class="div-table-responsive-no-min">';
3173 if (!empty($object->lines) || ($object->status == $object::STATUS_DRAFT && $permissiontoadd && $action != 'selectlines' && $action != 'editline')) {
3174 print '<table id="tablelines" class="noborder noshadow" width="100%">';
3175 }
3176
3177 if (!empty($object->lines)) {
3178 $object->printObjectLines($action, $mysoc, null, GETPOSTINT('lineid'), 1, '/expedition/tpl');
3179 }
3180
3181 // Form to add new line
3182 if ($object->status == 0 && $permissiontoadd && $action != 'selectlines') {
3183 if ($action != 'editline') {
3184 // Add products/services form
3185
3186 $parameters = array();
3187 $reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3188 if ($reshook < 0) {
3189 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3190 }
3191 if (empty($reshook)) {
3192 $object->formAddObjectLine(1, $mysoc, $soc);
3193 }
3194 }
3195 }
3196
3197 if (!empty($object->lines) || ($object->status == $object::STATUS_DRAFT && $permissiontoadd && $action != 'selectlines' && $action != 'editline')) {
3198 print '</table>';
3199 }
3200 print '</div>';
3201
3202 print "</form>\n";
3203 }
3204 }
3205
3206 // Lines of products of origin
3207 if (!empty($object->origin) && $object->origin_id > 0) {
3208 if ($action == 'editline') {
3209 print ' <form name="updateline" id="updateline" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&amp;lineid=' . $line_id . '" method="POST">
3210 <input type="hidden" name="token" value="' . newToken() . '">
3211 <input type="hidden" name="action" value="updateline">
3212 <input type="hidden" name="mode" value="">
3213 <input type="hidden" name="id" value="' . $object->id . '">
3214 ';
3215 }
3216 print '<br>';
3217 if (!empty($conf->use_javascript_ajax) && $object->status == Expedition::STATUS_DRAFT && $usercancreate && $num_prod > 1) {
3218 include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php';
3219 }
3220
3221 print '<div class="div-table-responsive-no-min">';
3222 print '<table class="noborder centpercent" id="tablelines" >';
3223 print '<thead>';
3224 print '<tr class="liste_titre">';
3225 // Adds a line numbering column
3226 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
3227 print '<td width="5" class="center linecolnum">&nbsp;</td>';
3228 }
3229 // Product/Service
3230 print '<td class="linecoldescription" >' . $langs->trans("Products") . '</td>';
3231 // Qty
3232 print '<td class="center linecolqty">' . $langs->trans("QtyOrdered") . '</td>';
3233 if ($origin_id > 0) {
3234 print '<td class="center linecolqtyinothershipments">' . $langs->trans("QtyInOtherShipments") . '</td>';
3235 }
3236 if ($action == 'editline') {
3237 $editColspan = 3;
3238 if (!isModEnabled('stock')) {
3239 $editColspan--;
3240 }
3241 if (!isModEnabled('productbatch')) {
3242 $editColspan--;
3243 }
3244 print '<td class="center linecoleditlineotherinfo" colspan="' . $editColspan . '">';
3245 if ($object->status <= 1) {
3246 print $langs->trans("QtyToShip");
3247 } else {
3248 print $langs->trans("QtyShipped");
3249 }
3250 if (isModEnabled('stock')) {
3251 print ' - ' . $langs->trans("WarehouseSource");
3252 }
3253 if (isModEnabled('productbatch')) {
3254 print ' - ' . $langs->trans("Batch");
3255 }
3256 print '</td>';
3257 } else {
3258 if ($object->status <= 1) {
3259 print '<td class="center linecolqtytoship">' . $langs->trans("QtyToShip") . '</td>';
3260 } else {
3261 print '<td class="center linecolqtyshipped">' . $langs->trans("QtyShipped") . '</td>';
3262 }
3263 if (isModEnabled('stock')) {
3264 print '<td class="left linecolwarehousesource">' . $langs->trans("WarehouseSource") . '</td>';
3265 }
3266
3267 if (isModEnabled('productbatch')) {
3268 print '<td class="left linecolbatch">' . $langs->trans("Batch") . '</td>';
3269 }
3270 }
3271 print '<td class="center linecolweight">' . $langs->trans("CalculatedWeight") . '</td>';
3272 print '<td class="center linecolvolume">' . $langs->trans("CalculatedVolume") . '</td>';
3273 //print '<td class="center">'.$langs->trans("Size").'</td>';
3274 if ($object->status == 0) {
3275 print '<td class="linecoledit"></td>';
3276 print '<td class="linecoldelete" width="10"></td>';
3277 print '<td class="linecolmove"></td>';
3278 }
3279 print "</tr>\n";
3280 print '</thead>';
3281
3282 $outputlangs = $langs;
3283
3284 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
3285 $object->fetch_thirdparty();
3286 $newlang = '';
3287 if (/* empty($newlang) && */GETPOST('lang_id', 'aZ09')) {
3288 $newlang = GETPOST('lang_id', 'aZ09');
3289 }
3290 if (empty($newlang)) {
3291 $newlang = $object->thirdparty->default_lang;
3292 }
3293 if (!empty($newlang)) {
3294 $outputlangs = new Translate("", $conf);
3295 $outputlangs->setDefaultLang($newlang);
3296 }
3297 }
3298
3299 $origin = (string) $origin;
3300 if (empty($origin) || $origin == 'order') {
3301 $origin = 'commande';
3302 }
3303
3304 // List of allowed value of $origin
3305 if (!in_array($origin, array('supplier_proposal', 'supplier_order', 'commande_fournisseur', 'facture_fourn', 'propal', 'commande', 'facture'))) {
3306 dol_print_error($db, 'Bad value for parameter origin in expedition/card.php');
3307 exit;
3308 }
3309
3310 // Get list of products already sent for same source object into $alreadysent
3311 $alreadysent = array();
3312 if ($origin_id > 0) {
3313 $tablenametouse = (($origin == 'supplier_order') ? 'commande_fournisseur' : (($origin == 'facture_fourn') ? 'facture_fourn_' : $origin));
3314
3315 $sql = "SELECT obj.rowid, obj.fk_product, obj.label, obj.description, obj.product_type as fk_product_type, obj.qty as qty_asked, obj.fk_unit, obj.date_start, obj.date_end, obj.special_code";
3316 $sql .= ", ed.rowid as shipmentline_id, ed.qty as qty_shipped, ed.fk_expedition as expedition_id, ed.fk_elementdet, ed.fk_entrepot";
3317 $sql .= ", e.rowid as shipment_id, e.ref as shipment_ref, e.date_creation, e.date_valid, e.date_delivery, e.date_expedition";
3318 $sql .= ', p.label as product_label, p.ref, p.fk_product_type, p.rowid as prodid, p.tosell as product_tosell, p.tobuy as product_tobuy, p.tobatch as product_tobatch';
3319 $sql .= ', p.description as product_desc';
3320 $sql .= " FROM " . MAIN_DB_PREFIX . "expeditiondet as ed";
3321 $sql .= ", " . MAIN_DB_PREFIX . "expedition as e";
3322 $sql .= ", " . MAIN_DB_PREFIX . $db->sanitize($tablenametouse) . "det as obj";
3323 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "product as p ON obj.fk_product = p.rowid";
3324 $sql .= " WHERE e.entity IN (" . getEntity('expedition') . ")";
3325 $sql .= " AND obj.fk_" . $db->sanitize((string) $origin) . " = " . ((int) $origin_id);
3326 $sql .= " AND obj.rowid = ed.fk_elementdet";
3327 $sql .= " AND ed.fk_expedition = e.rowid";
3328 //if ($filter) $sql.= $filter;
3329 $sql .= " ORDER BY obj.fk_product";
3330
3331 dol_syslog("expedition/card.php get list of shipment lines", LOG_DEBUG);
3332 $resql = $db->query($sql);
3333 if ($resql) {
3334 $num = $db->num_rows($resql);
3335 $i = 0;
3336
3337 while ($i < $num) {
3338 $obj = $db->fetch_object($resql);
3339 if ($obj) {
3340 // $obj->rowid is rowid in $origin."det" table
3341 $alreadysent[$obj->rowid][$obj->shipmentline_id] = array(
3342 'shipment_ref' => $obj->shipment_ref,
3343 'shipment_id' => $obj->shipment_id,
3344 'warehouse' => $obj->fk_entrepot,
3345 'qty_shipped' => $obj->qty_shipped,
3346 'product_tosell' => $obj->product_tosell,
3347 'product_tobuy' => $obj->product_tobuy,
3348 'product_tobatch' => $obj->product_tobatch,
3349 'date_valid' => $db->jdate($obj->date_valid),
3350 'date_delivery' => $db->jdate($obj->date_delivery));
3351 }
3352 $i++;
3353 }
3354 }
3355 //var_dump($alreadysent);
3356 }
3357
3358 print '<tbody>';
3359
3360 // Loop on each product to send/sent
3361 $conf->cache['product'] = array();
3362 $conf->cache['warehouse'] = array();
3363 for ($i = 0; $i < $num_prod; $i++) {
3364 $parameters = array('i' => $i, 'line' => $lines[$i], 'line_id' => $line_id, 'num' => $num_prod, 'alreadysent' => $alreadysent, 'editColspan' => !empty($editColspan) ? $editColspan : 0, 'outputlangs' => $outputlangs);
3365 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $object, $action);
3366 if ($reshook < 0) {
3367 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3368 }
3369
3370 if (empty($reshook) && $lines[$i]->product_type != "9") {
3371 print '<!-- origin line id = ' . $lines[$i]->origin_line_id . ' -->'; // id of order line
3372 print '<tr class="oddeven" id="row-' . $lines[$i]->id . '" data-id="' . $lines[$i]->id . '" data-element="' . $lines[$i]->element . '" >';
3373 $line = $lines[$i];
3374 $line->fetch_optionals();
3375
3376 // #
3377 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
3378 print '<td class="center linecolnum">' . ($i + 1) . '</td>';
3379 }
3380
3381 // Predefined product or service
3382 if ($lines[$i]->fk_product > 0) {
3383 // Define output language
3384 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
3385 $product_id = $lines[$i]->fk_product;
3386 if (!isset($conf->cache['product'][$product_id])) {
3387 $prod = new Product($db);
3388 $prod->fetch($product_id);
3389 $conf->cache['product'][$product_id] = $prod;
3390 } else {
3391 $prod = $conf->cache['product'][$product_id];
3392 }
3393 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $lines[$i]->product_label;
3394 } else {
3395 $label = (!empty($lines[$i]->label) ? $lines[$i]->label : $lines[$i]->product_label);
3396 }
3397
3398 print '<td class="linecoldescription">';
3399
3400 // Show product and description
3401 $product_static->type = $lines[$i]->fk_product_type;
3402 $product_static->id = $lines[$i]->fk_product;
3403 $product_static->ref = $lines[$i]->ref;
3404 $product_static->status = $lines[$i]->product_tosell;
3405 $product_static->status_buy = $lines[$i]->product_tobuy;
3406 $product_static->status_batch = $lines[$i]->product_tobatch;
3407
3408 $product_static->weight = $lines[$i]->weight;
3409 $product_static->weight_units = $lines[$i]->weight_units;
3410 $product_static->length = $lines[$i]->length;
3411 $product_static->length_units = $lines[$i]->length_units;
3412 $product_static->width = !empty($lines[$i]->width) ? $lines[$i]->width : 0;
3413 $product_static->width_units = !empty($lines[$i]->width_units) ? $lines[$i]->width_units : 0;
3414 $product_static->height = !empty($lines[$i]->height) ? $lines[$i]->height : 0;
3415 $product_static->height_units = !empty($lines[$i]->height_units) ? $lines[$i]->height_units : 0;
3416 $product_static->surface = $lines[$i]->surface;
3417 $product_static->surface_units = $lines[$i]->surface_units;
3418 $product_static->volume = $lines[$i]->volume;
3419 $product_static->volume_units = $lines[$i]->volume_units;
3420 $product_static->stockable_product = $lines[$i]->stockable_product;
3421
3422 $text = $product_static->getNomUrl(1);
3423 $text .= ' - ' . $label;
3424 $description = (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE') ? '' : dol_htmlentitiesbr($lines[$i]->description));
3425 print $form->textwithtooltip($text, $description, 3, 0, '', (string) $i);
3426 print_date_range(!empty($lines[$i]->date_start) ? $lines[$i]->date_start : '', !empty($lines[$i]->date_end) ? $lines[$i]->date_end : '');
3427 if (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE')) {
3428 print (!empty($lines[$i]->description) && $lines[$i]->description != $lines[$i]->product) ? '<br>' . dol_htmlentitiesbr($lines[$i]->description) : '';
3429 }
3430 $lineExtrafieldsHtml = $line->showOptionals($extrafields, ($action == 'editline' && $line->id == $line_id) ? 'edit' : 'view', array(), '', '', '1', 'line');
3431 if (!empty($lineExtrafieldsHtml)) {
3432 print '<div style="padding-top: 10px" id="extrafield_lines_area_' . $line->id . '" name="extrafield_lines_area_' . $line->id . '">';
3433 print $lineExtrafieldsHtml;
3434 print '</div>';
3435 }
3436 print "</td>\n";
3437 } else {
3438 print '<td class="linecoldescription" >';
3439 if ($lines[$i]->product_type == Product::TYPE_SERVICE) {
3440 $text = img_object($langs->trans('Service'), 'service');
3441 } else {
3442 $text = img_object($langs->trans('Product'), 'product');
3443 }
3444
3445 if (!empty($lines[$i]->label)) {
3446 $text .= ' <strong>' . $lines[$i]->label . '</strong>';
3447 print $form->textwithtooltip($text, $lines[$i]->description, 3, 0, '', (string) $i);
3448 } else {
3449 print $text . ' ' . nl2br($lines[$i]->description);
3450 }
3451
3452 print_date_range($lines[$i]->date_start, $lines[$i]->date_end);
3453 $lineExtrafieldsHtml = $line->showOptionals($extrafields, ($action == 'editline' && $line->id == $line_id) ? 'edit' : 'view', array(), '', '', '1', 'line');
3454 if (!empty($lineExtrafieldsHtml)) {
3455 print '<div style="padding-top: 10px" id="extrafield_lines_area_' . $line->id . '" name="extrafield_lines_area_' . $line->id . '">';
3456 print $lineExtrafieldsHtml;
3457 print '</div>';
3458 }
3459 print "</td>\n";
3460 }
3461
3462 $unit_order = '';
3463 if (getDolGlobalString('PRODUCT_USE_UNITS')) {
3464 $unit_order = measuringUnitString((int) $lines[$i]->fk_unit);
3465 }
3466
3467 // Qty ordered
3468 print '<td class="center linecolqty">' . $lines[$i]->qty_asked . ' ' . $unit_order . '</td>';
3469
3470 // Qty in other shipments (with shipment and warehouse used)
3471 if ($origin_id > 0) {
3472 print '<td class="linecolqtyinothershipments center nowrap">';
3473 $htmltooltip = '';
3474 $qtyalreadysent = 0;
3475 foreach ($alreadysent as $key => $val) {
3476 if ($lines[$i]->fk_elementdet == $key) {
3477 $j = 0;
3478 foreach ($val as $shipmentline_id => $shipmentline_var) {
3479 if ($shipmentline_var['shipment_id'] == $lines[$i]->fk_expedition) {
3480 continue; // We want to show only "other shipments"
3481 }
3482
3483 $j++;
3484 if ($j > 1) {
3485 $htmltooltip .= '<br>';
3486 }
3487 $shipment_static->fetch($shipmentline_var['shipment_id']);
3488 $htmltooltip .= $shipment_static->getNomUrl(1, '', 0, 0, 1);
3489 $htmltooltip .= ' - ' . $shipmentline_var['qty_shipped'];
3490 $htmltooltip .= ' - ' . $langs->trans("DateValidation") . ' : ' . (empty($shipmentline_var['date_valid']) ? $langs->trans("Draft") : dol_print_date($shipmentline_var['date_valid'], 'dayhour'));
3491 /*if (isModEnabled('stock') && $shipmentline_var['warehouse'] > 0) {
3492 $warehousestatic->fetch($shipmentline_var['warehouse']);
3493 $htmltext .= '<br>'.$langs->trans("FromLocation").' : '.$warehousestatic->getNomUrl(1, '', 0, 1);
3494 }*/
3495 //print ' '.$form->textwithpicto('', $htmltext, 1);
3496
3497 $qtyalreadysent += $shipmentline_var['qty_shipped'];
3498 }
3499 if ($j) {
3500 $htmltooltip = $langs->trans("QtyInOtherShipments") . '...<br><br>' . $htmltooltip . '<br><input type="submit" name="dummyhiddenbuttontogetfocus" style="display:none" autofocus>';
3501 }
3502 }
3503 }
3504 print $form->textwithpicto((string) $qtyalreadysent, $htmltooltip, 1, 'info', '', 0, 3, 'tooltip' . $lines[$i]->id);
3505 print '</td>';
3506 }
3507
3508 if ($action == 'editline' && $lines[$i]->id == $line_id) {
3509 // edit mode
3510 print '<td colspan="' . $editColspan . '" class="center"><table class="nobordernopadding centpercent">';
3511 if (is_array($lines[$i]->detail_batch) && count($lines[$i]->detail_batch) > 0) {
3512 print '<!-- case edit 1 -->';
3513 $line = new ExpeditionLigne($db);
3514 foreach ($lines[$i]->detail_batch as $detail_batch) {
3515 print '<tr>';
3516 // Qty to ship or shipped
3517 print '<td><input class="qtyl right" name="qtyl' . $detail_batch->fk_expeditiondet . '_' . $detail_batch->id . '" id="qtyl' . $line_id . '_' . $detail_batch->id . '" type="text" size="4" value="' . $detail_batch->qty . '"></td>';
3518 // Batch number management
3519 if ($lines[$i]->entrepot_id == 0) {
3520 // only show lot numbers from src warehouse when shipping from multiple warehouses
3521 $line->fetch($detail_batch->fk_expeditiondet);
3522 }
3523 $entrepot_id = !empty($detail_batch->entrepot_id) ? $detail_batch->entrepot_id : $lines[$i]->entrepot_id;
3524 print '<td>' . $formproduct->selectLotStock($detail_batch->fk_origin_stock, 'batchl' . $detail_batch->fk_expeditiondet . '_' . $detail_batch->fk_origin_stock, '', 1, 0, $lines[$i]->fk_product, $entrepot_id) . '</td>';
3525 print '</tr>';
3526 }
3527 // add a 0 qty lot row to be able to add a lot
3528 print '<tr>';
3529 // Qty to ship or shipped
3530 print '<td><input class="qtyl" name="qtyl' . $line_id . '_0" id="qtyl' . $line_id . '_0" type="text" size="4" value="0"></td>';
3531 // Batch number management
3532 print '<td>' . $formproduct->selectLotStock('', 'batchl' . $line_id . '_0', '', 1, 0, $lines[$i]->fk_product) . '</td>';
3533 print '</tr>';
3534 } elseif (isModEnabled('stock')) {
3535 if ($lines[$i]->fk_product > 0) {
3536 if ($lines[$i]->entrepot_id > 0) {
3537 print '<!-- case edit 2 -->';
3538 print '<tr>';
3539 // Qty to ship or shipped
3540 print '<td><input class="qtyl right" name="qtyl' . $line_id . '" id="qtyl' . $line_id . '" type="text" size="4" value="' . $lines[$i]->qty_shipped . '">' . $unit_order . '</td>';
3541 // Warehouse source
3542 print '<td>' . $formproduct->selectWarehouses($lines[$i]->entrepot_id, 'entl' . $line_id, '', 1, 0, $lines[$i]->fk_product, '', 1, 0, array(), 'minwidth200') . '</td>';
3543 // Batch number management
3544 print '<td>';
3545 if (isModEnabled('productbatch')) {
3546 print ' - ' . $langs->trans("NA");
3547 }
3548 print '</td>';
3549 print '</tr>';
3550 } elseif (count($lines[$i]->details_entrepot) > 1) {
3551 print '<!-- case edit 3 -->';
3552 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
3553 print '<tr>';
3554 // Qty to ship or shipped
3555 print '<td><input class="qtyl right" name="qtyl' . $detail_entrepot->line_id . '" id="qtyl' . $detail_entrepot->line_id . '" type="text" size="4" value="' . $detail_entrepot->qty_shipped . '">' . $unit_order . '</td>';
3556 // Warehouse source
3557 print '<td>' . $formproduct->selectWarehouses($detail_entrepot->entrepot_id, 'entl' . $detail_entrepot->line_id, '', 1, 0, $lines[$i]->fk_product, '', 1, 0, array(), 'minwidth200') . '</td>';
3558 // Batch number management
3559 print '<td>';
3560 if (isModEnabled('productbatch')) {
3561 print ' - ' . $langs->trans("NA");
3562 }
3563 print '</td>';
3564 print '</tr>';
3565 }
3566 } elseif ($lines[$i]->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
3567 print '<!-- case edit 4 -->';
3568 print '<tr>';
3569 // Qty to ship or shipped
3570 print '<td><input class="qtyl right" name="qtyl' . $line_id . '" id="qtyl' . $line_id . '" type="text" size="4" value="' . $lines[$i]->qty_shipped . '"></td>';
3571 print '<td><span class="opacitymedium">(' . $langs->trans("Service") . ')</span></td>';
3572 print '<td></td>';
3573 print '</tr>';
3574 } else {
3575 print '<!-- case edit 5 -->';
3576 print '<tr><td colspan="3">' . $langs->trans("ErrorStockIsNotEnough") . '</td></tr>';
3577 }
3578 } else {
3579 print '<!-- case edit 6 -->';
3580 print '<tr>';
3581 // Qty to ship or shipped
3582 print '<td><input class="qtyl right" name="qtyl' . $line_id . '" id="qtyl' . $line_id . '" type="text" size="4" value="' . $lines[$i]->qty_shipped . '">' . $unit_order . '</td>';
3583 // Warehouse source
3584 print '<td></td>';
3585 // Batch number management
3586 print '<td></td>';
3587 print '</tr>';
3588 }
3589 } else { // both product batch and stock are not activated.
3590 print '<!-- case edit 7 -->';
3591 print '<tr>';
3592 // Qty to ship or shipped
3593 print '<td><input class="qtyl right" name="qtyl' . $line_id . '" id="qtyl' . $line_id . '" type="text" size="4" value="' . $lines[$i]->qty_shipped . '"></td>';
3594 // Warehouse source
3595 print '<td></td>';
3596 // Batch number management
3597 print '<td></td>';
3598 print '</tr>';
3599 }
3600
3601 print '</table></td>';
3602 } else {
3603 // Qty to ship or shipped
3604 print '<td class="linecolqtytoship center">' . $lines[$i]->qty_shipped . ' ' . $unit_order . '</td>';
3605
3606 // Warehouse source
3607 if (isModEnabled('stock')) {
3608 print '<td class="linecolwarehousesource tdoverflowmax200">';
3609 if ($lines[$i]->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
3610 print '<span class="opacitymedium">(' . $langs->trans("Service") . ')</span>';
3611 } elseif ($lines[$i]->entrepot_id > 0 && $lines[$i]->stockable_product == Product::ENABLED_STOCK) {
3612 $warehouse_id = $lines[$i]->entrepot_id;
3613 if (!isset($conf->cache['warehouse'][$warehouse_id])) {
3614 $warehouse = new Entrepot($db);
3615 $warehouse->fetch($warehouse_id);
3616 $conf->cache['warehouse'][$warehouse_id] = $warehouse;
3617 } else {
3618 $warehouse = $conf->cache['warehouse'][$warehouse_id];
3619 }
3620 print $warehouse->getNomUrl(1);
3621 } elseif (count($lines[$i]->details_entrepot) > 1) {
3622 $detail = '';
3623 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
3624 $warehouse_id = $detail_entrepot->entrepot_id;
3625 if ($warehouse_id > 0) {
3626 if (!isset($conf->cache['warehouse'][$warehouse_id])) {
3627 $warehouse = new Entrepot($db);
3628 $warehouse->fetch($warehouse_id);
3629 $conf->cache['warehouse'][$warehouse_id] = $warehouse;
3630 } else {
3631 $warehouse = $conf->cache['warehouse'][$warehouse_id];
3632 }
3633 $detail .= $langs->trans("DetailWarehouseFormat", $warehouse->label, $detail_entrepot->qty_shipped) . '<br>';
3634 }
3635 }
3636 print $form->textwithtooltip(img_picto('', 'object_stock') . ' ' . $langs->trans("DetailWarehouseNumber"), $detail);
3637 } elseif (count($lines[$i]->detail_children ?? []) > 1) {
3638 $detail = '';
3639 foreach ($lines[$i]->detail_children as $child_product_id => $child_stock_list) {
3640 foreach ($child_stock_list as $warehouse_id => $total_qty) {
3641 // get product from cache
3642 $child_product_label = '';
3643 if (!isset($conf->cache['product'][$child_product_id])) {
3644 $child_product = new Product($db);
3645 $child_product->fetch($child_product_id);
3646 $conf->cache['product'][$child_product_id] = $child_product;
3647 } else {
3648 $child_product = $conf->cache['product'][$child_product_id];
3649 }
3650 $child_product_label = $child_product->ref . ' ' . $child_product->label;
3651
3652 // get warehouse from cache
3653 if (!isset($conf->cache['warehouse'][$warehouse_id])) {
3654 $child_warehouse = new Entrepot($db);
3655 $child_warehouse->fetch($warehouse_id);
3656 $conf->cache['warehouse'][$warehouse_id] = $child_warehouse;
3657 } else {
3658 $child_warehouse = $conf->cache['warehouse'][$warehouse_id];
3659 }
3660 $detail .= $langs->trans('DetailChildrenFormat', $child_product_label, $child_warehouse->label, price2num($total_qty, 'MS')) . '<br>';
3661 }
3662 }
3663 print $form->textwithtooltip(img_picto('', 'object_stock') . ' ' . $langs->trans('DetailWarehouseNumber'), $detail);
3664 }
3665 print '</td>';
3666 }
3667
3668 // Batch number management
3669 if (isModEnabled('productbatch')) {
3670 if (isset($lines[$i]->detail_batch)) {
3671 print '<!-- Detail of lot -->';
3672 print '<td class="linecolbatch">';
3673 if ($lines[$i]->product_tobatch) {
3674 $detail = '';
3675 foreach ($lines[$i]->detail_batch as $dbatch) { // $dbatch is instance of ExpeditionLineBatch
3676 $detail .= $langs->trans("Batch") . ': ' . $dbatch->batch;
3677 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY')) {
3678 $detail .= ' - ' . $langs->trans("SellByDate") . ': ' . dol_print_date($dbatch->sellby, "day");
3679 }
3680 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY')) {
3681 $detail .= ' - ' . $langs->trans("EatByDate") . ': ' . dol_print_date($dbatch->eatby, "day");
3682 }
3683 $detail .= ' - ' . $langs->trans("Qty") . ': ' . $dbatch->qty;
3684 $detail .= '<br>';
3685 }
3686 print $form->textwithtooltip(img_picto('', 'object_barcode') . ' ' . $langs->trans("DetailBatchNumber"), $detail);
3687 } else {
3688 print $langs->trans("NA");
3689 }
3690 print '</td>';
3691 } else {
3692 print '<td class="linecolbatch" ></td>';
3693 }
3694 }
3695 }
3696
3697 // Weight
3698 print '<td class="center linecolweight">';
3699 if ($lines[$i]->fk_product_type == Product::TYPE_PRODUCT) {
3700 print $lines[$i]->weight * $lines[$i]->qty_shipped . ' ' . measuringUnitString(0, "weight", $lines[$i]->weight_units);
3701 } else {
3702 print '&nbsp;';
3703 }
3704 print '</td>';
3705
3706 // Volume
3707 print '<td class="center linecolvolume">';
3708 if ($lines[$i]->fk_product_type == Product::TYPE_PRODUCT) {
3709 print $lines[$i]->volume * $lines[$i]->qty_shipped . ' ' . measuringUnitString(0, "volume", $lines[$i]->volume_units);
3710 } else {
3711 print '&nbsp;';
3712 }
3713 print '</td>';
3714
3715 // Size
3716 //print '<td class="center">'.$lines[$i]->volume*$lines[$i]->qty_shipped.' '.measuringUnitString(0, "volume", $lines[$i]->volume_units).'</td>';
3717
3718 $showmovecol = ($object->status == Expedition::STATUS_DRAFT);
3719
3720 if ($action == 'editline' && $lines[$i]->id == $line_id) {
3721 print '<td class="center" colspan="2" valign="middle">';
3722 print '<input type="submit" class="button button-save" id="savelinebutton marginbottomonly" name="save" value="' . $langs->trans("Save") . '"><br>';
3723 print '<input type="submit" class="button button-cancel" id="cancellinebutton" name="cancel" value="' . $langs->trans("Cancel") . '"><br>';
3724 print '</td>';
3725 if ($showmovecol) {
3726 print '<td class="linecolmove tdlineupdown center"></td>';
3727 }
3728 } elseif ($showmovecol) {
3729 $edit_url = $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=editline&token=' . newToken() . '&lineid=' . $lines[$i]->id;
3730 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
3731 $product_id = $lines[$i]->fk_product;
3732 if (!isset($conf->cache['product'][$product_id])) {
3733 $product = new Product($db);
3734 $product->fetch($product_id);
3735 $conf->cache['product'][$product_id] = $product;
3736 } else {
3737 $product = $conf->cache['product'][$product_id];
3738 }
3739
3740 if ($product->hasFatherOrChild(1)) {
3741 $edit_url = dol_buildpath('/expedition/dispatch.php?id=' . $object->id, 1);
3742 }
3743 }
3744
3745 // edit-delete buttons
3746 print '<td class="linecoledit center">';
3747 print '<a class="editfielda reposition" href="' . $edit_url . '">' . img_edit() . '</a>';
3748 print '</td>';
3749 print '<td class="linecoldelete" width="10">';
3750 print '<a class="reposition" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=deleteline&token=' . newToken() . '&lineid=' . $lines[$i]->id . '">' . img_delete() . '</a>';
3751 print '</td>';
3752 print '<td class="linecolmove tdlineupdown center">';
3753 if ($usercancreate && $num_prod > 1) {
3754 if ($i > 0) {
3755 print '<a class="lineupdown" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=up&token=' . newToken() . '&rowid=' . $lines[$i]->id . '">' . img_up('default', 0, 'imgupforline') . '</a>';
3756 }
3757 if ($i < $num_prod - 1) {
3758 print '<a class="lineupdown" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=down&token=' . newToken() . '&rowid=' . $lines[$i]->id . '">' . img_down('default', 0, 'imgdownforline') . '</a>';
3759 }
3760 }
3761 print '</td>';
3762 // Display lines extrafields
3763 if (isset($rowExtrafieldsStart, $rowExtrafieldsView, $rowEnd)) { // @phan-suppress-current-line PhanPluginUndeclaredVariableIsset
3764 print $rowExtrafieldsStart;
3765 print $rowExtrafieldsView;
3766 print $rowEnd;
3767 }
3768 }
3769 print "</tr>";
3770 } elseif (empty($reshook) && $lines[$i]->product_type == "9") {
3771 $objectsrc = new OrderLine($db);
3772 $objectsrc->fetch($lines[$i]->origin_line_id);
3773 if ($objectsrc->special_code == SUBTOTALS_SPECIAL_CODE) {
3774 $line = $lines[$i];
3775 require dol_buildpath('/core/tpl/subtotal_expedition_view.tpl.php');
3776 }
3777 }
3778 }
3779 // TODO Show also lines ordered but not delivered
3780 if (empty($num_prod)) {
3781 print '<tr><td colspan="8"><span class="opacitymedium">' . $langs->trans("NoLineGoOnTabToAddSome", $langs->transnoentitiesnoconv("ShipmentDistribution")) . '</span></td></tr>';
3782 }
3783
3784 print '</tbody>';
3785 print "</table>\n";
3786 print '</div>';
3787 if ($action == 'editline') {
3788 print "</form>\n";
3789 }
3790
3791 $object->fetchObjectLinked($object->id, $object->element);
3792 }
3793
3794 /*
3795 * Boutons actions
3796 */
3797
3798 if (($user->socid == 0) && ($action != 'presend') && ($action != 'editline')) {
3799 print '<div class="tabsAction">';
3800
3801 $parameters = array();
3802 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
3803 // modified by hook
3804 if (empty($reshook)) {
3805 if ($object->status == Expedition::STATUS_DRAFT && $num_prod > 0) {
3806 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'creer'))
3807 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'shipping_advance', 'validate'))
3808 ) {
3809 print dolGetButtonAction('', $langs->trans('Validate'), 'default', $_SERVER["PHP_SELF"] . '?action=valid&token=' . newToken() . '&id=' . $object->id, '');
3810 } else {
3811 print dolGetButtonAction($langs->trans('NotAllowed'), $langs->trans('Validate'), 'default', $_SERVER['PHP_SELF'] . '#', '', false);
3812 }
3813 }
3814
3815 // 0=draft, 1=validated/delivered, 2=closed/delivered
3816 if ($object->status == Expedition::STATUS_VALIDATED && !getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) {
3817 if ($user->hasRight('expedition', 'creer')) {
3818 print dolGetButtonAction('', $langs->trans('SetToDraft'), 'default', $_SERVER["PHP_SELF"] . '?action=setdraft&token=' . newToken() . '&id=' . $object->id, '');
3819 }
3820 }
3821 if ($object->status == Expedition::STATUS_CLOSED) {
3822 if ($user->hasRight('expedition', 'creer')) {
3823 print dolGetButtonAction('', $langs->trans('ReOpen'), 'default', $_SERVER["PHP_SELF"] . '?action=reopen&token=' . newToken() . '&id=' . $object->id, '');
3824 }
3825 }
3826
3827 // Send
3828 if (empty($user->socid)) {
3829 if ($object->status > 0) {
3830 if (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') || $user->hasRight('expedition', 'shipping_advance', 'send')) {
3831 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER["PHP_SELF"] . '?action=presend&token=' . newToken() . '&id=' . $object->id . '&mode=init#formmailbeforetitle', '');
3832 } else {
3833 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER['PHP_SELF'] . '#', '', false);
3834 }
3835 }
3836 }
3837
3838 // This is just to generate a delivery receipt when option to do this is on
3839 //var_dump($object->linkedObjectsIds['delivery']);
3840 if (getDolGlobalInt('MAIN_SUBMODULE_DELIVERY') && ($object->status == Expedition::STATUS_VALIDATED || $object->status == Expedition::STATUS_CLOSED) && $user->hasRight('expedition', 'delivery', 'creer') && empty($object->linkedObjectsIds['delivery'])) {
3841 print dolGetButtonAction('', $langs->trans('CreateDeliveryOrder'), 'default', $_SERVER["PHP_SELF"] . '?action=create_delivery&token=' . newToken() . '&id=' . $object->id, '');
3842 }
3843
3844 // Sign (to set to status "Signed" without using the online signature page)
3845 if ($object->status > Expedition::STATUS_DRAFT) {
3846 if ($object->signed_status != Expedition::$SIGNED_STATUSES['STATUS_SIGNED_ALL']) {
3847 print '<div class="inline-block divButAction"><a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=sign&token=' . newToken() . '">' . $langs->trans("SignShipping") . '</a></div>';
3848 } else {
3849 print '<div class="inline-block divButAction"><a class="butAction" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=unsign&token=' . newToken() . '">' . $langs->trans("UnsignShipping") . '</a></div>';
3850 }
3851 }
3852
3853 // Create bill
3854 if (isModEnabled('invoice') && ($object->status == Expedition::STATUS_VALIDATED || $object->status == Expedition::STATUS_CLOSED)) {
3855 if ($user->hasRight('facture', 'creer')) {
3856 if (getDolGlobalString('WORKFLOW_BILL_ON_SHIPMENT') !== '0') {
3857 print dolGetButtonAction('', $langs->trans('CreateBill'), 'default', DOL_URL_ROOT . '/compta/facture/card.php?action=create&origin=' . $object->element . '&originid=' . $object->id . '&socid=' . $object->socid, '');
3858 }
3859 }
3860 }
3861
3862 // Set Billed and Closed
3863 if ($object->status == Expedition::STATUS_VALIDATED) {
3864 if ($user->hasRight('expedition', 'creer') && $object->status > 0) {
3865 if (!$object->billed && getDolGlobalString('WORKFLOW_BILL_ON_SHIPMENT') !== '0') {
3866 print dolGetButtonAction('', $langs->trans('ClassifyBilled'), 'default', $_SERVER["PHP_SELF"] . '?action=classifybilled&token=' . newToken() . '&id=' . $object->id, '');
3867 }
3868 print dolGetButtonAction('', $langs->trans("Close"), 'default', $_SERVER["PHP_SELF"] . '?action=classifyclosed&token=' . newToken() . '&id=' . $object->id, '');
3869 }
3870 }
3871
3872 // Cancel
3873 if ($object->status == Expedition::STATUS_VALIDATED) {
3874 if ($user->hasRight('expedition', 'creer')) {
3875 print dolGetButtonAction('', $langs->trans('Cancel'), 'default', $_SERVER["PHP_SELF"] . '?action=cancel&token=' . newToken() . '&id=' . $object->id . '&mode=init#formmailbeforetitle', '', true, array('attr' => array('classOverride' => 'butAction butActionDanger')))."\n";
3876 }
3877 }
3878
3879 // Delete
3880 if ($user->hasRight('expedition', 'supprimer')) {
3881 print dolGetButtonAction($langs->trans('Delete'), $langs->trans('Delete'), 'delete', $_SERVER["PHP_SELF"] . '?action=delete&token=' . newToken() . '&id=' . $object->id, '', true, array('attr' => array('class' => 'reposition')))."\n";
3882 }
3883 }
3884
3885 print '</div>';
3886 }
3887
3888
3889 /*
3890 * Documents generated
3891 */
3892
3893 if ($action != 'presend' && $action != 'editline') {
3894 print '<div class="fichecenter"><div class="fichehalfleft">';
3895
3896 $objectref = dol_sanitizeFileName((string) $object->ref);
3897 $filedir = $conf->expedition->dir_output . "/sending/" . $objectref;
3898
3899 $urlsource = $_SERVER["PHP_SELF"] . "?id=" . $object->id;
3900
3901 $genallowed = $user->hasRight('expedition', 'lire');
3902 $delallowed = $user->hasRight('expedition', 'creer');
3903
3904 print $formfile->showdocuments('expedition', $objectref, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', '', '', $soc->default_lang);
3905
3906
3907 // Show links to link elements
3908 $tmparray = $form->showLinkToObjectBlock($object, array(), array('shipping'), 1);
3909 $linktoelem = $tmparray['linktoelem'];
3910 $htmltoenteralink = $tmparray['htmltoenteralink'];
3911 print $htmltoenteralink;
3912
3913 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
3914
3915 // Show online signature link
3916 $useonlinesignature = getDolGlobalInt('EXPEDITION_ALLOW_ONLINESIGN');
3917
3918 if ($object->statut != Expedition::STATUS_DRAFT && $useonlinesignature) {
3919 print '<br><!-- Link to sign -->';
3920 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
3921 print showOnlineSignatureUrl('expedition', $object->ref, $object) . '<br>';
3922 }
3923
3924 print '</div><div class="fichehalfright">';
3925
3926
3927 // List of actions on element
3928 include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
3929 $formactions = new FormActions($db);
3930
3931 //button to go to messaging from the events box
3932 $MAXEVENT = 10;
3933 $morehtmlcenter = dolGetButtonTitle($langs->trans('FullConversation'), '', 'fa fa-comments imgforviewmode', DOL_URL_ROOT . '/expedition/messaging.php?id=' . $object->id);
3934
3935 $somethingshown = $formactions->showactions($object, 'shipping', $socid, 1, '', $MAXEVENT, '', $morehtmlcenter);
3936 print '</div></div>';
3937 }
3938
3939
3940 /*
3941 * Action presend
3942 */
3943
3944 //Select mail models is same action as presend
3945 if (GETPOST('modelselected')) {
3946 $action = 'presend';
3947 }
3948
3949 // Presend form
3950 $modelmail = 'shipping_send';
3951 $defaulttopic = 'SendShippingRef';
3952 $diroutput = $conf->expedition->dir_output . '/sending';
3953 $trackid = 'shi' . $object->id;
3954
3955 include DOL_DOCUMENT_ROOT . '/core/tpl/card_presend.tpl.php';
3956}
3957
3958// End of page
3959llxFooter();
3960$db->close();
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
Class to manage customers orders.
Class to manage a WYSIWYG editor.
Class to manage warehouses.
const STATUS_DRAFT
Draft status.
const STATUS_CANCELED
Canceled status.
const STATUS_CLOSED
Closed status -> parcel was received by customer / end of process prev status : validated or shipment...
const STATUS_VALIDATED
Validated status -> parcel is ready to be sent prev status : draft next status : closed or shipment_i...
Class to manage lines of shipment.
CRUD class for batch number management within shipment.
Class to manage building of HTML components.
Class to offer components to list and upload files.
Class to manage generation of HTML components Only common components must be here.
Class with static methods for building HTML components related to products Only components common to ...
Class to manage building of HTML components.
static liste_modeles($db, $maxfilenamelength=0)
Return list of active generation modules.
Class to manage the table of subscription to notifications.
Class to manage order lines.
Class ProductCombination Used to represent the relation between a product and one of its variants.
Class to manage products or services.
const DISABLED_STOCK
Stockable product.
const TYPE_PRODUCT
Regular product.
const TYPE_SERVICE
Service.
Manage record for batch number management.
Class with list of lots and properties.
Class to manage projects.
Class to manage proposals.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
Class to manage Dolibarr users.
getCountry($searchkey, $withcode='', $dbtouse=null, $outputlangs=null, $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
global $mysoc
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
GETPOSTFLOAT($paramname, $rounding='', $option=2)
Return the value of a $_GET or $_POST supervariable, converted into float.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
print_date_range($date_start, $date_end, $format='', $outputlangs=null)
Format output for start and end date.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_clone($srcobject, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
Definition html.lib.php:540
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
Definition html.lib.php:738
img_down($titlealt='default', $selected=0, $moreclass='')
Show down arrow logo.
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
img_up($titlealt='default', $selected=0, $moreclass='')
Show top arrow logo.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='', $cssfordropdown='info_admin')
Show information in HTML for admin users or standard users.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Definition html.lib.php:181
measuringUnitString($unitid, $measuring_style='', $unitscale=null, $use_short_label=0, $outputlangs=null)
Return translation label of a unit key.
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $nodie=0, $mode='')
Check permissions of a user to show a page and an object.
shipping_prepare_head($object)
Prepare array with list of tabs.