dolibarr 24.0.2
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 $result = $object->delete($user, 0, (bool) $also_update_stock);
677 if ($result > 0) {
678 header("Location: " . DOL_URL_ROOT . '/expedition/index.php');
679 exit;
680 } else {
681 setEventMessages($object->error, $object->errors, 'errors');
682 }
683 // TODO add alternative status
684 //} elseif ($action == 'reopen' && ($user->hasRight('expedition', 'creer') || $user->hasRight('expedition', 'shipping_advance', 'validate')))
685 //{
686 // $result = $object->setStatut(Expedition::STATUS_DRAFT);
687 // if ($result < 0)
688 // {
689 // setEventMessages($object->error, $object->errors, 'errors');
690 // }
691 //}
692 } elseif ($action == 'confirm_delete_subtotalline' && $confirm == 'yes' && $permissiontoadd) {
693 $result = $object->deleteSubtotalLine($langs, GETPOSTINT('lineid'), (bool) GETPOST('deletecorrespondingsubtotalline'), $user);
694 if ($result > 0) {
695 // reorder lines
696 $object->line_order(true, 'ASC', false);
697 // Define output language
698 $outputlangs = $langs;
699 $newlang = '';
700 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
701 $newlang = GETPOST('lang_id', 'aZ09');
702 }
703 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
704 $newlang = $object->thirdparty->default_lang;
705 }
706 if (!empty($newlang)) {
707 $outputlangs = new Translate("", $conf);
708 $outputlangs->setDefaultLang($newlang);
709 }
710 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
711 $ret = $object->fetch($object->id); // Reload to get new records
712 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
713 }
714
715 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
716 exit;
717 } else {
718 setEventMessages($object->error, $object->errors, 'errors');
719 }
720 } elseif ($action == 'confirm_deleteline' && $confirm == 'yes' && $usercancreate) {
721 // Remove a product line
722 $result = $object->deleteLine($user, $lineid);
723 if ($result > 0) {
724 // reorder lines
725 $object->line_order(true);
726 // Define output language
727 $outputlangs = $langs;
728 $newlang = '';
729 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
730 $newlang = GETPOST('lang_id', 'aZ09');
731 }
732 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
733 $newlang = $object->thirdparty->default_lang;
734 }
735 if (!empty($newlang)) {
736 $outputlangs = new Translate("", $conf);
737 $outputlangs->setDefaultLang($newlang);
738 }
739 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
740 $ret = $object->fetch($object->id); // Reload to get new records
741 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
742 }
743
744 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
745 exit;
746 } else {
747 setEventMessages($object->error, $object->errors, 'errors');
748 }
749 } elseif ($action == 'confirm_sign' && $confirm == 'yes' && $user->hasRight('expedition', 'creer')) {
750 $result = $object->setSignedStatus($user, GETPOSTINT('signed_status'), 0, 'SHIPPING_MODIFY');
751 if ($result >= 0) {
752 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
753 exit;
754 } else {
755 setEventMessages($object->error, $object->errors, 'errors');
756 }
757 } elseif ($action == 'confirm_unsign' && $confirm == 'yes' && $user->hasRight('expedition', 'creer')) {
758 $result = $object->setSignedStatus($user, Expedition::$SIGNED_STATUSES['STATUS_NO_SIGNATURE'], 0, 'SHIPPING_MODIFY');
759 if ($result >= 0) {
760 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
761 exit;
762 } else {
763 setEventMessages($object->error, $object->errors, 'errors');
764 }
765 } elseif ($action == 'setdate_livraison' && $user->hasRight('expedition', 'creer')) {
766 $datedelivery = dol_mktime(GETPOSTINT('liv_hour'), GETPOSTINT('liv_min'), 0, GETPOSTINT('liv_month'), GETPOSTINT('liv_day'), GETPOSTINT('liv_year'));
767
768 $object->fetch($id);
769 $result = $object->setDeliveryDate($user, $datedelivery); // Set the planned delivery date
770 if ($result < 0) {
771 setEventMessages($object->error, $object->errors, 'errors');
772 }
773 } elseif ($action == 'setdate_shipping' && $user->hasRight('expedition', 'creer')) {
774 $dateshipping = dol_mktime(GETPOSTINT('ship_hour'), GETPOSTINT('ship_min'), 0, GETPOSTINT('ship_month'), GETPOSTINT('ship_day'), GETPOSTINT('ship_year'));
775
776 $object->fetch($id);
777 $result = $object->setShippingDate($user, $dateshipping);
778 if ($result < 0) {
779 setEventMessages($object->error, $object->errors, 'errors');
780 }
781 } elseif (in_array($action, array('settracking_number', 'settracking_url', 'settrueWeight', 'settrueWidth', 'settrueHeight', 'settrueDepth', 'setshipping_method_id')) && $user->hasRight('expedition', 'creer')) {
782 // Action update
783 $error = 0;
784
785 if ($action == 'settracking_number') { // Test on permission not required
786 $object->tracking_number = trim(GETPOST('tracking_number', 'alpha'));
787 $object->tracking_number = preg_replace('/\s+/', '', $object->tracking_number);
788 }
789 if ($action == 'settracking_url') { // Test on permission not required
790 $object->tracking_url = trim(GETPOST('tracking_url', 'restricthtml'));
791 }
792
793 if ($action == 'settrueWeight') { // Test on permission not required
794 $object->trueWeight = GETPOSTFLOAT('trueWeight');
795 $object->weight_units = GETPOSTINT('weight_units');
796 }
797 if ($action == 'settrueWidth') { // Test on permission not required
798 $object->trueWidth = GETPOSTFLOAT('trueWidth');
799 }
800 if ($action == 'settrueHeight') { // Test on permission not required
801 $object->trueHeight = GETPOSTFLOAT('trueHeight');
802 $object->size_units = GETPOSTINT('size_units');
803 }
804 if ($action == 'settrueDepth') { // Test on permission not required
805 $object->trueDepth = GETPOSTFLOAT('trueDepth');
806 }
807 if ($action == 'setshipping_method_id') { // Test on permission not required
808 $object->shipping_method_id = GETPOSTINT('shipping_method_id');
809 }
810
811 if ($object->update($user) >= 0) {
812 header("Location: card.php?id=" . $object->id);
813 exit;
814 }
815 setEventMessages($object->error, $object->errors, 'errors');
816 $action = "";
817 } elseif ($action == 'classifybilled' && $permissiontoadd) {
818 $object->fetch($id);
819 $result = $object->setBilled();
820 if ($result >= 0) {
821 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
822 exit();
823 }
824 setEventMessages($object->error, $object->errors, 'errors');
825 } elseif ($action == 'classifyclosed' && $permissiontoadd) {
826 $object->fetch($id);
827 $result = $object->setClosed();
828 if ($result >= 0) {
829 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
830 exit();
831 }
832 setEventMessages($object->error, $object->errors, 'errors');
833 } elseif ($action == 'deleteline' && !empty($line_id) && $permissiontoadd) {
834 // delete a line
835 $error = 0;
836 $object->fetch($id);
837 $lines = $object->lines;
838 $line = new ExpeditionLigne($db);
839 $line->fk_expedition = $object->id;
840
841 $num_prod = count($lines);
842 for ($i = 0; $i < $num_prod; $i++) {
843 if ($lines[$i]->id == $line_id) {
844 // A standalone shipment (SHIPMENT_STANDALONE) loads its lines with fetch_lines_free(),
845 // which does not set details_entrepot, so count() must not be called on it blindly.
846 if (is_array($lines[$i]->details_entrepot) && count($lines[$i]->details_entrepot) > 1) {
847 // delete multi warehouse lines
848 foreach ($lines[$i]->details_entrepot as $details_entrepot) {
849 $line->id = $details_entrepot->line_id;
850 if (!$error && $line->delete($user) < 0) {
851 $error++;
852 }
853 }
854 } else {
855 // delete single warehouse line
856 $line->id = $line_id;
857 if (!$error && $line->delete($user) < 0) {
858 $error++;
859 }
860 }
861 }
862 unset($_POST["lineid"]);
863 }
864
865 if (!$error) {
866 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
867 exit();
868 } else {
869 setEventMessages($line->error, $line->errors, 'errors');
870 }
871 } elseif ($action == 'updateline' && $permissiontoadd && GETPOST('cancel', 'alpha') != $langs->trans("Cancel")) {
872 if (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
873 // Update a line
874 // Clean parameters
875
876 if (!$object->fetch($id) > 0) {
878 }
879 $object->fetch_thirdparty();
880
881 $qty = GETPOST('qty', 'alpha');
882 $description = '';
883 $fk_parent = 0;
884 $element_type = 'shipping';
885 $fk_unit = '';
886 $fk_product = 0;
887 $rang = 0;
888
889 // Extrafields
890 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
891 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
892 // Unset extrafield
893 if (is_array($extralabelsline)) {
894 // Get extra fields
895 foreach ($extralabelsline as $key => $value) {
896 unset($_POST["options_" . $key]);
897 }
898 }
899
900 $shipline = new ExpeditionLigne($db);
901 $shipline->fetch(GETPOSTINT('lineid'));
902
903
904 if (!$error) {
905 $result = $object->updatelinefree(GETPOSTINT('lineid'), (float) $qty, $element_type, $fk_product, GETPOSTINT('units'), $rang, $description, $fk_parent, 0, $array_options);
906
907 if ($result >= 0) {
908 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
909 // Define output language
910 $outputlangs = $langs;
911 $newlang = '';
912 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
913 $newlang = GETPOST('lang_id', 'aZ09');
914 }
915 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
916 $newlang = $object->thirdparty->default_lang;
917 }
918 if (!empty($newlang)) {
919 $outputlangs = new Translate("", $conf);
920 $outputlangs->setDefaultLang($newlang);
921 $outputlangs->load('products');
922 }
923
924 $ret = $object->fetch($object->id); // Reload to get new records
925 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
926 }
927 unset($_POST['qty']);
928 unset($_POST['units']);
929 } else {
930 setEventMessages($object->error, $object->errors, 'errors');
931 }
932 }
933 } elseif ($origin && $origin_id > 0) {
934 // Update a line
935 // Clean parameters
936 $qty = 0;
937 $entrepot_id = 0;
938 $batch_id = 0;
939
940 $lines = $object->lines;
941 $num_prod = count($lines);
942 for ($i = 0; $i < $num_prod; $i++) {
943 if ($lines[$i]->id == $line_id) { // we have found line to update
944 $update_done = false;
945 $line = new ExpeditionLigne($db);
946 $line->fk_expedition = $object->id;
947
948 // Extrafields Lines
949 $line->array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
950 // Unset extrafield POST Data
951 if (is_array($extrafields->attributes[$object->table_element_line]['label'])) {
952 foreach ($extrafields->attributes[$object->table_element_line]['label'] as $key => $value) {
953 unset($_POST["options_" . $key]);
954 }
955 }
956 $line->fk_product = $lines[$i]->fk_product;
957 if (is_array($lines[$i]->detail_batch) && count($lines[$i]->detail_batch) > 0) {
958 // line with lot
959 foreach ($lines[$i]->detail_batch as $detail_batch) {
960 $lotStock = new Productbatch($db);
961 $batch = "batchl" . $detail_batch->fk_expeditiondet . "_" . $detail_batch->fk_origin_stock;
962 $qty = "qtyl" . $detail_batch->fk_expeditiondet . '_' . $detail_batch->id;
963 $batch_id = GETPOSTINT($batch);
964 $batch_qty = GETPOSTFLOAT($qty);
965 if (!empty($batch_id)) {
966 if ($lotStock->fetch($batch_id) > 0 && $line->fetch($detail_batch->fk_expeditiondet) > 0) { // $line is ExpeditionLine
967 if ($lines[$i]->entrepot_id != 0) {
968 // allow update line entrepot_id if not multi warehouse shipping
969 $line->entrepot_id = $lotStock->warehouseid;
970 }
971
972 // detail_batch can be an object with keys, or an array of ExpeditionLineBatch
973 if (empty($line->detail_batch)) {
974 $line->detail_batch = new stdClass();
975 }
976
977 $line->detail_batch->fk_origin_stock = $batch_id;
978 $line->detail_batch->batch = $lotStock->batch;
979 $line->detail_batch->id = $detail_batch->id;
980 $line->detail_batch->entrepot_id = $lotStock->warehouseid;
981 $line->detail_batch->qty = $batch_qty;
982 if ($line->update($user) < 0) {
983 setEventMessages($line->error, $line->errors, 'errors');
984 $error++;
985 } else {
986 $update_done = true;
987 }
988 } else {
989 setEventMessages($lotStock->error, $lotStock->errors, 'errors');
990 $error++;
991 }
992 }
993 unset($_POST[$batch]);
994 unset($_POST[$qty]);
995 }
996 // add new batch
997 $lotStock = new Productbatch($db);
998 $batch = "batchl" . $line_id . "_0";
999 $qty = "qtyl" . $line_id . "_0";
1000 $batch_id = GETPOSTINT($batch);
1001 $batch_qty = GETPOSTFLOAT($qty);
1002 $lineIdToAddLot = 0;
1003 if ($batch_qty > 0 && !empty($batch_id)) {
1004 if ($lotStock->fetch($batch_id) > 0) {
1005 // check if lotStock warehouse id is same as line warehouse id
1006 if ($lines[$i]->entrepot_id > 0) {
1007 // single warehouse shipment line
1008 if ($lines[$i]->entrepot_id == $lotStock->warehouseid) {
1009 $lineIdToAddLot = $line_id;
1010 }
1011 } elseif (count($lines[$i]->details_entrepot) > 1) {
1012 // multi warehouse shipment lines
1013 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
1014 if ($detail_entrepot->entrepot_id == $lotStock->warehouseid) {
1015 $lineIdToAddLot = $detail_entrepot->line_id;
1016 }
1017 }
1018 }
1019 if ($lineIdToAddLot) {
1020 // add lot to existing line
1021 if ($line->fetch($lineIdToAddLot) > 0) {
1022 $line->detail_batch->fk_origin_stock = $batch_id;
1023 $line->detail_batch->batch = $lotStock->batch;
1024 $line->detail_batch->entrepot_id = $lotStock->warehouseid;
1025 $line->detail_batch->qty = $batch_qty;
1026 if ($line->update($user) < 0) {
1027 setEventMessages($line->error, $line->errors, 'errors');
1028 $error++;
1029 } else {
1030 $update_done = true;
1031 }
1032 } else {
1033 setEventMessages($line->error, $line->errors, 'errors');
1034 $error++;
1035 }
1036 } else {
1037 // create new line with new lot
1038 $line->origin_line_id = $lines[$i]->origin_line_id;
1039 $line->entrepot_id = $lotStock->warehouseid;
1040 $line->detail_batch[0] = new ExpeditionLineBatch($db);
1041 $line->detail_batch[0]->fk_origin_stock = $batch_id;
1042 $line->detail_batch[0]->batch = $lotStock->batch;
1043 $line->detail_batch[0]->entrepot_id = $lotStock->warehouseid;
1044 $line->detail_batch[0]->qty = $batch_qty;
1045 if ($object->create_line_batch($line, $line->array_options) < 0) {
1046 setEventMessages($object->error, $object->errors, 'errors');
1047 $error++;
1048 } else {
1049 $update_done = true;
1050 }
1051 }
1052 } else {
1053 setEventMessages($lotStock->error, $lotStock->errors, 'errors');
1054 $error++;
1055 }
1056 }
1057 } else {
1058 if ($lines[$i]->fk_product > 0) {
1059 // line without lot
1060 if ($lines[$i]->entrepot_id == 0) {
1061 // single warehouse shipment line or line in several warehouses context but with warehouse not defined
1062 $stockLocation = "entl" . $line_id;
1063 $qty = "qtyl" . $line_id;
1064 $line->id = $line_id;
1065 $line->entrepot_id = GETPOSTINT((string) $stockLocation);
1066 $line->qty = GETPOSTFLOAT($qty);
1067 if ($line->update($user) < 0) {
1068 setEventMessages($line->error, $line->errors, 'errors');
1069 $error++;
1070 } else {
1071 $update_done = true;
1072 }
1073 unset($_POST[$stockLocation]);
1074 unset($_POST[$qty]);
1075 } elseif ($lines[$i]->entrepot_id > 0) {
1076 // single warehouse shipment line
1077 $stockLocation = "entl" . $line_id;
1078 $qty = "qtyl" . $line_id;
1079 $line->id = $line_id;
1080 $line->entrepot_id = GETPOSTINT($stockLocation);
1081 $line->qty = GETPOSTFLOAT($qty);
1082 if ($line->update($user) < 0) {
1083 setEventMessages($line->error, $line->errors, 'errors');
1084 $error++;
1085 } else {
1086 $update_done = true;
1087 }
1088 unset($_POST[$stockLocation]);
1089 unset($_POST[$qty]);
1090 } elseif (count($lines[$i]->details_entrepot) > 1) {
1091 // multi warehouse shipment lines
1092 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
1093 if (!$error) {
1094 $stockLocation = "entl" . $detail_entrepot->line_id;
1095 $qty = "qtyl" . $detail_entrepot->line_id;
1096 $warehouse = GETPOSTINT($stockLocation);
1097 if (!empty($warehouse)) {
1098 $line->id = $detail_entrepot->line_id;
1099 $line->entrepot_id = $warehouse;
1100 $line->qty = GETPOSTFLOAT($qty);
1101 if ($line->update($user) < 0) {
1102 setEventMessages($line->error, $line->errors, 'errors');
1103 $error++;
1104 } else {
1105 $update_done = true;
1106 }
1107 }
1108 unset($_POST[$stockLocation]);
1109 unset($_POST[$qty]);
1110 }
1111 }
1112 } elseif (!isModEnabled('stock') && !isModEnabled('productbatch')) { // both product batch and stock are not activated.
1113 $qty = "qtyl" . $line_id;
1114 $line->id = $line_id;
1115 $line->qty = GETPOSTFLOAT($qty);
1116 $line->entrepot_id = 0;
1117 if ($line->update($user) < 0) {
1118 setEventMessages($line->error, $line->errors, 'errors');
1119 $error++;
1120 } else {
1121 $update_done = true;
1122 }
1123 unset($_POST[$qty]);
1124 }
1125 } else {
1126 // Product no predefined
1127 $qty = "qtyl" . $line_id;
1128 $line->id = $line_id;
1129 $line->qty = GETPOSTFLOAT($qty);
1130 $line->entrepot_id = 0;
1131 if ($line->update($user) < 0) {
1132 setEventMessages($line->error, $line->errors, 'errors');
1133 $error++;
1134 } else {
1135 $update_done = true;
1136 }
1137 unset($_POST[$qty]);
1138 }
1139 }
1140
1141 if (empty($update_done)) {
1142 $line->id = $lines[$i]->id;
1143 $resultInsertExtraFields = $line->insertExtraFields();
1144 if ($resultInsertExtraFields < 0) {
1145 setEventMessages($line->error, $line->errors, 'errors');
1146 $error++;
1147 } elseif ($resultInsertExtraFields > 0) {
1148 $update_done = true;
1149 }
1150 }
1151
1152 if (empty($update_done) && !$error) {
1153 setEventMessages($langs->trans('NoShipmentLineChangeDetected'), null, 'warnings');
1154 }
1155 }
1156 }
1157
1158 unset($_POST["lineid"]);
1159
1160 if (!$error) {
1161 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1162 // Define output language
1163 $outputlangs = $langs;
1164 $newlang = '';
1165 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
1166 $newlang = GETPOST('lang_id', 'aZ09');
1167 }
1168 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1169 $newlang = $object->thirdparty->default_lang;
1170 }
1171 if (!empty($newlang)) {
1172 $outputlangs = new Translate("", $conf);
1173 $outputlangs->setDefaultLang($newlang);
1174 }
1175
1176 $ret = $object->fetch($object->id); // Reload to get new records
1177 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1178 }
1179 } else {
1180 header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $object->id); // To redisplay the form being edited
1181 exit();
1182 }
1183 }
1184 } elseif ($action == 'updateline' && $permissiontoadd && GETPOST('cancel', 'alpha') == $langs->trans("Cancel")) {
1185 header('Location: ' . $_SERVER['PHP_SELF'] . '?id=' . $object->id); // To redisplay the form being edited
1186 exit();
1187 } elseif ($action == 'addline' && !$origin && getDolGlobalString('SHIPMENT_STANDALONE') && $usercancreate) { // Add a new line
1188 $langs->load('errors');
1189 $error = 0;
1190 $line_desc = (GETPOSTISSET('dp_desc') ? GETPOST('dp_desc', 'restricthtml') : '');
1191 $predef = '';
1192 $description = '';
1193 $fk_parent = 0;
1194 $fk_elementdet = '';
1195 $element_type = 'shipping';
1196 $fk_unit = '';
1197 $idprod = 0;
1198 $fk_product = 0;
1199 $fk_entrepot = '';
1200 $rang = '';
1201 $prod_entry_mode = GETPOST('prod_entry_mode', 'aZ09');
1202 if ($prod_entry_mode == 'free') {
1203 $idprod = 0;
1204 } else {
1205 $idprod = GETPOSTINT('idprod');
1206 if (getDolGlobalString('MAIN_DISABLE_FREE_LINES') && $idprod <= 0) {
1207 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("ProductOrService")), null, 'errors');
1208 $error++;
1209 }
1210 }
1211
1212 $qty = price2num(GETPOST('qty'.$predef, 'alpha'), 'MS', 2);
1213
1214 // Extrafields
1215 $extralabelsline = $extrafields->fetch_name_optionals_label($object->table_element_line);
1216 $array_options = $extrafields->getOptionalsFromPost($object->table_element_line, $predef);
1217 // Unset extrafield
1218 if (is_array($extralabelsline)) {
1219 // Get extra fields
1220 foreach ($extralabelsline as $key => $value) {
1221 unset($_POST["options_".$key]);
1222 }
1223 }
1224
1225 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && GETPOST('type') < 0) {
1226 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Type')), null, 'errors');
1227 $error++;
1228 }
1229
1230 if ($qty == '') {
1231 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
1232 $error++;
1233 }
1234 if ($qty < 0) {
1235 setEventMessages($langs->trans('FieldCannotBeNegative', $langs->transnoentitiesnoconv('Qty')), null, 'errors');
1236 $error++;
1237 }
1238 if ($prod_entry_mode == 'free' && (empty($idprod) || $idprod < 0) && empty($line_desc)) {
1239 setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Description')), null, 'errors');
1240 $error++;
1241 }
1242
1243 if (!$error && isModEnabled('variants') && $prod_entry_mode != 'free') {
1244 if ($combinations = GETPOST('combinations', 'array')) {
1245 // Check if there is a product with the given combination
1246 $prodcomb = new ProductCombination($db);
1247
1248 if ($res = $prodcomb->fetchByProductCombination2ValuePairs($idprod, $combinations)) {
1249 $idprod = $res->fk_product_child;
1250 $fk_product = $idprod; // Update $fk_product with the fetched child product
1251 } else {
1252 setEventMessages($langs->trans('ErrorProductCombinationNotFound'), null, 'errors');
1253 $error++;
1254 }
1255 }
1256 }
1257
1258 if (!$error && ($qty >= 0) && (!empty($line_desc) || (!empty($idprod) && $idprod > 0))) {
1259 // Clean parameters
1260 if (!empty($idprod) && $idprod > 0) {
1261 $prod = new Product($db);
1262 $prod->fetch($idprod);
1263 $desc = $prod->label;
1264 $description = $desc;
1265 // Define output language
1266 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
1267 $outputlangs = $langs;
1268 $newlang = '';
1269 if (GETPOST('lang_id', 'aZ09')) {
1270 $newlang = GETPOST('lang_id', 'aZ09');
1271 }
1272 if (empty($newlang)) {
1273 $newlang = $object->thirdparty->default_lang;
1274 }
1275 if (!empty($newlang)) {
1276 $outputlangs = new Translate("", $conf);
1277 $outputlangs->setDefaultLang($newlang);
1278 }
1279
1280 $description = (!empty($prod->multilangs[$outputlangs->defaultlang]["description"])) ? $prod->multilangs[$outputlangs->defaultlang]["description"] : $prod->description;
1281 } else {
1282 $description = $prod->description;
1283 }
1284 if (getDolGlobalInt('PRODUIT_AUTOFILL_DESC') == 0) {
1285 $description = dol_concatdesc($desc, $line_desc, false, getDolGlobalString('MAIN_CHANGE_ORDER_CONCAT_DESCRIPTION') ? true : false);
1286 } else {
1287 $description = $line_desc;
1288 }
1289
1290 // Add custom code and origin country into description
1291 if (!getDolGlobalString('MAIN_PRODUCT_DISABLE_CUSTOMCOUNTRYCODE') && (!empty($prod->customcode) || !empty($prod->country_code))) {
1292 $tmptxt = '(';
1293 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
1294 $outputlangs = $langs;
1295 $newlang = '';
1296 if (GETPOST('lang_id', 'alpha')) {
1297 $newlang = GETPOST('lang_id', 'alpha');
1298 }
1299 if (empty($newlang)) {
1300 $newlang = $object->thirdparty->default_lang;
1301 }
1302 if (!empty($newlang)) {
1303 $outputlangs = new Translate("", $conf);
1304 $outputlangs->setDefaultLang($newlang);
1305 $outputlangs->load('products');
1306 }
1307 if (!empty($prod->customcode)) {
1308 $tmptxt .= $outputlangs->transnoentitiesnoconv("CustomsCode").': '.$prod->customcode;
1309 }
1310 if (!empty($prod->customcode) && !empty($prod->country_code)) {
1311 $tmptxt .= ' - ';
1312 }
1313 if (!empty($prod->country_code)) {
1314 $tmptxt .= $outputlangs->transnoentitiesnoconv("CountryOrigin").': '.getCountry($prod->country_code, '', $db, $outputlangs, 0);
1315 }
1316 } else {
1317 if (!empty($prod->customcode)) {
1318 $tmptxt .= $langs->transnoentitiesnoconv("CustomsCode").': '.$prod->customcode;
1319 }
1320 if (!empty($prod->customcode) && !empty($prod->country_code)) {
1321 $tmptxt .= ' - ';
1322 }
1323 if (!empty($prod->country_code)) {
1324 $tmptxt .= $langs->transnoentitiesnoconv("CountryOrigin").': '.getCountry($prod->country_code, '', $db, $langs, 0);
1325 }
1326 }
1327 $tmptxt .= ')';
1328 $description = dol_concatdesc($desc, $tmptxt);
1329 }
1330 $type = $prod->type;
1331 $fk_unit = $prod->fk_unit;
1332 } else {
1333 $label = (GETPOST('product_label') ? GETPOST('product_label') : '');
1334 $desc = $line_desc;
1335 $type = GETPOST('type');
1336 $fk_unit = GETPOST('units', 'alpha');
1337 $description = $desc;
1338 $fk_elementdet = '';
1339 }
1340 $desc = dol_htmlcleanlastbr($desc);
1341
1342 // Insert line
1343 $result = $object->addlinefree((float) $qty, $element_type, $idprod, $fk_unit, min($rank, count($object->lines) + 1), $description, $fk_parent, $array_options);
1344
1345 if ($result > 0) {
1346 $ret = $object->fetch($object->id); // Reload to get new records
1347 $object->fetch_thirdparty();
1348 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1349 // Define output language
1350 $outputlangs = $langs;
1351 $newlang = GETPOST('lang_id', 'alpha');
1352 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1353 $newlang = $object->thirdparty->default_lang;
1354 }
1355 if (!empty($newlang)) {
1356 $outputlangs = new Translate("", $conf);
1357 $outputlangs->setDefaultLang($newlang);
1358 }
1359 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1360 }
1361 } else {
1362 header('Location: '.$_SERVER['PHP_SELF'].'?id='.$object->id); // To redisplay the form being edited
1363 exit();
1364 }
1365 }
1366 } elseif ($action == 'confirm_sign' && $confirm == 'yes' && $permissiontoadd) {
1367 $result = $object->setSignedStatus($user, GETPOSTINT('signed_status'), 0, 'SHIPPING_MODIFY');
1368 if ($result >= 0) {
1369 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1370 // Define output language
1371 $outputlangs = $langs;
1372 $newlang = '';
1373 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
1374 $newlang = GETPOST('lang_id', 'aZ09');
1375 }
1376 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1377 $newlang = $object->thirdparty->default_lang;
1378 }
1379 if (!empty($newlang)) {
1380 $outputlangs = new Translate("", $conf);
1381 $outputlangs->setDefaultLang($newlang);
1382 }
1383 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1384 }
1385
1386 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
1387 exit;
1388 } else {
1389 $mesg = $object->error;
1390 }
1391 } elseif ($action == 'confirm_unsign' && $confirm == 'yes' && $permissiontoadd) {
1392 $result = $object->setSignedStatus($user, Expedition::$SIGNED_STATUSES['STATUS_NO_SIGNATURE'], 0, 'SHIPPING_MODIFY');
1393 if ($result >= 0) {
1394 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
1395 // Define output language
1396 $outputlangs = $langs;
1397 $newlang = '';
1398 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
1399 $newlang = GETPOST('lang_id', 'aZ09');
1400 }
1401 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
1402 $newlang = $object->thirdparty->default_lang;
1403 }
1404 if (!empty($newlang)) {
1405 $outputlangs = new Translate("", $conf);
1406 $outputlangs->setDefaultLang($newlang);
1407 }
1408 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
1409 }
1410
1411 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
1412 exit;
1413 } else {
1414 $mesg = $object->error;
1415 }
1416 }
1417
1418 include DOL_DOCUMENT_ROOT . '/core/actions_printing.inc.php';
1419
1420 // Actions to send emails
1421 if (empty($id)) {
1422 $id = $facid;
1423 }
1424 $triggersendname = 'SHIPPING_SENTBYMAIL';
1425 $paramname = 'id';
1426 $autocopy = 'MAIN_MAIL_AUTOCOPY_SHIPMENT_TO';
1427 $mode = 'emailfromshipment';
1428 $trackid = 'shi' . $object->id;
1429 include DOL_DOCUMENT_ROOT . '/core/actions_sendmails.inc.php';
1430}
1431
1432/*
1433 * View
1434 */
1435
1436$title = $object->ref . ' - ' . $langs->trans("Card");
1437if ($action == 'create') {
1438 $title = $langs->trans("NewSending");
1439}
1440
1441$help_url = 'EN:Module_Shipments|FR:Module_Expéditions|ES:M&oacute;dulo_Expediciones|DE:Modul_Lieferungen';
1442
1443llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-expedition page-card');
1444
1445if (empty($action)) {
1446 $action = 'view';
1447}
1448
1449$form = new Form($db);
1450$formfile = new FormFile($db);
1451$formproduct = new FormProduct($db);
1452if (isModEnabled('project')) {
1453 $formproject = new FormProjets($db);
1454} else {
1455 $formproject = null;
1456}
1457
1458$product_static = new Product($db);
1459$shipment_static = new Expedition($db);
1460$warehousestatic = new Entrepot($db);
1461
1462if (!$origin && $action == 'create' && !getDolGlobalString('SHIPMENT_STANDALONE')) {
1463 print load_fiche_titre($langs->trans("CreateShipment"), '', 'dolly');
1464
1465 print '<br>' .$langs->trans("ShipmentCreationIsDoneFromOrder");
1466 $action = '';
1467 $id = '';
1468 $ref = '';
1469}
1470
1471// Mode creation.
1472if ($action == 'create' && $usercancreate) {
1473 $expe = new Expedition($db);
1474
1475 print load_fiche_titre($langs->trans("NewSending"), '', 'dolly');
1476
1477 if (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
1478 $soc = new Societe($db);
1479 if ($socid > 0) {
1480 $res = $soc->fetch($socid);
1481 }
1482
1483 $shipping_method_id = $soc->shipping_method_id;
1484 $note_private = $object->getDefaultCreateValueFor('note_private');
1485 $note_public = $object->getDefaultCreateValueFor('note_public');
1486
1487 print '<form name="crea_expedition" action="' . $_SERVER["PHP_SELF"] . '" method="POST">';
1488 print '<input type="hidden" name="token" value="' . newToken() . '">';
1489 print '<input type="hidden" name="action" value="add">';
1490 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
1491 print '<input type="hidden" name="originid" value="' . $id . '">';
1492 print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
1493
1494 print dol_get_fiche_head([]);
1495
1496 print '<table class="border centpercent">';
1497
1498 // Thirdparty
1499 print '<tr>';
1500 print '<td class="fieldrequired">' . $langs->trans('ThirdParty') . '</td>';
1501 if ($socid > 0) {
1502 print '<td>';
1503 print $soc->getNomUrl(1);
1504 print '<input type="hidden" name="socid" value="' . $soc->id . '">';
1505 print '</td>';
1506 } else {
1507 print '<td class="valuefieldcreate">';
1508 print img_picto('', 'company', 'class="pictofixedwidth"').$form->select_company('', 'socid', '', 'SelectThirdParty', 1, 0, array(), 0, 'minwidth175 maxwidth500 widthcentpercentminusxx');
1509 // reload page to retrieve customer information
1510 if (!getDolGlobalString('RELOAD_PAGE_ON_CUSTOMER_CHANGE_DISABLED')) {
1511 print '<script>
1512 $(document).ready(function() {
1513 $("#socid").change(function() {
1514 console.log("We have changed the company - Reload page");
1515 var socid = $(this).val();
1516 // reload page
1517 $("input[name=action]").val("create");
1518 $("input[name=changecompany]").val("1");
1519 $("form[name=crea_expedition]").submit();
1520 });
1521 });
1522 </script>';
1523 }
1524 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>';
1525 print '</td>';
1526 }
1527 print '</tr>'."\n";
1528
1529 // Project
1530 if (isModEnabled('project') && is_object($formproject)) {
1531 $projectid = GETPOSTINT('projectid');
1532 if (empty($projectid) && !empty($object->fk_project)) {
1533 $projectid = (int) $object->fk_project;
1534 }
1535
1536 $langs->load("projects");
1537 print '<tr>';
1538 print '<td>' . $langs->trans("Project") . '</td><td colspan="2">';
1539 print img_picto('', 'project', 'class="pictofixedwidth"');
1540 print $formproject->select_projects($soc->id, $projectid, 'projectid', 0, 0, 1, 0, 0, 0, 0, '', 1, 0, 'widthcentpercentminusxx');
1541 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>';
1542 print '</td>';
1543 print '</tr>';
1544 }
1545
1546 // Date sending
1547 print '<tr><td>'.$langs->trans("DateShipping").'</td>';
1548 print '<td colspan="3">';
1549 print img_picto('', 'action', 'class="pictofixedwidth"');
1550 $date_shipping = ($date_shipping ? $date_shipping : $object->date_shipping); // $date_shipping comes from GETPOST
1551 print $form->selectDate($date_shipping ? $date_shipping : -1, 'date_shipping', 1, 1, 1);
1552 print "</td>\n";
1553 print '</tr>';
1554
1555 // Date delivery planned
1556 print '<tr><td>'.$langs->trans("DateDeliveryPlanned").'</td>';
1557 print '<td colspan="3">';
1558 print img_picto('', 'action', 'class="pictofixedwidth"');
1559 $date_delivery = ($date_delivery ? $date_delivery : $object->delivery_date); // $date_delivery comes from GETPOST
1560 print $form->selectDate($date_delivery ? $date_delivery : -1, 'date_delivery', 1, 1, 1);
1561 print "</td>\n";
1562 print '</tr>';
1563
1564 // Weight
1565 print '<tr><td>';
1566 print $langs->trans("Weight");
1567 print '</td><td colspan="3">';
1568 print img_picto('', 'fa-balance-scale', 'class="pictofixedwidth"');
1569 print '<input name="weight" size="4" value="' . GETPOST('weight') . '"> '; // Do not use GETPOSTINT here, we must accept '' also.
1570 $text = $formproduct->selectMeasuringUnits("weight_units", "weight", GETPOST('weight_units'), 0, 2);
1571 $htmltext = $langs->trans("KeepEmptyForAutoCalculation");
1572 print $form->textwithpicto($text, $htmltext);
1573 print '</td></tr>';
1574
1575 // Dim width x height x depth
1576 print '<tr><td>';
1577 print $langs->trans("Width") . ' x ' . $langs->trans("Height") . ' x ' . $langs->trans("Depth");
1578 print ' </td><td colspan="3">';
1579 print img_picto('', 'fa-ruler', 'class="pictofixedwidth"');
1580 print '<input name="sizeW" size="4" value="' . GETPOST('sizeW') . '">';
1581 print ' x <input name="sizeH" size="4" value="' . GETPOST('sizeH') . '">';
1582 print ' x <input name="sizeS" size="4" value="' . GETPOST('sizeS') . '">';
1583 print ' ';
1584 $text = $formproduct->selectMeasuringUnits("size_units", "size", GETPOST('size_units'), 0, 2);
1585 $htmltext = $langs->trans("KeepEmptyForAutoCalculation");
1586 print $form->textwithpicto($text, $htmltext);
1587 print '</td></tr>';
1588
1589 // Delivery method
1590 print "<tr><td>".$langs->trans("DeliveryMethod")."</td>";
1591 print '<td colspan="3">';
1592 $expe->fetch_delivery_methods();
1593 print img_picto('', 'dolly', 'class="pictofixedwidth"');
1594 print $form->selectarray("shipping_method_id", $expe->meths, GETPOSTINT('shipping_method_id'), 1, 0, 0, "", 1, 0, 0, '', 'widthcentpercentminusxx');
1595 if ($user->admin) {
1596 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1597 }
1598 print "</td></tr>\n";
1599
1600 // Tracking number
1601 print "<tr><td>".$langs->trans("TrackingNumber")."</td>";
1602 print '<td colspan="3">';
1603 print img_picto('', 'barcode', 'class="pictofixedwidth"');
1604 print '<input name="tracking_number" size="20" value="'.GETPOST('tracking_number', 'alpha').'">';
1605 print "</td></tr>\n";
1606
1607 // Other attributes
1608 $parameters = array('objectsrc' => isset($objectsrc) ? $objectsrc : '', 'colspan' => ' colspan="3"', 'cols' => '3', 'socid' => $socid);
1609 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $expe, $action); // Note that $action and $object may have been modified by hook
1610 print $hookmanager->resPrint;
1611
1612 if (empty($reshook)) {
1613 // copy from order
1614 if ($object->fetch_optionals() > 0) {
1615 $expe->array_options = array_merge($expe->array_options, $object->array_options);
1616 }
1617 print $expe->showOptionals($extrafields, 'edit', $parameters);
1618 }
1619
1620 // Incoterms
1621 if (isModEnabled('incoterm')) {
1622 print '<tr>';
1623 print '<td><label for="incoterm_id">'.$form->textwithpicto($langs->trans("IncotermLabel"), $object->label_incoterms, 1).'</label></td>';
1624 print '<td colspan="3" class="maxwidthonsmartphone">';
1625 print img_picto('', 'incoterm', 'class="pictofixedwidth"');
1626 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''));
1627 print '</td></tr>';
1628 }
1629
1630 // Document model
1631 include_once DOL_DOCUMENT_ROOT.'/core/modules/expedition/modules_expedition.php';
1633 if (is_array($list) && count($list) > 1) {
1634 print "<tr><td>".$langs->trans("DefaultModel")."</td>";
1635 print '<td colspan="3">';
1636 print img_picto('', 'pdf', 'class="pictofixedwidth"');
1637 print $form->selectarray('model', $list, getDolGlobalString('EXPEDITION_ADDON_PDF'), 0, 0, 0, '', 0, 0, 0, '', 'widthcentpercentminusx');
1638 print "</td></tr>\n";
1639 }
1640
1641 // Note Public
1642 $htmltext = '';
1643 print '<tr>';
1644 print '<td class="tdtop">';
1645 print $form->textwithpicto($langs->trans('NotePublic'), $htmltext);
1646 print '</td>';
1647 print '<td valign="top" colspan="2">';
1648 $doleditor = new DolEditor('note_public', (string) $note_public, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
1649 print $doleditor->Create(1);
1650
1651 // Note Private
1652 if (!$user->socid) {
1653 print '<tr>';
1654 print '<td class="tdtop">';
1655 print $form->textwithpicto($langs->trans('NotePrivate'), $htmltext);
1656 print '</td>';
1657 print '<td valign="top" colspan="2">';
1658 $doleditor = new DolEditor('note_private', (string) $note_private, '', 80, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
1659 print $doleditor->Create(1);
1660 print "</td></tr>";
1661 }
1662
1663 print "</table>\n";
1664
1665 print dol_get_fiche_end();
1666
1667 print $form->buttonsSaveCancel("CreateDraft");
1668
1669 print "</form>\n";
1670 } elseif ($origin) {
1671 $classname = ucfirst($origin);
1672
1673 $object = new $classname($db);
1674 /* @var Commande|Facture $object */
1675 '@phan-var-force Commande|Facture $object';
1676
1677
1678 if ($object->fetch($origin_id)) { // This include the fetch_lines
1679 $soc = new Societe($db);
1680 $soc->fetch($object->socid);
1681
1682 $author = new User($db);
1683 $author->fetch($object->user_author_id);
1684
1685 if (isModEnabled('stock')) {
1686 $entrepot = new Entrepot($db);
1687 }
1688
1689 print '<form action="' . $_SERVER["PHP_SELF"] . '" method="post">';
1690 print '<input type="hidden" name="token" value="' . newToken() . '">';
1691 print '<input type="hidden" name="action" value="add">';
1692 print '<input type="hidden" name="origin" value="' . $origin . '">';
1693 print '<input type="hidden" name="origin_id" value="' . $object->id . '">';
1694 if (GETPOSTINT('entrepot_id')) {
1695 print '<input type="hidden" name="entrepot_id" value="' . GETPOSTINT('entrepot_id') . '">';
1696 }
1697
1698 print dol_get_fiche_head([]);
1699
1700 print '<table class="border centpercent">';
1701
1702 // Ref
1703 print '<tr><td class="titlefieldcreate fieldrequired">';
1704 if ($origin == 'commande' && isModEnabled('order')) {
1705 print $langs->trans("RefOrder");
1706 }
1707 if ($origin == 'propal' && isModEnabled("propal")) {
1708 print $langs->trans("RefProposal");
1709 }
1710 print '</td><td colspan="3">';
1711 print $object->getNomUrl(1);
1712 print '</td>';
1713 print "</tr>\n";
1714
1715 // Ref client
1716 print '<tr><td>';
1717 if ($origin == 'commande') {
1718 print $langs->trans('RefCustomerOrder');
1719 } elseif ($origin == 'propal') {
1720 print $langs->trans('RefCustomerOrder');
1721 } else {
1722 print $langs->trans('RefCustomer');
1723 }
1724 print '</td><td colspan="3">';
1725 print '<input type="text" name="ref_customer" value="' . $object->ref_client . '" />';
1726 print '</td>';
1727 print '</tr>';
1728
1729 // Thirdparty
1730 print '<tr><td class="titlefieldcreate fieldrequired">' . $langs->trans('Company') . '</td>';
1731 print '<td colspan="3">' . $soc->getNomUrl(1) . '</td>';
1732 print '<input type="hidden" name="socid" value="' . $soc->id . '">';
1733 print '</tr>';
1734
1735 // Project
1736 if (isModEnabled('project') && is_object($formproject)) {
1737 $projectid = GETPOSTINT('projectid');
1738 if (empty($projectid) && !empty($object->fk_project)) {
1739 $projectid = (int) $object->fk_project;
1740 }
1741 if ($origin == 'project') {
1742 $projectid = ($object->id ? $object->id : 0);
1743 }
1744 $langs->load("projects");
1745 print '<tr>';
1746 print '<td>' . $langs->trans("Project") . '</td><td colspan="2">';
1747 print img_picto('', 'project', 'class="pictofixedwidth"');
1748 print $formproject->select_projects($soc->id, $projectid, 'projectid', 0, 0, 1, 0, 0, 0, 0, '', 1, 0, 'widthcentpercentminusxx');
1749 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>';
1750 print '</td>';
1751 print '</tr>';
1752 }
1753
1754 // Date delivery planned
1755 print '<tr><td>' . $langs->trans("DateDeliveryPlanned") . '</td>';
1756 print '<td colspan="3">';
1757 print img_picto('', 'action', 'class="pictofixedwidth"');
1758 $date_delivery = ($date_delivery ? $date_delivery : $object->delivery_date); // $date_delivery comes from GETPOST
1759 print $form->selectDate($date_delivery ? $date_delivery : -1, 'date_delivery', 1, 1, 1);
1760 print "</td>\n";
1761 print '</tr>';
1762
1763 // Date shipment (sending)
1764 print '<tr><td>' . $langs->trans("DateShipping") . '</td>';
1765 print '<td colspan="3">';
1766 print img_picto('', 'action', 'class="pictofixedwidth"');
1767 //$date_shipping = ($date_shipping ? $date_shipping : $object->date_shipping); // $date_shipping comes from GETPOST
1768 print $form->selectDate($date_shipping ? $date_shipping : -1, 'date_shipping', 1, 1, 1);
1769 print "</td>\n";
1770 print '</tr>';
1771
1772 // Note Public
1773 print '<tr><td>' . $langs->trans("NotePublic") . '</td>';
1774 print '<td colspan="3">';
1775 $doleditor = new DolEditor('note_public', $object->note_public, '', 60, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
1776 print $doleditor->Create(1);
1777 print "</td></tr>";
1778
1779 // Note Private
1780 if ($object->note_private && !$user->socid) {
1781 print '<tr><td>' . $langs->trans("NotePrivate") . '</td>';
1782 print '<td colspan="3">';
1783 $doleditor = new DolEditor('note_private', $object->note_private, '', 60, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
1784 print $doleditor->Create(1);
1785 print "</td></tr>";
1786 }
1787
1788 // Weight
1789 print '<tr><td>';
1790 print $langs->trans("Weight");
1791 print '</td><td colspan="3">';
1792 print img_picto('', 'fa-balance-scale', 'class="pictofixedwidth"');
1793
1794 print '<input name="weight" size="4" value="' . dol_escape_htmltag(GETPOST('weight')) . '"> '; // Do not use GETPOSTINT here, we must accept '' also.
1795 $text = $formproduct->selectMeasuringUnits("weight_units", "weight", (string) GETPOST('weight_units'), 0, 2);
1796 $htmltext = $langs->trans("KeepEmptyForAutoCalculation");
1797 print $form->textwithpicto($text, $htmltext);
1798 print '</td></tr>';
1799
1800 // Dim Width x Height x Depth
1801 print '<tr><td>';
1802 print $langs->trans("Width") . ' x ' . $langs->trans("Height") . ' x ' . $langs->trans("Depth");
1803 print ' </td><td colspan="3">';
1804 print img_picto('', 'fa-ruler', 'class="pictofixedwidth"');
1805 print '<input name="sizeW" size="4" value="' . GETPOST('sizeW') . '">';
1806 print ' x <input name="sizeH" size="4" value="' . GETPOST('sizeH') . '">';
1807 print ' x <input name="sizeS" size="4" value="' . GETPOST('sizeS') . '">';
1808 print ' ';
1809 $text = $formproduct->selectMeasuringUnits("size_units", "size", GETPOST('size_units'), 0, 2);
1810 $htmltext = $langs->trans("KeepEmptyForAutoCalculation");
1811 print $form->textwithpicto($text, $htmltext);
1812 print '</td></tr>';
1813
1814 // Delivery method
1815 print "<tr><td>" . $langs->trans("DeliveryMethod") . "</td>";
1816 print '<td colspan="3">';
1817 $expe->fetch_delivery_methods();
1818 print img_picto('', 'dolly', 'class="pictofixedwidth"');
1819 print $form->selectarray("shipping_method_id", $expe->meths, GETPOSTINT('shipping_method_id'), 1, 0, 0, "", 1, 0, 0, '', 'widthcentpercentminusxx');
1820 if ($user->admin) {
1821 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1822 }
1823 print "</td></tr>\n";
1824
1825 // Tracking number
1826 print "<tr><td>" . $langs->trans("TrackingNumber") . "</td>";
1827 print '<td colspan="3">';
1828 print img_picto('', 'barcode', 'class="pictofixedwidth"');
1829 print '<input name="tracking_number" size="20" value="' . GETPOST('tracking_number', 'alpha') . '">';
1830 print "</td></tr>\n";
1831
1832 // Incoterms
1833 if (isModEnabled('incoterm')) {
1834 print '<tr>';
1835 print '<td><label for="incoterm_id">' . $form->textwithpicto($langs->trans("IncotermLabel"), $object->label_incoterms, 1) . '</label></td>';
1836 print '<td colspan="3" class="maxwidthonsmartphone">';
1837 print img_picto('', 'incoterm', 'class="pictofixedwidth"');
1838 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''));
1839 print '</td></tr>';
1840 }
1841
1842 // Document model
1843 include_once DOL_DOCUMENT_ROOT . '/core/modules/expedition/modules_expedition.php';
1845 if (is_array($list) && count($list) > 1) {
1846 print "<tr><td>" . $langs->trans("DefaultModel") . "</td>";
1847 print '<td colspan="3">';
1848 print img_picto('', 'pdf', 'class="pictofixedwidth"');
1849 print $form->selectarray('model', $list, getDolGlobalString('EXPEDITION_ADDON_PDF'), 0, 0, 0, '', 0, 0, 0, '', 'widthcentpercentminusx');
1850 print "</td></tr>\n";
1851 }
1852
1853 // Other attributes. Fields from hook formObjectOptions and Extrafields.
1854 // $objectsrc is Commande|Facture
1855 $objectsav = $object; // Because Expedition is $expe and not $object that is wrongly a duplicate of $objectsrc.
1856 $object = $expe;
1857 // Propagate extrafieldsvalue from source object to shipment object
1858 if (isset($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && !empty($extrafields->attributes[$object->table_element]['label'])) {
1859 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) {
1860 if (array_key_exists('options_'.$key, $objectsav->array_options)) { // We take value from order only if extrafield has the same name/key.
1861 $object->array_options['options_'.$key] = $objectsav->array_options['options_'.$key];
1862 }
1863 }
1864 }
1865 $parameters = array(
1866 'objectsrc' => isset($objectsrc) ? $objectsrc : '',
1867 'cols' => '3',
1868 'socid' => $socid
1869 );
1870 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php';
1871 $object = $objectsav;
1872
1873 print "</table>";
1874
1875 print dol_get_fiche_end();
1876
1877
1878 // Shipment lines
1879
1880 $numAsked = count($object->lines);
1881
1882 print '<script type="text/javascript">' . "\n";
1883 print 'jQuery(document).ready(function() {' . "\n";
1884 print 'jQuery("#autofill").click(function() {';
1885 $i = 0;
1886 while ($i < $numAsked) {
1887 print 'jQuery("#qtyl' . $i . '").val(jQuery("#qtyasked' . $i . '").val() - jQuery("#qtydelivered' . $i . '").val());' . "\n";
1888 if (isModEnabled('productbatch')) {
1889 print 'jQuery("#qtyl' . $i . '_' . $i . '").val(jQuery("#qtyasked' . $i . '").val() - jQuery("#qtydelivered' . $i . '").val());' . "\n";
1890 }
1891 $i++;
1892 }
1893 print 'return false; });' . "\n";
1894 print 'jQuery("#autoreset").click(function() { console.log("Reset values to 0"); jQuery(".qtyl").val(0);' . "\n";
1895 print 'return false; });' . "\n";
1896 print '});' . "\n";
1897 print '</script>' . "\n";
1898
1899 print '<br>';
1900
1901 print '<div class="div-table-responsive-no-min">';
1902 print '<table class="noborder centpercent">';
1903
1904 // Load shipments already done for same order
1905 $object->loadExpeditions();
1906
1907 $alreadyQtyBatchSetted = $alreadyQtySetted = array();
1908
1909 $title_lines_to_disable = array();
1910
1911 if ($numAsked) {
1912 if (isModEnabled('subtotals')) {
1913 if (!(getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES') || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
1914 $title_lines_to_disable = $object->getDisabledShippmentSubtotalLines();
1915 }
1916 $selectedLines = GETPOST('subtotal_toselect', 'array:int');
1917 foreach ($object->lines as $line) {
1918 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1919 $show_check_add_buttons = true;
1920 break;
1921 }
1922 }
1923 }
1924 print '<tr class="liste_titre">';
1925 print '<td>';
1926 if (isset($show_check_add_buttons)) {
1927 print $form->showCheckAddButtons('checkforselect');
1928 }
1929 print $langs->trans("Description");
1930 print '</td>';
1931 print '<td class="center">' . $langs->trans("QtyOrdered") . '</td>';
1932 print '<td class="center">' . $langs->trans("QtyShipped") . '</td>';
1933 print '<td class="center">' . $langs->trans("QtyToShip");
1934 if (!isModEnabled('productbatch')) {
1935 print '<br><a href="#" id="autofill" class="opacitymedium link cursor cursorpointer">' . img_picto($langs->trans("Autofill"), 'autofill', 'class="paddingrightonly"') . '</a>';
1936 print ' / ';
1937 } else {
1938 print '<br>';
1939 }
1940 print '<span id="autoreset" class="opacitymedium link cursor cursorpointer">' . img_picto($langs->trans("Reset"), 'eraser') . '</span>';
1941 print '</td>';
1942 if (isModEnabled('stock')) {
1943 if (!isModEnabled('productbatch')) {
1944 print '<td class="left">' . $langs->trans("Warehouse") . ' (' . $langs->trans("Stock") . ')</td>';
1945 } else {
1946 print '<td class="left">' . $langs->trans("Warehouse") . ' / ' . $langs->trans("Batch") . ' (' . $langs->trans("Stock") . ')</td>';
1947 }
1948 }
1949 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1950 print '<td class="left">' . $langs->trans('StockEntryDate') . '</td>';
1951 }
1952 print "</tr>\n";
1953 }
1954
1955 $warehouse_id = GETPOSTINT('entrepot_id');
1956 $warehousePicking = array();
1957 // get all warehouse children for picking
1958 if ($warehouse_id > 0) {
1959 $warehousePicking[] = $warehouse_id;
1960 $warehouseObj = new Entrepot($db);
1961 $warehouseObj->get_children_warehouses($warehouse_id, $warehousePicking);
1962 }
1963
1964 $indiceAsked = 0;
1965 while ($indiceAsked < $numAsked) {
1966 $product = new Product($db);
1967
1968 $line = $object->lines[$indiceAsked];
1969
1970 $parameters = array('i' => $indiceAsked, 'line' => $line, 'num' => $numAsked);
1971 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $object, $action);
1972 if ($reshook < 0) {
1973 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1974 }
1975
1976 if (empty($reshook) && $line->special_code != SUBTOTALS_SPECIAL_CODE) {
1977 // Show product and description
1978 $type = $line->product_type ? $line->product_type : $line->fk_product_type;
1979 // Try to enhance type detection using date_start and date_end for free lines where type was not saved.
1980 if (!empty($line->date_start)) {
1981 $type = 1;
1982 }
1983 if (!empty($line->date_end)) {
1984 $type = 1;
1985 }
1986
1987 print '<!-- line for order line ' . $line->id . ' -->' . "\n";
1988 print '<tr class="oddeven" id="row-' . $line->id . '">' . "\n";
1989
1990 $qtyProdCom = $line->qty;
1991 $productChildrenNb = 0;
1992 // Product label
1993 if ($line->fk_product > 0) { // If predefined product
1994 $res = $product->fetch($line->fk_product);
1995 if ($res < 0) {
1996 dol_print_error($db, $product->error, $product->errors);
1997 }
1998 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
1999 $productChildrenNb = $product->hasFatherOrChild(1);
2000 }
2001 if ($productChildrenNb > 0) {
2002 $product->loadStockForVirtualProduct('warehouseopen', $qtyProdCom);
2003 } else {
2004 $product->load_stock('warehouseopen'); // Load all $product->stock_warehouse[idwarehouse]->detail_batch
2005 }
2006 //var_dump($product->stock_warehouse[1]);
2007
2008 print '<td>';
2009 print '<a name="' . $line->id . '"></a>'; // Anchor to retor to the line
2010
2011 // Show product and description
2012 $product_static->type = $line->fk_product_type;
2013 $product_static->id = $line->fk_product;
2014 $product_static->ref = $line->ref;
2015 $product_static->status = $line->product_tosell;
2016 $product_static->status_buy = $line->product_tobuy;
2017 $product_static->status_batch = $line->product_tobatch;
2018
2019 $showdescinproductdesc = getDolGlobalString('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE');
2020
2021 $text = $product_static->getNomUrl(1);
2022 $text .= ' - ' . (!empty($line->label) ? $line->label : $line->product_label);
2023 $description = ($showdescinproductdesc ? '' : dol_htmlentitiesbr($line->desc));
2024 $description .= empty($product->stockable_product) ? $langs->trans('StockDisabled') : $langs->trans('StockEnabled');
2025 print $form->textwithtooltip($text, $description, 3, 0, '', (string) $i);
2026
2027 // Show range
2028 print_date_range($db->jdate($line->date_start), $db->jdate($line->date_end));
2029
2030 // Add description in form
2031 if ($showdescinproductdesc) {
2032 print ($line->desc && $line->desc != $line->product_label) ? '<br>' . dol_htmlentitiesbr($line->desc) : '';
2033 }
2034
2035 $expLine = new ExpeditionLigne($db);
2036 $srcLine = new OrderLine($db);
2037 $srcLine->id = $line->id;
2038 $srcLine->fetch_optionals(); // Fetch source line extrafields so shipment line creation matches order UI.
2039 $expLine->array_options = array_merge($expLine->array_options, $srcLine->array_options);
2040 $lineExtrafieldsHtml = $expLine->showOptionals($extrafields, 'edit', array(), (string) $indiceAsked, '', '1', 'line');
2041 if (!empty($lineExtrafieldsHtml)) {
2042 print '<div style="padding-top: 10px" id="extrafield_lines_area_create_' . $indiceAsked . '" name="extrafield_lines_area_create_' . $indiceAsked . '">';
2043 print $lineExtrafieldsHtml;
2044 print '</div>';
2045 }
2046
2047 print '</td>';
2048 } else {
2049 print "<td>";
2050 if ($type == 1) {
2051 $text = img_object($langs->trans('Service'), 'service');
2052 } else {
2053 $text = img_object($langs->trans('Product'), 'product');
2054 }
2055
2056 if (!empty($line->label)) {
2057 $text .= ' <strong>' . $line->label . '</strong>';
2058 print $form->textwithtooltip($text, $line->desc, 3, 0, '', (string) $i);
2059 } else {
2060 print $text . ' ' . nl2br($line->desc);
2061 }
2062
2063 // Show range
2064 print_date_range($db->jdate($line->date_start), $db->jdate($line->date_end));
2065
2066 $expLine = new ExpeditionLigne($db);
2067 $srcLine = new OrderLine($db);
2068 $srcLine->id = $line->id;
2069 $srcLine->fetch_optionals(); // Fetch source line extrafields so shipment line creation matches order UI.
2070 $expLine->array_options = array_merge($expLine->array_options, $srcLine->array_options);
2071 $lineExtrafieldsHtml = $expLine->showOptionals($extrafields, 'edit', array(), (string) $indiceAsked, '', '1', 'line');
2072 if (!empty($lineExtrafieldsHtml)) {
2073 print '<div style="padding-top: 10px" id="extrafield_lines_area_create_' . $indiceAsked . '" name="extrafield_lines_area_create_' . $indiceAsked . '">';
2074 print $lineExtrafieldsHtml;
2075 print '</div>';
2076 }
2077 print "</td>\n";
2078 }
2079
2080 // unit of order
2081 $unit_order = '';
2082 if (getDolGlobalString('PRODUCT_USE_UNITS')) {
2083 $unit_order = measuringUnitString($line->fk_unit);
2084 }
2085
2086 // Qty
2087 print '<td class="center">' . $line->qty;
2088 print '<input name="qtyasked' . $indiceAsked . '" id="qtyasked' . $indiceAsked . '" type="hidden" value="' . $line->qty . '">';
2089 if ($line->qty) {
2090 print ' ' . $unit_order . '</td>';
2091 }
2092
2093 // Qty already shipped
2094 print '<td class="center">';
2095 $quantityDelivered = isset($object->expeditions[$line->id]) ? $object->expeditions[$line->id] : '';
2096 print $quantityDelivered;
2097 print '<input name="qtydelivered' . $indiceAsked . '" id="qtydelivered' . $indiceAsked . '" type="hidden" value="' . $quantityDelivered . '">';
2098 if ($quantityDelivered) {
2099 print ' ' . $unit_order . '</td>';
2100 }
2101
2102 // Qty to ship
2103 $quantityAsked = $line->qty;
2104 if ($line->product_type == Product::TYPE_SERVICE && !getDolGlobalString('STOCK_SUPPORTS_SERVICES') && !getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
2105 $quantityToBeDelivered = 0;
2106 } else {
2107 if (is_numeric($quantityDelivered)) {
2108 $quantityToBeDelivered = $quantityAsked - $quantityDelivered;
2109 } else {
2110 $quantityToBeDelivered = $quantityAsked;
2111 }
2112 }
2113
2114 $warehouseObject = null;
2115 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
2116 print '<!-- Case warehouse already known or product not a predefined product -->';
2117 //ship from preselected location
2118 $stock = + (isset($product->stock_warehouse[$warehouse_id]->real) ? $product->stock_warehouse[$warehouse_id]->real : 0); // Convert to number
2119 if (($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) || $product->stockable_product == Product::DISABLED_STOCK) {
2120 $deliverableQty = $quantityToBeDelivered;
2121 } else {
2122 $deliverableQty = min($quantityToBeDelivered, $stock);
2123 }
2124 if ($deliverableQty < 0) {
2125 $deliverableQty = 0;
2126 }
2127 if (!isModEnabled('productbatch') || !$product->hasbatch()) {
2128 // Quantity to send
2129 print '<td class="center">';
2130 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || ($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES'))) {
2131 if (GETPOSTISSET('qtyl'.$indiceAsked) && GETPOST('qtyl'.$indiceAsked) !== '') {
2132 $deliverableQty = GETPOSTFLOAT('qtyl'.$indiceAsked, 'MS');
2133 }
2134 print '<input name="idl' . $indiceAsked . '" type="hidden" value="' . $line->id . '">';
2135 $qtylValue = $deliverableQty;
2136 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
2137 $qtylValue = '';
2138 } elseif (is_numeric($qtylValue)) {
2139 // Render as locale number so GETPOSTFLOAT() on submit reads it back via price2num option=2 without confusing '.' for a thousand separator in es_ES.
2140 $qtylValue = price($qtylValue);
2141 }
2142 print '<input name="qtyl' . $indiceAsked . '" id="qtyl' . $indiceAsked . '" class="qtyl right" type="text" size="4" value="' . $qtylValue . '">';
2143 } else {
2144 if (getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
2145 print '<input name="idl' . $indiceAsked . '" type="hidden" value="' . $line->id . '">';
2146 print '<input name="qtyl' . $indiceAsked . '" id="qtyl' . $indiceAsked . '" type="hidden" value="0">';
2147 }
2148
2149 print $langs->trans("NA");
2150 }
2151 print '</td>';
2152
2153 // Stock
2154 if (isModEnabled('stock')) {
2155 print '<td class="left">';
2156 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) { // Type of product need stock change ?
2157 // Show warehouse combo list
2158 $ent = "entl" . $indiceAsked;
2159 $idl = "idl" . $indiceAsked;
2160 $tmpentrepot_id = is_numeric(GETPOST($ent)) ? GETPOSTINT($ent) : $warehouse_id;
2161 if ($line->fk_product > 0) {
2162 print '<!-- Show warehouse selection -->';
2163
2164 $stockMin = false;
2165 if (getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
2166 $stockMin = 0;
2167 }
2168 if ($productChildrenNb > 0) {
2169 print $formproduct->selectWarehouses($tmpentrepot_id, 'entl' . $indiceAsked, '', 1, 0, 0, '', 0, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
2170 } else {
2171 if ($product->stockable_product == Product::ENABLED_STOCK) {
2172 print $formproduct->selectWarehouses($tmpentrepot_id, 'entl' . $indiceAsked, '', 1, 0, $line->fk_product, '', 1, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
2173 } else {
2174 print img_warning() . ' ' . $langs->trans('StockDisabled');
2175 }
2176 }
2177
2178 if ($tmpentrepot_id > 0 && $tmpentrepot_id == $warehouse_id) {
2179 //print $stock.' '.$quantityToBeDelivered;
2180 if ($stock < $quantityToBeDelivered) {
2181 print ' ' . img_warning($langs->trans("StockTooLow")); // Stock too low for this $warehouse_id but you can change warehouse
2182 }
2183 }
2184 }
2185 } else {
2186 print '<span class="opacitymedium">(' . $langs->trans("Service") . ')</span><input name="entl' . $indiceAsked . '" id="entl' . $indiceAsked . '" type="hidden" value="0">';
2187 }
2188 print '</td>';
2189 }
2190 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2191 print '<td></td>';
2192 } //StockEntrydate
2193 print "</tr>\n";
2194
2195 // Show subproducts of product
2196 if (getDolGlobalString('PRODUIT_SOUSPRODUITS') && $line->fk_product > 0) {
2197 $product->get_sousproduits_arbo();
2198 $prods_arbo = $product->get_arbo_each_prod($qtyProdCom);
2199 if (count($prods_arbo) > 0) {
2200 foreach ($prods_arbo as $key => $value) {
2201 //print $value[0];
2202 $img = '';
2203 if ($value['stock'] < $value['stock_alert']) {
2204 $img = img_warning($langs->trans("StockTooLow"));
2205 }
2206 print "<tr class=\"oddeven\"><td>&nbsp; &nbsp; &nbsp; ->
2207 <a href=\"" . DOL_URL_ROOT . "/product/card.php?id=" . $value['id'] . "\">" . $value['fullpath'] . "
2208 </a> (" . $value['nb'] . ")</td><td class=\"center\"> " . $value['nb_total'] . "</td><td>&nbsp;</td><td>&nbsp;</td>
2209 <td class=\"center\">" . $value['stock'] . " " . $img . "</td>";
2210 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2211 print '<td></td>';
2212 } //StockEntrydate
2213 print "</tr>";
2214 }
2215 }
2216 }
2217 } else {
2218 // Product need lot
2219 print '<td></td><td></td>';
2220 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2221 print '<td></td>';
2222 } //StockEntrydate
2223 print '</tr>'; // end line and start a new one for lot/serial
2224 print '<!-- Case product need lot -->';
2225
2226 $staticwarehouse = new Entrepot($db);
2227 if ($warehouse_id > 0) {
2228 $staticwarehouse->fetch($warehouse_id);
2229 }
2230
2231 $subj = 0;
2232 // Define nb of lines suggested for this order line
2233 $nbofsuggested = 0;
2234 if (is_object($product->stock_warehouse[$warehouse_id]) && !empty($product->stock_warehouse[$warehouse_id]->detail_batch)) {
2235 foreach ($product->stock_warehouse[$warehouse_id]->detail_batch as $dbatch) {
2236 $nbofsuggested++;
2237 }
2238 }
2239 print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
2240 if (is_object($product->stock_warehouse[$warehouse_id]) && !empty($product->stock_warehouse[$warehouse_id]->detail_batch)) {
2241 foreach ($product->stock_warehouse[$warehouse_id]->detail_batch as $dbatch) { // $dbatch is instance of Productbatch
2242 //var_dump($dbatch);
2243 $batchStock = +$dbatch->qty; // To get a numeric
2244 $deliverableQty = min($quantityToBeDelivered, $batchStock);
2245
2246 // Now we will check if we have to reduce the deliverableQty by taking into account the qty already suggested in previous line
2247 if (isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
2248 $deliverableQty = min($quantityToBeDelivered, $batchStock - $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)]);
2249 } else {
2250 if (!isset($alreadyQtyBatchSetted[$line->fk_product])) {
2251 $alreadyQtyBatchSetted[$line->fk_product] = array();
2252 }
2253
2254 if (!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])) {
2255 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch] = array();
2256 }
2257
2258 $deliverableQty = min($quantityToBeDelivered, $batchStock);
2259 }
2260
2261 if ($deliverableQty < 0) {
2262 $deliverableQty = 0;
2263 }
2264
2265 $inputName = 'qtyl' . $indiceAsked . '_' . $subj;
2266 if (GETPOSTISSET($inputName)) {
2267 $deliverableQty = GETPOST($inputName, 'int');
2268 }
2269
2270 $tooltipClass = $tooltipTitle = '';
2271 if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
2272 $tooltipClass = ' classfortooltip';
2273 $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines') . ' : ' . $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
2274 } else {
2275 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = 0;
2276 }
2277 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = $deliverableQty + $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
2278
2279 print '<!-- subj=' . $subj . '/' . $nbofsuggested . ' --><tr ' . ((($subj + 1) == $nbofsuggested) ? 'oddeven' : '') . '>';
2280 print '<td colspan="3" ></td><td class="center">';
2281 $qtylValue = $deliverableQty;
2282 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
2283 $qtylValue = '';
2284 } elseif (is_numeric($qtylValue)) {
2285 $qtylValue = price($qtylValue);
2286 }
2287 print '<input class="qtyl ' . $tooltipClass . ' right" title="' . $tooltipTitle . '" name="qtyl' . $indiceAsked . '_' . $subj . '" id="qtyl' . $indiceAsked . '_' . $subj . '" type="text" size="4" value="' . $qtylValue . '">';
2288 print '</td>';
2289
2290 print '<!-- Show details of lot -->';
2291 print '<td class="left">';
2292
2293 print $staticwarehouse->getNomUrl(0) . ' / ';
2294
2295 print '<input name="batchl' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $dbatch->id . '">';
2296
2297 $detail = '';
2298 $detail .= $langs->trans("Batch") . ': ' . $dbatch->batch;
2299 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY') && !empty($dbatch->sellby)) {
2300 $detail .= ' - ' . $langs->trans("SellByDate") . ': ' . dol_print_date($dbatch->sellby, "day");
2301 }
2302 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY') && !empty($dbatch->eatby)) {
2303 $detail .= ' - ' . $langs->trans("EatByDate") . ': ' . dol_print_date($dbatch->eatby, "day");
2304 }
2305 $detail .= ' - ' . $langs->trans("Qty") . ': ' . $dbatch->qty;
2306 $detail .= '<br>';
2307 print $detail;
2308
2309 $quantityToBeDelivered -= $deliverableQty;
2310 if ($quantityToBeDelivered < 0) {
2311 $quantityToBeDelivered = 0;
2312 }
2313 $subj++;
2314 print '</td>';
2315 if (getDolGlobalInt('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2316 print '<td>' . dol_print_date($dbatch->context['stock_entry_date'], 'day') . '</td>'; //StockEntrydate
2317 }
2318 print '</tr>';
2319 }
2320 } else {
2321 print '<!-- Case there is no details of lot at all -->';
2322 print '<tr class="oddeven"><td colspan="3"></td><td class="center">';
2323 print '<input class="qtyl right" name="qtyl' . $indiceAsked . '_' . $subj . '" id="qtyl' . $indiceAsked . '_' . $subj . '" type="text" size="4" value="0" disabled="disabled"> ';
2324 print '</td>';
2325
2326 print '<td class="left">';
2327 print img_warning() . ' ' . $langs->trans("NoProductToShipFoundIntoStock", $staticwarehouse->label);
2328 print '</td>';
2329 if (getDolGlobalInt('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2330 print '<td></td>';
2331 } //StockEntrydate
2332 print '</tr>';
2333 }
2334 }
2335 } else {
2336 // ship from multiple locations
2337 if (!isModEnabled('productbatch') || !$product->hasbatch()) {
2338 print '<!-- Case warehouse not already known and product does not need lot -->';
2339 print '<td></td><td></td>';
2340 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2341 print '<td></td>';
2342 } //StockEntrydate
2343 print '</tr>' . "\n"; // end line and start a new one for each warehouse
2344
2345 print '<input name="idl' . $indiceAsked . '" type="hidden" value="' . $line->id . '">';
2346 $subj = 0;
2347 // Define nb of lines suggested for this order line
2348 $nbofsuggested = 0;
2349
2350 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
2351 if ($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
2352 $nbofsuggested++;
2353 }
2354 }
2355 $tmpwarehouseObject = new Entrepot($db);
2356 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) { // $stock_warehouse is product_stock
2357 $var = $subj % 2;
2358 if (!empty($warehousePicking) && !in_array($warehouse_id, $warehousePicking)) {
2359 // if a warehouse was selected by user, picking is limited to this warehouse and his children
2360 continue;
2361 }
2362
2363 $tmpwarehouseObject->fetch($warehouse_id);
2364 if ($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
2365 $stock = +$stock_warehouse->real; // Convert it to number
2366 $deliverableQty = min($quantityToBeDelivered, $stock);
2367 $deliverableQty = max(0, $deliverableQty);
2368 // Quantity to send
2369 print '<!-- subj=' . $subj . '/' . $nbofsuggested . ' --><tr ' . ((($subj + 1) == $nbofsuggested) ? 'oddeven' : '') . '>';
2370 print '<td colspan="3" ></td><td class="center"><!-- qty to ship (no lot management for product line indiceAsked=' . $indiceAsked . ') -->';
2371 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
2372 // For a virtual product (kit), $deliverableQty is already min($quantityToBeDelivered, $stock) computed above
2373 // and the per-warehouse "stock" returned by loadStockForVirtualProduct() is $qtyWish, not a shared physical
2374 // stock. The cross-line $alreadyQtySetted cap must not be applied for kits, otherwise the 2nd and next order
2375 // lines of the same kit would be wrongly pre-filled with 0.
2376 $tooltipClass = $tooltipTitle = '';
2377 if ($productChildrenNb <= 0) {
2378 if (isset($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])) {
2379 $deliverableQty = min($quantityToBeDelivered, $stock - $alreadyQtySetted[$line->fk_product][intval($warehouse_id)]);
2380 } else {
2381 if (!isset($alreadyQtySetted[$line->fk_product])) {
2382 $alreadyQtySetted[$line->fk_product] = array();
2383 }
2384
2385 $deliverableQty = min($quantityToBeDelivered, $stock);
2386 }
2387
2388 if ($deliverableQty < 0) {
2389 $deliverableQty = 0;
2390 }
2391
2392 if (!empty($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])) {
2393 $tooltipClass = ' classfortooltip';
2394 $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$alreadyQtySetted[$line->fk_product][intval($warehouse_id)];
2395 } else {
2396 $alreadyQtySetted[$line->fk_product][intval($warehouse_id)] = 0;
2397 }
2398
2399 $alreadyQtySetted[$line->fk_product][intval($warehouse_id)] = $deliverableQty + $alreadyQtySetted[$line->fk_product][intval($warehouse_id)];
2400 }
2401
2402 $inputName = 'qtyl'.$indiceAsked.'_'.$subj;
2403 if (GETPOSTISSET($inputName)) {
2404 $deliverableQty = GETPOSTINT($inputName);
2405 }
2406 $qtylValue = $deliverableQty;
2407 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
2408 $qtylValue = '';
2409 } elseif (is_numeric($qtylValue)) {
2410 $qtylValue = price($qtylValue);
2411 }
2412 print '<input class="qtyl' . $tooltipClass . ' right" title="' . $tooltipTitle . '" name="qtyl' . $indiceAsked . '_' . $subj . '" id="qtyl' . $indiceAsked . '" type="text" size="4" value="' . $qtylValue . '">';
2413 print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $warehouse_id . '">';
2414 } else {
2415 if (getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
2416 print '<input name="qtyl' . $indiceAsked . '_' . $subj . '" id="qtyl' . $indiceAsked . '" type="hidden" value="0">';
2417 }
2418
2419 print $langs->trans("NA");
2420 }
2421 print '</td>';
2422
2423 // Stock
2424 print '<td class="left">';
2425 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
2426 if ($product->stockable_product == Product::ENABLED_STOCK || $productChildrenNb > 0) {
2427 print $tmpwarehouseObject->getNomUrl(0) . ' ';
2428 if ($productChildrenNb <= 0) {
2429 print '<!-- Show details of stock -->';
2430 print '(' . $stock . ')';
2431 }
2432 } else {
2433 print img_warning() . ' ' . $langs->trans('StockDisabled');
2434 }
2435 } else {
2436 print '<span class="opacitymedium">(' . $langs->trans("Service") . ')</span>';
2437 }
2438 print '</td>';
2439 $quantityToBeDelivered -= $deliverableQty;
2440 if ($quantityToBeDelivered < 0) {
2441 $quantityToBeDelivered = 0;
2442 }
2443 $subj++;
2444 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2445 print '<td></td>';
2446 } //StockEntrydate
2447 print "</tr>\n";
2448 }
2449 }
2450 // Show subproducts of product (not recommended)
2451 if (getDolGlobalString('PRODUIT_SOUSPRODUITS') && $line->fk_product > 0) {
2452 $product->get_sousproduits_arbo();
2453 $prods_arbo = $product->get_arbo_each_prod($qtyProdCom);
2454 if (count($prods_arbo) > 0) {
2455 foreach ($prods_arbo as $key => $value) {
2456 //print $value[0];
2457 $img = '';
2458 if ($value['stock'] < $value['stock_alert']) {
2459 $img = img_warning($langs->trans("StockTooLow"));
2460 }
2461 print '<tr class"oddeven"><td>';
2462 print "&nbsp; &nbsp; &nbsp; ->
2463 <a href=\"" . DOL_URL_ROOT . "/product/card.php?id=" . $value['id'] . "\">" . $value['fullpath'] . "
2464 </a> (" . $value['nb'] . ")</td><td class=\"center\"> " . $value['nb_total'] . "</td><td>&nbsp;</td><td>&nbsp;</td>
2465 <td class=\"center\">" . $value['stock'] . " " . $img . "</td>";
2466 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2467 print '<td></td>';
2468 } //StockEntrydate
2469 print "</tr>";
2470 }
2471 }
2472 }
2473 } else {
2474 print '<!-- Case warehouse not already known and product need lot -->';
2475 print '<td></td><td></td>';
2476 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2477 print '<td></td>';
2478 } //StockEntrydate
2479 print '</tr>'; // end line and start a new one for lot/serial
2480
2481 $subj = 0;
2482 print '<input name="idl' . $indiceAsked . '" type="hidden" value="' . $line->id . '">';
2483
2484 $tmpwarehouseObject = new Entrepot($db);
2485 $productlotObject = new Productlot($db);
2486
2487 // Define nb of lines suggested for this order line
2488 $nbofsuggested = 0;
2489 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
2490 if (($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) && (!empty($stock_warehouse->detail_batch))) {
2491 $nbofsuggested += count($stock_warehouse->detail_batch);
2492 }
2493 }
2494
2495 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
2496 $var = $subj % 2;
2497 if (!empty($warehousePicking) && !in_array($warehouse_id, $warehousePicking)) {
2498 // if a warehouse was selected by user, picking is limited to this warehouse and his children
2499 continue;
2500 }
2501
2502 $tmpwarehouseObject->fetch($warehouse_id);
2503 if (($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) && (!empty($stock_warehouse->detail_batch))) {
2504 foreach ($stock_warehouse->detail_batch as $dbatch) {
2505 $batchStock = +$dbatch->qty; // To get a numeric
2506 if (isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
2507 $deliverableQty = min($quantityToBeDelivered, $batchStock - $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)]);
2508 } else {
2509 if (!isset($alreadyQtyBatchSetted[$line->fk_product])) {
2510 $alreadyQtyBatchSetted[$line->fk_product] = array();
2511 }
2512
2513 if (!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])) {
2514 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch] = array();
2515 }
2516
2517 $deliverableQty = min($quantityToBeDelivered, $batchStock);
2518 }
2519
2520 if ($deliverableQty < 0) {
2521 $deliverableQty = 0;
2522 }
2523
2524 $inputName = 'qtyl' . $indiceAsked . '_' . $subj;
2525 if (GETPOSTISSET($inputName)) {
2526 $deliverableQty = GETPOSTINT($inputName);
2527 }
2528
2529 $tooltipClass = $tooltipTitle = '';
2530 if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
2531 $tooltipClass = ' classfortooltip';
2532 $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines') . ' : ' . $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
2533 } else {
2534 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = 0;
2535 }
2536 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = $deliverableQty + $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
2537
2538 print '<!-- subj=' . $subj . '/' . $nbofsuggested . ' --><tr ' . ((($subj + 1) == $nbofsuggested) ? 'oddeven' : '') . '><td colspan="3"></td><td class="center">';
2539 $qtylValue = $deliverableQty;
2540 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
2541 $qtylValue = '';
2542 } elseif (is_numeric($qtylValue)) {
2543 $qtylValue = price($qtylValue);
2544 }
2545 print '<input class="qtyl right ' . $tooltipClass . '" title="' . $tooltipTitle . '" name="' . $inputName . '" id="' . $inputName . '" type="text" size="4" value="' . $qtylValue . '">';
2546 print '</td>';
2547
2548 print '<td class="left">';
2549
2550 print $tmpwarehouseObject->getNomUrl(0) . ' / ';
2551
2552 print '<!-- Show details of lot -->';
2553 print '<input name="batchl' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $dbatch->id . '">';
2554 print '<input name="entl' . $indiceAsked . '_'.$subj.'" type="hidden" value="' . $tmpwarehouseObject->id . '">';
2555
2556 //print '|'.$line->fk_product.'|'.$dbatch->batch.'|<br>';
2557 print $langs->trans("Batch") . ': ';
2558 $result = $productlotObject->fetch(0, $line->fk_product, $dbatch->batch);
2559 if ($result > 0) {
2560 print $productlotObject->getNomUrl(1);
2561 } else {
2562 print $langs->trans("TableLotIncompleteRunRepairWithParamStandardEqualConfirmed");
2563 }
2564 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY') && !empty($dbatch->sellby)) {
2565 print ' - ' . $langs->trans("SellByDate") . ': ' . dol_print_date($dbatch->sellby, "day");
2566 }
2567 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY') && !empty($dbatch->eatby)) {
2568 print ' - ' . $langs->trans("EatByDate") . ': ' . dol_print_date($dbatch->eatby, "day");
2569 }
2570 print ' (' . $dbatch->qty . ')';
2571 $quantityToBeDelivered -= $deliverableQty;
2572 if ($quantityToBeDelivered < 0) {
2573 $quantityToBeDelivered = 0;
2574 }
2575 //dol_syslog('deliverableQty = '.$deliverableQty.' batchStock = '.$batchStock);
2576 $subj++;
2577 print '</td>';
2578 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2579 print '<td class="left">' . dol_print_date($dbatch->context['stock_entry_date'], 'day') . '</td>';
2580 }
2581 print '</tr>';
2582 }
2583 }
2584 }
2585 }
2586 if ($subj == 0) { // Line not shown yet, we show it
2587 $warehouse_selected_id = GETPOSTINT('entrepot_id');
2588
2589 print '<!-- line not shown yet, we show it -->';
2590 print '<tr class="oddeven"><td colspan="3"></td><td class="center">';
2591
2592 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
2593 $disabled = '';
2594 $alt = '';
2595 if (isModEnabled('productbatch') && $product->hasbatch()) {
2596 $disabled = 'disabled="disabled"';
2597 $alt = 'Product need serial or batch number';
2598 }
2599 if ($warehouse_selected_id <= 0) { // We did not force a given warehouse, so we won't have no warehouse to change qty.
2600 $disabled = 'disabled="disabled"';
2601 }
2602 // finally we overwrite the input with the product status stockable_product if it's disabled
2603 if ($product->stockable_product == Product::ENABLED_STOCK) {
2604 $disabled = '';
2605 }
2606 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).'"' : '').'> ';
2607 if (empty($disabled) && (!getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER') || $product->stockable_product == Product::DISABLED_STOCK)) {
2608 print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $warehouse_selected_id . '">';
2609 }
2610 } elseif ($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
2611 // If we are here, it means STOCK_SUPPORTS_SERVICES is off (otherwise the previous test would be true)
2612 // So a service has no stock here, so neither a preselected warehouse nor STOCK_DISALLOW_NEGATIVE_TRANSFER is relevant here.
2613 $disabled = '';
2614 if (isModEnabled('productbatch') && $product->hasbatch()) {
2615 $disabled = 'disabled="disabled"';
2616 }
2617 print '<input class="qtyl right" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="'.$quantityToBeDelivered.'"'.($disabled ? ' '.$disabled : '').'> ';
2618 // This hidden field must always be output: it is the marker the POST handler uses to detect that the quantities
2619 // of this line are named qtyl<i>_<subj>. Without it the handler looks for qtyl<i> instead, finds nothing and
2620 // silently drops the line. Value 0 = no warehouse, same as entl<i> in the single warehouse case above.
2621 print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="0">';
2622 } else {
2623 print $langs->trans("NA");
2624 }
2625 print '</td>';
2626
2627 print '<td class="left">';
2628 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
2629 // product may use stock management
2630 if (($warehouse_selected_id > 0 && $product->stockable_product == Product::ENABLED_STOCK)) {
2631 $warehouseObject = new Entrepot($db);
2632 $warehouseObject->fetch($warehouse_selected_id);
2633 print img_warning() . ' ' . $langs->trans("NoProductToShipFoundIntoStock", $warehouseObject->label);
2634 } else {
2635 if ($line->fk_product) {
2636 if ($product->stockable_product != Product::ENABLED_STOCK) {
2637 print img_warning() . ' ' . $langs->trans('StockDisabled');
2638 } else {
2639 print img_warning() . ' ' . $langs->trans('StockTooLow');
2640 }
2641 } else {
2642 print '';
2643 }
2644 }
2645 } else {
2646 print '<span class="opacitymedium">(' . $langs->trans("Service") . ')</span>';
2647 }
2648 print '</td>';
2649 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
2650 print '<td></td>';
2651 } //StockEntrydate
2652 print '</tr>';
2653 }
2654 }
2655 } elseif (empty($reshook) && $line->special_code == SUBTOTALS_SPECIAL_CODE && !in_array($line->id, $title_lines_to_disable)) {
2656 require dol_buildpath('/core/tpl/subtotalline_select.tpl.php');
2657 }
2658
2659 $indiceAsked++;
2660 }
2661
2662 print "</table>";
2663 print '</div>';
2664
2665 print '<br>';
2666
2667 print $form->buttonsSaveCancel("Create");
2668
2669 print '</form>';
2670
2671 print '<br>';
2672 } else {
2674 }
2675 }
2676} elseif ($object->id > 0) {
2677 '@phan-var-force Expedition $object'; // Need to force it (type overridden earlier)
2678
2679 // Edit and view mode
2680
2681 $lines = $object->lines;
2682
2683 $num_prod = count($lines);
2684
2685 if (!empty($object->origin) && $object->origin_id > 0) {
2686 $typeobject = $object->origin;
2687 $origin = (string) $object->origin; // Cast to string because origin is type hinted as object too.
2688 $origin_id = $object->origin_id;
2689
2690 $object->fetch_origin(); // Load property $object->origin_object (old $object->commande, $object->propal, ...)
2691 }
2692
2693 $soc = new Societe($db);
2694 $soc->fetch($object->socid);
2695
2696 $res = $object->fetch_optionals();
2697
2698 $head = shipping_prepare_head($object);
2699 print dol_get_fiche_head($head, 'shipping', $langs->trans("Shipment"), -1, $object->picto);
2700
2701 $formconfirm = '';
2702
2703 // Confirm deletion
2704 if ($action == 'delete') {
2705 $formquestion = array();
2706 if ($object->status == Expedition::STATUS_CLOSED && getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) {
2707 $formquestion = array(
2708 array(
2709 'label' => $langs->trans('ShipmentIncrementStockOnDelete'),
2710 'name' => 'alsoUpdateStock',
2711 'type' => 'checkbox',
2712 'value' => 0
2713 ),
2714 );
2715 }
2716 $formconfirm = $form->formconfirm(
2717 $_SERVER['PHP_SELF'] . '?id=' . $object->id,
2718 $langs->trans('DeleteSending'),
2719 $langs->trans("ConfirmDeleteSending", $object->ref),
2720 'confirm_delete',
2721 $formquestion,
2722 0,
2723 1
2724 );
2725 }
2726
2727 // Confirm delete of subtotal line
2728 if ($action == 'ask_subtotal_deleteline') {
2729 $lineid = GETPOSTINT('lineid');
2730 $langs->load("subtotals");
2731 $title = "DeleteSubtotalLine";
2732 $question = "ConfirmDeleteSubtotalLine";
2733 if (GETPOST('type') == 'title') {
2734 $formconfirm = array(array('type' => 'checkbox', 'name' => 'deletecorrespondingsubtotalline', 'label' => $langs->trans("DeleteCorrespondingSubtotalLine"), 'value' => 0));
2735 $title = "DeleteTitleLine";
2736 $question = "ConfirmDeleteTitleLine";
2737 }
2738 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id . '&lineid=' . $lineid, $langs->trans($title), $langs->trans($question), 'confirm_delete_subtotalline', $formconfirm, 'no', 1);
2739 }
2740
2741 // Confirm validation
2742 if ($action == 'valid') {
2743 $objectref = substr($object->ref, 1, 4);
2744 if ($objectref == 'PROV') {
2745 $numref = $object->getNextNumRef($soc);
2746 } else {
2747 $numref = (string) $object->ref;
2748 }
2749
2750 $text = $langs->trans("ConfirmValidateSending", $numref);
2751 if (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) {
2752 $text .= '<br>' . img_picto('', 'movement', 'class="pictofixedwidth"') . $langs->trans("StockMovementWillBeRecorded") . '.';
2753 } elseif (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) {
2754 $text .= '<br>' . img_picto('', 'movement', 'class="pictofixedwidth"') . $langs->trans("StockMovementNotYetRecorded") . '.';
2755 }
2756
2757 if (isModEnabled('notification')) {
2758 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2759 $notify = new Notify($db);
2760 $text .= '<br>';
2761 $text .= $notify->confirmMessage('SHIPPING_VALIDATE', $object->socid, $object);
2762 }
2763
2764 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id, $langs->trans('ValidateSending'), $text, 'confirm_valid', '', 0, 1, 260);
2765 }
2766
2767 // Confirm cancellation
2768 if ($action == 'cancel') {
2769 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'] . '?id=' . $object->id, $langs->trans('CancelSending'), $langs->trans("ConfirmCancelSending", $object->ref), 'confirm_cancel', '', 0, 1);
2770 }
2771
2772 // Confirm sign
2773 if ($action == 'sign') {
2774 $text = $langs->trans('ConfirmSignShipping');
2775 if (isModEnabled('notification')) {
2776 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2777 $notify = new Notify($db);
2778 $text .= '<br>';
2779 $text .= $notify->confirmMessage('SHIPPING_MODIFY', $object->socid, $object);
2780 }
2781 $formquestion = [];
2782 $formquestion[] = [
2783 'type' => 'select',
2784 'name' => 'signed_status',
2785 'select_show_empty' => 0,
2786 'label' => '<span class="fieldrequired">' . $langs->trans('SignStatus') . '</span>',
2787 'values' => $object->getSignedStatusLocalisedArray()
2788 ];
2789 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('SignShipping'), $text, 'confirm_sign', $formquestion, 0, 1);
2790 }
2791
2792 // Confirm unsign
2793 if ($action == 'unsign') {
2794 $text = $langs->trans('ConfirmUnsignShipping');
2795 if (isModEnabled('notification')) {
2796 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2797 $notify = new Notify($db);
2798 $text .= '<br>';
2799 $text .= $notify->confirmMessage('SHIPPING_MODIFY', $object->socid, $object);
2800 }
2801 $formconfirm = $form->formconfirm(dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id]), $langs->trans('UnsignShipping'), $text, 'confirm_unsign', '', 0, 1);
2802 }
2803
2804 // Call Hook formConfirm
2805 $parameters = array('formConfirm' => $formconfirm);
2806 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2807 if (empty($reshook)) {
2808 $formconfirm .= $hookmanager->resPrint;
2809 } elseif ($reshook > 0) {
2810 $formconfirm = $hookmanager->resPrint;
2811 }
2812
2813 // Print form confirm
2814 print $formconfirm;
2815
2816 // Calculate totalWeight and totalVolume for all products
2817 // by adding weight and volume of each product line.
2818 $tmparray = $object->getTotalWeightVolume();
2819 $totalWeight = $tmparray['weight'];
2820 $totalVolume = $tmparray['volume'];
2821
2822 if (!empty($typeobject) && $typeobject === 'commande' && is_object($object->origin_object) && $object->origin_object->id && isModEnabled('order')) {
2823 $objectsrc = new Commande($db);
2824 $objectsrc->fetch($object->origin_object->id);
2825 }
2826 if (!empty($typeobject) && $typeobject === 'propal' && is_object($object->origin_object) && $object->origin_object->id && isModEnabled("propal")) {
2827 $objectsrc = new Propal($db);
2828 $objectsrc->fetch($object->origin_object->id);
2829 }
2830
2831 // Shipment card
2832 $linkback = '<a href="' . DOL_URL_ROOT . '/expedition/list.php?restore_lastsearch_values=1' . (!empty($socid) ? '&socid=' . $socid : '') . '">' . $langs->trans("BackToList") . '</a>';
2833 $morehtmlref = '<div class="refidno">';
2834 // Ref customer shipment
2835 $morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_customer', $object->ref_customer, $object, $user->hasRight('expedition', 'creer'), 'string', '', 0, 1);
2836 $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);
2837
2838 // Thirdparty
2839 $morehtmlref .= '<br>' . $object->thirdparty->getNomUrl(1);
2840
2841 // Project
2842 if ($origin && $origin_id > 0) {
2843 if (isModEnabled('project')) {
2844 $langs->load("projects");
2845 $morehtmlref .= '<br>';
2846 if (0) { // @phpstan-ignore-line Do not change on shipment
2847 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
2848 if ($action != 'classify') {
2849 $morehtmlref .= '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=classify&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
2850 }
2851 $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');
2852 } else {
2853 if (!empty($objectsrc) && !empty($objectsrc->fk_project)) {
2854 $proj = new Project($db);
2855 $proj->fetch($objectsrc->fk_project);
2856 $morehtmlref .= $proj->getNomUrl(1);
2857 if ($proj->title) {
2858 $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
2859 }
2860 }
2861 }
2862 }
2863 } elseif (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
2864 // Project
2865 if (isModEnabled('project')) {
2866 $langs->load("projects");
2867 $morehtmlref .= '<br>';
2868 if ($permissiontoadd) {
2869 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
2870 if ($action != 'classify') {
2871 $morehtmlref .= '<a class="editfielda" href="' . $_SERVER['PHP_SELF'] . '?action=classify&token=' . newToken() . '&id=' . $object->id . '">' . img_edit($langs->transnoentitiesnoconv('SetProject')) . '</a> ';
2872 }
2873 $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');
2874 } else {
2875 if (!empty($object->fk_project)) {
2876 $proj = new Project($db);
2877 $proj->fetch($object->fk_project);
2878 $morehtmlref .= $proj->getNomUrl(1);
2879 if ($proj->title) {
2880 $morehtmlref .= '<span class="opacitymedium"> - ' . dol_escape_htmltag($proj->title) . '</span>';
2881 }
2882 }
2883 }
2884 }
2885 }
2886
2887 $morehtmlref .= '</div>';
2888 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
2889
2890 print '<div class="fichecenter">';
2891 print '<div class="fichehalfleft">';
2892 print '<div class="underbanner clearboth"></div>';
2893
2894 print '<table class="border tableforfield centpercent">';
2895
2896 // Linked documents
2897 if (!empty($typeobject) && $typeobject == 'commande' && $object->origin_object->id && isModEnabled('order')) {
2898 print '<tr><td>';
2899 print $langs->trans("RefOrder") . '</td>';
2900 print '<td>';
2901 print $objectsrc->getNomUrl(1, 'commande');
2902 print "</td>\n";
2903 print '</tr>';
2904 }
2905 if (!empty($typeobject) && $typeobject == 'propal' && $object->origin_object->id && isModEnabled("propal")) {
2906 print '<tr><td>';
2907 print $langs->trans("RefProposal") . '</td>';
2908 print '<td>';
2909 print $objectsrc->getNomUrl(1, 'expedition');
2910 print "</td>\n";
2911 print '</tr>';
2912 }
2913
2914 // Date creation
2915 print '<tr><td class="titlefieldmiddle">' . $langs->trans("DateCreation") . '</td>';
2916 print '<td>' . dol_print_date($object->date_creation, "dayhour") . "</td>\n";
2917 print '</tr>';
2918
2919 // Delivery date planned
2920 print '<tr><td height="10">';
2921 print '<table class="nobordernopadding centpercent"><tr><td>';
2922 print $langs->trans('DateDeliveryPlanned');
2923 print '</td>';
2924 if ($action != 'editdate_livraison') {
2925 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>';
2926 }
2927 print '</tr></table>';
2928 print '</td><td>';
2929 if ($action == 'editdate_livraison') {
2930 print '<form name="setdate_livraison" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
2931 print '<input type="hidden" name="token" value="' . newToken() . '">';
2932 print '<input type="hidden" name="action" value="setdate_livraison">';
2933 print $form->selectDate($object->date_delivery ? $object->date_delivery : -1, 'liv_', 1, 1, 0, "setdate_livraison", 1, 0);
2934 print '<input type="submit" class="button button-edit smallpaddingimp" value="' . $langs->trans('Modify') . '">';
2935 print '</form>';
2936 } else {
2937 print $object->date_delivery ? dol_print_date($object->date_delivery, 'dayhour') : '&nbsp;';
2938 }
2939 print '</td>';
2940 print '</tr>';
2941
2942 // Delivery sending date
2943 print '<tr><td height="10">';
2944 print '<table class="nobordernopadding centpercent"><tr><td>';
2945 print $langs->trans('DateShipping');
2946 print '</td>';
2947 if ($action != 'editdate_shipping') {
2948 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>';
2949 }
2950 print '</tr></table>';
2951 print '</td><td>';
2952 if ($action == 'editdate_shipping') {
2953 print '<form name="setdate_shipping" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
2954 print '<input type="hidden" name="token" value="' . newToken() . '">';
2955 print '<input type="hidden" name="action" value="setdate_shipping">';
2956 print $form->selectDate($object->date_shipping ? $object->date_shipping : -1, 'ship_', 1, 1, 0, "setdate_shipping", 1, 0);
2957 print '<input type="submit" class="button button-edit smallpaddingimp" value="' . $langs->trans('Modify') . '">';
2958 print '</form>';
2959 } else {
2960 print $object->date_shipping ? dol_print_date($object->date_shipping, 'dayhour') : '&nbsp;';
2961 }
2962 print '</td>';
2963 print '</tr>';
2964
2965 // Weight
2966 print '<tr><td>';
2967 print $form->editfieldkey("Weight", 'trueWeight', $object->trueWeight, $object, $user->hasRight('expedition', 'creer'));
2968 print '</td><td>';
2969
2970 if ($action == 'edittrueWeight') {
2971 print '<form name="settrueweight" action="' . $_SERVER["PHP_SELF"] . '" method="post">';
2972 print '<input name="action" value="settrueWeight" type="hidden">';
2973 print '<input name="id" value="' . $object->id . '" type="hidden">';
2974 print '<input type="hidden" name="token" value="' . newToken() . '">';
2975 print '<input id="trueWeight" name="trueWeight" value="' . $object->trueWeight . '" type="text" class="width50 valignmiddle">';
2976 print $formproduct->selectMeasuringUnits("weight_units", "weight", (string) $object->weight_units, 0, 2, 'maxwidth125 valignmiddle');
2977 print ' <input class="button smallpaddingimp valignmiddle" name="modify" value="' . $langs->trans("Modify") . '" type="submit">';
2978 print ' <input class="button button-cancel smallpaddingimp valignmiddle" name="cancel" value="' . $langs->trans("Cancel") . '" type="submit">';
2979 print '</form>';
2980 } else {
2981 print $object->trueWeight;
2982 print ($object->trueWeight && $object->weight_units != '') ? ' ' . measuringUnitString(0, "weight", $object->weight_units) : '';
2983 }
2984
2985 // Calculated
2986 if ($totalWeight > 0) {
2987 if (!empty($object->trueWeight)) {
2988 print ' (' . $langs->trans("SumOfProductWeights") . ': ';
2989 }
2990 print showDimensionInBestUnit($totalWeight, 0, "weight", $langs, getDolGlobalInt('MAIN_WEIGHT_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_WEIGHT_DEFAULT_UNIT', 'no'));
2991 if (!empty($object->trueWeight)) {
2992 print ')';
2993 }
2994 }
2995 print '</td></tr>';
2996
2997 // Width
2998 print '<tr><td>' . $form->editfieldkey("Width", 'trueWidth', $object->trueWidth, $object, $user->hasRight('expedition', 'creer')) . '</td><td>';
2999 print $form->editfieldval("Width", 'trueWidth', $object->trueWidth, $object, $user->hasRight('expedition', 'creer'));
3000 print ($object->trueWidth && $object->width_units != '') ? ' ' . measuringUnitString(0, "size", $object->width_units) : '';
3001 print '</td></tr>';
3002
3003 // Height
3004 print '<tr><td>' . $form->editfieldkey("Height", 'trueHeight', $object->trueHeight, $object, $user->hasRight('expedition', 'creer')) . '</td><td>';
3005 if ($action == 'edittrueHeight') {
3006 print '<form name="settrueHeight" action="' . $_SERVER["PHP_SELF"] . '" method="post">';
3007 print '<input name="action" value="settrueHeight" type="hidden">';
3008 print '<input name="id" value="' . $object->id . '" type="hidden">';
3009 print '<input type="hidden" name="token" value="' . newToken() . '">';
3010 print '<input id="trueHeight" name="trueHeight" value="' . $object->trueHeight . '" type="text" class="width50">';
3011 print $formproduct->selectMeasuringUnits("size_units", "size", $object->size_units, 0, 2);
3012 print ' <input class="button smallpaddingimp" name="modify" value="' . $langs->trans("Modify") . '" type="submit">';
3013 print ' <input class="button button-cancel smallpaddingimp" name="cancel" value="' . $langs->trans("Cancel") . '" type="submit">';
3014 print '</form>';
3015 } else {
3016 print $object->trueHeight;
3017 print ($object->trueHeight && $object->height_units != '') ? ' ' . measuringUnitString(0, "size", $object->height_units) : '';
3018 }
3019
3020 print '</td></tr>';
3021
3022 // Depth
3023 print '<tr><td>' . $form->editfieldkey("Depth", 'trueDepth', $object->trueDepth, $object, $user->hasRight('expedition', 'creer')) . '</td><td>';
3024 print $form->editfieldval("Depth", 'trueDepth', $object->trueDepth, $object, $user->hasRight('expedition', 'creer'));
3025 print ($object->trueDepth && $object->depth_units != '') ? ' ' . measuringUnitString(0, "size", $object->depth_units) : '';
3026 print '</td></tr>';
3027
3028 // Volume
3029 print '<tr><td>';
3030 print $langs->trans("Volume");
3031 print '</td>';
3032 print '<td>';
3033 $calculatedVolume = 0;
3034 $volumeUnit = 0;
3035 if ($object->trueWidth && $object->trueHeight && $object->trueDepth) {
3036 $calculatedVolume = ($object->trueWidth * $object->trueHeight * $object->trueDepth);
3037 $volumeUnit = $object->size_units * 3;
3038 }
3039 // If sending volume not defined we use sum of products
3040 if ($calculatedVolume > 0) {
3041 if ($volumeUnit < 50) {
3042 print showDimensionInBestUnit($calculatedVolume, $volumeUnit, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'));
3043 } else {
3044 print $calculatedVolume . ' ' . measuringUnitString(0, "volume", $volumeUnit);
3045 }
3046 }
3047 if ($totalVolume > 0) {
3048 if ($calculatedVolume) {
3049 print ' (' . $langs->trans("SumOfProductVolumes") . ': ';
3050 }
3051 print showDimensionInBestUnit($totalVolume, 0, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'));
3052 //if (empty($calculatedVolume)) print ' ('.$langs->trans("Calculated").')';
3053 if ($calculatedVolume) {
3054 print ')';
3055 }
3056 }
3057 print "</td>\n";
3058 print '</tr>';
3059
3060 // Other attributes
3061 //$cols = 2;
3062 include DOL_DOCUMENT_ROOT . '/core/tpl/extrafields_view.tpl.php';
3063
3064 print '</table>';
3065
3066 print '</div>';
3067 print '<div class="fichehalfright">';
3068 print '<div class="underbanner clearboth"></div>';
3069
3070 print '<table class="border centpercent tableforfield">';
3071
3072 // Sending method
3073 print '<tr><td>';
3074 print '<table class="nobordernopadding centpercent"><tr><td>';
3075 print $langs->trans('SendingMethod');
3076 print '</td>';
3077
3078 if ($action != 'editshipping_method_id' && $permissiontoadd) {
3079 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>';
3080 }
3081 print '</tr></table>';
3082 print '</td><td>';
3083 if ($action == 'editshipping_method_id') {
3084 print '<form name="setshipping_method_id" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '" method="post">';
3085 print '<input type="hidden" name="token" value="' . newToken() . '">';
3086 print '<input type="hidden" name="action" value="setshipping_method_id">';
3087 $object->fetch_delivery_methods();
3088 print $form->selectarray("shipping_method_id", $object->meths, $object->shipping_method_id, 1, 0, 0, "", 1);
3089 if ($user->admin) {
3090 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
3091 }
3092 print '<input type="submit" class="button button-edit smallpaddingimp" value="' . $langs->trans('Modify') . '">';
3093 print '</form>';
3094 } else {
3095 if ($object->shipping_method_id > 0) {
3096 // Get code using getLabelFromKey
3097 $code = $langs->getLabelFromKey($db, (string) $object->shipping_method_id, 'c_shipment_mode', 'rowid', 'code');
3098 print $langs->trans("SendingMethod" . strtoupper($code));
3099 }
3100 }
3101 print '</td>';
3102 print '</tr>';
3103
3104 // Tracking Number
3105 print '<tr><td class="titlefieldmiddle">' . $form->editfieldkey("TrackingNumber", 'tracking_number', $object->tracking_number, $object, $user->hasRight('expedition', 'creer')) . '</td><td>';
3106 print $form->editfieldval("TrackingNumber", 'tracking_number', $object->tracking_url, $object, $user->hasRight('expedition', 'creer'), 'safehtmlstring', $object->tracking_number);
3107 print '</td></tr>';
3108
3109 // Incoterms
3110 if (isModEnabled('incoterm')) {
3111 print '<tr><td>';
3112 print '<table class="nobordernopadding centpercent"><tr><td>';
3113 print $langs->trans('IncotermLabel');
3114 print '<td><td class="right">';
3115 if ($permissiontoadd) {
3116 print '<a class="editfielda" href="' . DOL_URL_ROOT . '/expedition/card.php?id=' . $object->id . '&action=editincoterm&token=' . newToken() . '">' . img_edit() . '</a>';
3117 } else {
3118 print '&nbsp;';
3119 }
3120 print '</td></tr></table>';
3121 print '</td>';
3122 print '<td>';
3123 if ($action != 'editincoterm') {
3124 print $form->textwithpicto($object->display_incoterms(), $object->label_incoterms, 1);
3125 } else {
3126 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''), $_SERVER['PHP_SELF'] . '?id=' . $object->id);
3127 }
3128 print '</td></tr>';
3129 }
3130
3131 // Other attributes
3132 $parameters = array();
3133 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3134 print $hookmanager->resPrint;
3135
3136 print "</table>";
3137
3138 print '</div>';
3139 print '</div>';
3140
3141 print '<div class="clearboth"></div>';
3142
3143 print dol_get_fiche_end();
3144
3145
3146 /*
3147 * Lines of simple shipment
3148 */
3149 if (!$origin && getDolGlobalString('SHIPMENT_STANDALONE')) {
3150 if (!empty($object->table_element_line)) {
3151 // Show object lines
3152 $result = $object->getLinesArray();
3153
3154 print ' <form name="addproduct" id="addproduct" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.(($action != 'editline') ? '' : '#line_'.GETPOSTINT('lineid')).'" method="POST">
3155 <input type="hidden" name="token" value="' . newToken().'">
3156 <input type="hidden" name="action" value="' . (($action != 'editline') ? 'addline' : 'updateline').'">
3157 <input type="hidden" name="mode" value="">
3158 <input type="hidden" name="page_y" value="">
3159 <input type="hidden" name="id" value="' . $object->id.'">
3160 ';
3161
3162 if (!empty($conf->use_javascript_ajax) && $object->status == 0) {
3163 include DOL_DOCUMENT_ROOT.'/core/tpl/ajaxrow.tpl.php';
3164 }
3165
3166 print '<div class="div-table-responsive-no-min">';
3167 if (!empty($object->lines) || ($object->status == $object::STATUS_DRAFT && $permissiontoadd && $action != 'selectlines' && $action != 'editline')) {
3168 print '<table id="tablelines" class="noborder noshadow" width="100%">';
3169 }
3170
3171 if (!empty($object->lines)) {
3172 $object->printObjectLines($action, $mysoc, null, GETPOSTINT('lineid'), 1, '/expedition/tpl');
3173 }
3174
3175 // Form to add new line
3176 if ($object->status == 0 && $permissiontoadd && $action != 'selectlines') {
3177 if ($action != 'editline') {
3178 // Add products/services form
3179
3180 $parameters = array();
3181 $reshook = $hookmanager->executeHooks('formAddObjectLine', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3182 if ($reshook < 0) {
3183 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3184 }
3185 if (empty($reshook)) {
3186 $object->formAddObjectLine(1, $mysoc, $soc);
3187 }
3188 }
3189 }
3190
3191 if (!empty($object->lines) || ($object->status == $object::STATUS_DRAFT && $permissiontoadd && $action != 'selectlines' && $action != 'editline')) {
3192 print '</table>';
3193 }
3194 print '</div>';
3195
3196 print "</form>\n";
3197 }
3198 }
3199
3200 // Lines of products of origin
3201 if (!empty($object->origin) && $object->origin_id > 0) {
3202 if ($action == 'editline') {
3203 print ' <form name="updateline" id="updateline" action="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&amp;lineid=' . $line_id . '" method="POST">
3204 <input type="hidden" name="token" value="' . newToken() . '">
3205 <input type="hidden" name="action" value="updateline">
3206 <input type="hidden" name="mode" value="">
3207 <input type="hidden" name="id" value="' . $object->id . '">
3208 ';
3209 }
3210 print '<br>';
3211 if (!empty($conf->use_javascript_ajax) && $object->status == Expedition::STATUS_DRAFT && $usercancreate && $num_prod > 1) {
3212 include DOL_DOCUMENT_ROOT . '/core/tpl/ajaxrow.tpl.php';
3213 }
3214
3215 print '<div class="div-table-responsive-no-min">';
3216 print '<table class="noborder centpercent" id="tablelines" >';
3217 print '<thead>';
3218 print '<tr class="liste_titre">';
3219 // Adds a line numbering column
3220 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
3221 print '<td width="5" class="center linecolnum">&nbsp;</td>';
3222 }
3223 // Product/Service
3224 print '<td class="linecoldescription" >' . $langs->trans("Products") . '</td>';
3225 // Qty
3226 print '<td class="center linecolqty">' . $langs->trans("QtyOrdered") . '</td>';
3227 if ($origin_id > 0) {
3228 print '<td class="center linecolqtyinothershipments">' . $langs->trans("QtyInOtherShipments") . '</td>';
3229 }
3230 if ($action == 'editline') {
3231 $editColspan = 3;
3232 if (!isModEnabled('stock')) {
3233 $editColspan--;
3234 }
3235 if (!isModEnabled('productbatch')) {
3236 $editColspan--;
3237 }
3238 print '<td class="center linecoleditlineotherinfo" colspan="' . $editColspan . '">';
3239 if ($object->status <= 1) {
3240 print $langs->trans("QtyToShip");
3241 } else {
3242 print $langs->trans("QtyShipped");
3243 }
3244 if (isModEnabled('stock')) {
3245 print ' - ' . $langs->trans("WarehouseSource");
3246 }
3247 if (isModEnabled('productbatch')) {
3248 print ' - ' . $langs->trans("Batch");
3249 }
3250 print '</td>';
3251 } else {
3252 if ($object->status <= 1) {
3253 print '<td class="center linecolqtytoship">' . $langs->trans("QtyToShip") . '</td>';
3254 } else {
3255 print '<td class="center linecolqtyshipped">' . $langs->trans("QtyShipped") . '</td>';
3256 }
3257 if (isModEnabled('stock')) {
3258 print '<td class="left linecolwarehousesource">' . $langs->trans("WarehouseSource") . '</td>';
3259 }
3260
3261 if (isModEnabled('productbatch')) {
3262 print '<td class="left linecolbatch">' . $langs->trans("Batch") . '</td>';
3263 }
3264 }
3265 print '<td class="center linecolweight">' . $langs->trans("CalculatedWeight") . '</td>';
3266 print '<td class="center linecolvolume">' . $langs->trans("CalculatedVolume") . '</td>';
3267 //print '<td class="center">'.$langs->trans("Size").'</td>';
3268 if ($object->status == 0) {
3269 print '<td class="linecoledit"></td>';
3270 print '<td class="linecoldelete" width="10"></td>';
3271 print '<td class="linecolmove"></td>';
3272 }
3273 print "</tr>\n";
3274 print '</thead>';
3275
3276 $outputlangs = $langs;
3277
3278 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
3279 $object->fetch_thirdparty();
3280 $newlang = '';
3281 if (/* empty($newlang) && */GETPOST('lang_id', 'aZ09')) {
3282 $newlang = GETPOST('lang_id', 'aZ09');
3283 }
3284 if (empty($newlang)) {
3285 $newlang = $object->thirdparty->default_lang;
3286 }
3287 if (!empty($newlang)) {
3288 $outputlangs = new Translate("", $conf);
3289 $outputlangs->setDefaultLang($newlang);
3290 }
3291 }
3292
3293 $origin = (string) $origin;
3294 if (empty($origin) || $origin == 'order') {
3295 $origin = 'commande';
3296 }
3297
3298 // List of allowed value of $origin
3299 if (!in_array($origin, array('supplier_proposal', 'supplier_order', 'commande_fournisseur', 'facture_fourn', 'propal', 'commande', 'facture'))) {
3300 dol_print_error($db, 'Bad value for parameter origin in expedition/card.php');
3301 exit;
3302 }
3303
3304 // Get list of products already sent for same source object into $alreadysent
3305 $alreadysent = array();
3306 if ($origin_id > 0) {
3307 $tablenametouse = (($origin == 'supplier_order') ? 'commande_fournisseur' : (($origin == 'facture_fourn') ? 'facture_fourn_' : $origin));
3308
3309 $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";
3310 $sql .= ", ed.rowid as shipmentline_id, ed.qty as qty_shipped, ed.fk_expedition as expedition_id, ed.fk_elementdet, ed.fk_entrepot";
3311 $sql .= ", e.rowid as shipment_id, e.ref as shipment_ref, e.date_creation, e.date_valid, e.date_delivery, e.date_expedition";
3312 $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';
3313 $sql .= ', p.description as product_desc';
3314 $sql .= " FROM " . MAIN_DB_PREFIX . "expeditiondet as ed";
3315 $sql .= ", " . MAIN_DB_PREFIX . "expedition as e";
3316 $sql .= ", " . MAIN_DB_PREFIX . $db->sanitize($tablenametouse) . "det as obj";
3317 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "product as p ON obj.fk_product = p.rowid";
3318 $sql .= " WHERE e.entity IN (" . getEntity('expedition') . ")";
3319 $sql .= " AND obj.fk_" . $db->sanitize((string) $origin) . " = " . ((int) $origin_id);
3320 $sql .= " AND obj.rowid = ed.fk_elementdet";
3321 $sql .= " AND ed.fk_expedition = e.rowid";
3322 //if ($filter) $sql.= $filter;
3323 $sql .= " ORDER BY obj.fk_product";
3324
3325 dol_syslog("expedition/card.php get list of shipment lines", LOG_DEBUG);
3326 $resql = $db->query($sql);
3327 if ($resql) {
3328 $num = $db->num_rows($resql);
3329 $i = 0;
3330
3331 while ($i < $num) {
3332 $obj = $db->fetch_object($resql);
3333 if ($obj) {
3334 // $obj->rowid is rowid in $origin."det" table
3335 $alreadysent[$obj->rowid][$obj->shipmentline_id] = array(
3336 'shipment_ref' => $obj->shipment_ref,
3337 'shipment_id' => $obj->shipment_id,
3338 'warehouse' => $obj->fk_entrepot,
3339 'qty_shipped' => $obj->qty_shipped,
3340 'product_tosell' => $obj->product_tosell,
3341 'product_tobuy' => $obj->product_tobuy,
3342 'product_tobatch' => $obj->product_tobatch,
3343 'date_valid' => $db->jdate($obj->date_valid),
3344 'date_delivery' => $db->jdate($obj->date_delivery));
3345 }
3346 $i++;
3347 }
3348 }
3349 //var_dump($alreadysent);
3350 }
3351
3352 print '<tbody>';
3353
3354 // Loop on each product to send/sent
3355 $conf->cache['product'] = array();
3356 $conf->cache['warehouse'] = array();
3357 for ($i = 0; $i < $num_prod; $i++) {
3358 $parameters = array('i' => $i, 'line' => $lines[$i], 'line_id' => $line_id, 'num' => $num_prod, 'alreadysent' => $alreadysent, 'editColspan' => !empty($editColspan) ? $editColspan : 0, 'outputlangs' => $outputlangs);
3359 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $object, $action);
3360 if ($reshook < 0) {
3361 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
3362 }
3363
3364 if (empty($reshook) && $lines[$i]->product_type != "9") {
3365 print '<!-- origin line id = ' . $lines[$i]->origin_line_id . ' -->'; // id of order line
3366 print '<tr class="oddeven" id="row-' . $lines[$i]->id . '" data-id="' . $lines[$i]->id . '" data-element="' . $lines[$i]->element . '" >';
3367 $line = $lines[$i];
3368 $line->fetch_optionals();
3369
3370 // #
3371 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
3372 print '<td class="center linecolnum">' . ($i + 1) . '</td>';
3373 }
3374
3375 // Predefined product or service
3376 if ($lines[$i]->fk_product > 0) {
3377 // Define output language
3378 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
3379 $product_id = $lines[$i]->fk_product;
3380 if (!isset($conf->cache['product'][$product_id])) {
3381 $prod = new Product($db);
3382 $prod->fetch($product_id);
3383 $conf->cache['product'][$product_id] = $prod;
3384 } else {
3385 $prod = $conf->cache['product'][$product_id];
3386 }
3387 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $lines[$i]->product_label;
3388 } else {
3389 $label = (!empty($lines[$i]->label) ? $lines[$i]->label : $lines[$i]->product_label);
3390 }
3391
3392 print '<td class="linecoldescription">';
3393
3394 // Show product and description
3395 $product_static->type = $lines[$i]->fk_product_type;
3396 $product_static->id = $lines[$i]->fk_product;
3397 $product_static->ref = $lines[$i]->ref;
3398 $product_static->status = $lines[$i]->product_tosell;
3399 $product_static->status_buy = $lines[$i]->product_tobuy;
3400 $product_static->status_batch = $lines[$i]->product_tobatch;
3401
3402 $product_static->weight = $lines[$i]->weight;
3403 $product_static->weight_units = $lines[$i]->weight_units;
3404 $product_static->length = $lines[$i]->length;
3405 $product_static->length_units = $lines[$i]->length_units;
3406 $product_static->width = !empty($lines[$i]->width) ? $lines[$i]->width : 0;
3407 $product_static->width_units = !empty($lines[$i]->width_units) ? $lines[$i]->width_units : 0;
3408 $product_static->height = !empty($lines[$i]->height) ? $lines[$i]->height : 0;
3409 $product_static->height_units = !empty($lines[$i]->height_units) ? $lines[$i]->height_units : 0;
3410 $product_static->surface = $lines[$i]->surface;
3411 $product_static->surface_units = $lines[$i]->surface_units;
3412 $product_static->volume = $lines[$i]->volume;
3413 $product_static->volume_units = $lines[$i]->volume_units;
3414 $product_static->stockable_product = $lines[$i]->stockable_product;
3415
3416 $text = $product_static->getNomUrl(1);
3417 $text .= ' - ' . $label;
3418 $description = (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE') ? '' : dol_htmlentitiesbr($lines[$i]->description));
3419 print $form->textwithtooltip($text, $description, 3, 0, '', (string) $i);
3420 print_date_range(!empty($lines[$i]->date_start) ? $lines[$i]->date_start : '', !empty($lines[$i]->date_end) ? $lines[$i]->date_end : '');
3421 if (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE')) {
3422 print (!empty($lines[$i]->description) && $lines[$i]->description != $lines[$i]->product) ? '<br>' . dol_htmlentitiesbr($lines[$i]->description) : '';
3423 }
3424 $lineExtrafieldsHtml = $line->showOptionals($extrafields, ($action == 'editline' && $line->id == $line_id) ? 'edit' : 'view', array(), '', '', '1', 'line');
3425 if (!empty($lineExtrafieldsHtml)) {
3426 print '<div style="padding-top: 10px" id="extrafield_lines_area_' . $line->id . '" name="extrafield_lines_area_' . $line->id . '">';
3427 print $lineExtrafieldsHtml;
3428 print '</div>';
3429 }
3430 print "</td>\n";
3431 } else {
3432 print '<td class="linecoldescription" >';
3433 if ($lines[$i]->product_type == Product::TYPE_SERVICE) {
3434 $text = img_object($langs->trans('Service'), 'service');
3435 } else {
3436 $text = img_object($langs->trans('Product'), 'product');
3437 }
3438
3439 if (!empty($lines[$i]->label)) {
3440 $text .= ' <strong>' . $lines[$i]->label . '</strong>';
3441 print $form->textwithtooltip($text, $lines[$i]->description, 3, 0, '', (string) $i);
3442 } else {
3443 print $text . ' ' . nl2br($lines[$i]->description);
3444 }
3445
3446 print_date_range($lines[$i]->date_start, $lines[$i]->date_end);
3447 $lineExtrafieldsHtml = $line->showOptionals($extrafields, ($action == 'editline' && $line->id == $line_id) ? 'edit' : 'view', array(), '', '', '1', 'line');
3448 if (!empty($lineExtrafieldsHtml)) {
3449 print '<div style="padding-top: 10px" id="extrafield_lines_area_' . $line->id . '" name="extrafield_lines_area_' . $line->id . '">';
3450 print $lineExtrafieldsHtml;
3451 print '</div>';
3452 }
3453 print "</td>\n";
3454 }
3455
3456 $unit_order = '';
3457 if (getDolGlobalString('PRODUCT_USE_UNITS')) {
3458 $unit_order = measuringUnitString((int) $lines[$i]->fk_unit);
3459 }
3460
3461 // Qty ordered
3462 print '<td class="center linecolqty">' . $lines[$i]->qty_asked . ' ' . $unit_order . '</td>';
3463
3464 // Qty in other shipments (with shipment and warehouse used)
3465 if ($origin_id > 0) {
3466 print '<td class="linecolqtyinothershipments center nowrap">';
3467 $htmltooltip = '';
3468 $qtyalreadysent = 0;
3469 foreach ($alreadysent as $key => $val) {
3470 if ($lines[$i]->fk_elementdet == $key) {
3471 $j = 0;
3472 foreach ($val as $shipmentline_id => $shipmentline_var) {
3473 if ($shipmentline_var['shipment_id'] == $lines[$i]->fk_expedition) {
3474 continue; // We want to show only "other shipments"
3475 }
3476
3477 $j++;
3478 if ($j > 1) {
3479 $htmltooltip .= '<br>';
3480 }
3481 $shipment_static->fetch($shipmentline_var['shipment_id']);
3482 $htmltooltip .= $shipment_static->getNomUrl(1, '', 0, 0, 1);
3483 $htmltooltip .= ' - ' . $shipmentline_var['qty_shipped'];
3484 $htmltooltip .= ' - ' . $langs->trans("DateValidation") . ' : ' . (empty($shipmentline_var['date_valid']) ? $langs->trans("Draft") : dol_print_date($shipmentline_var['date_valid'], 'dayhour'));
3485 /*if (isModEnabled('stock') && $shipmentline_var['warehouse'] > 0) {
3486 $warehousestatic->fetch($shipmentline_var['warehouse']);
3487 $htmltext .= '<br>'.$langs->trans("FromLocation").' : '.$warehousestatic->getNomUrl(1, '', 0, 1);
3488 }*/
3489 //print ' '.$form->textwithpicto('', $htmltext, 1);
3490
3491 $qtyalreadysent += $shipmentline_var['qty_shipped'];
3492 }
3493 if ($j) {
3494 $htmltooltip = $langs->trans("QtyInOtherShipments") . '...<br><br>' . $htmltooltip . '<br><input type="submit" name="dummyhiddenbuttontogetfocus" style="display:none" autofocus>';
3495 }
3496 }
3497 }
3498 print $form->textwithpicto((string) $qtyalreadysent, $htmltooltip, 1, 'info', '', 0, 3, 'tooltip' . $lines[$i]->id);
3499 print '</td>';
3500 }
3501
3502 if ($action == 'editline' && $lines[$i]->id == $line_id) {
3503 // edit mode
3504 print '<td colspan="' . $editColspan . '" class="center"><table class="nobordernopadding centpercent">';
3505 if (is_array($lines[$i]->detail_batch) && count($lines[$i]->detail_batch) > 0) {
3506 print '<!-- case edit 1 -->';
3507 $line = new ExpeditionLigne($db);
3508 foreach ($lines[$i]->detail_batch as $detail_batch) {
3509 print '<tr>';
3510 // Qty to ship or shipped
3511 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>';
3512 // Batch number management
3513 if ($lines[$i]->entrepot_id == 0) {
3514 // only show lot numbers from src warehouse when shipping from multiple warehouses
3515 $line->fetch($detail_batch->fk_expeditiondet);
3516 }
3517 $entrepot_id = !empty($detail_batch->entrepot_id) ? $detail_batch->entrepot_id : $lines[$i]->entrepot_id;
3518 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>';
3519 print '</tr>';
3520 }
3521 // add a 0 qty lot row to be able to add a lot
3522 print '<tr>';
3523 // Qty to ship or shipped
3524 print '<td><input class="qtyl" name="qtyl' . $line_id . '_0" id="qtyl' . $line_id . '_0" type="text" size="4" value="0"></td>';
3525 // Batch number management
3526 print '<td>' . $formproduct->selectLotStock('', 'batchl' . $line_id . '_0', '', 1, 0, $lines[$i]->fk_product) . '</td>';
3527 print '</tr>';
3528 } elseif (isModEnabled('stock')) {
3529 if ($lines[$i]->fk_product > 0) {
3530 if ($lines[$i]->entrepot_id > 0) {
3531 print '<!-- case edit 2 -->';
3532 print '<tr>';
3533 // Qty to ship or shipped
3534 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>';
3535 // Warehouse source
3536 print '<td>' . $formproduct->selectWarehouses($lines[$i]->entrepot_id, 'entl' . $line_id, '', 1, 0, $lines[$i]->fk_product, '', 1, 0, array(), 'minwidth200') . '</td>';
3537 // Batch number management
3538 print '<td>';
3539 if (isModEnabled('productbatch')) {
3540 print ' - ' . $langs->trans("NA");
3541 }
3542 print '</td>';
3543 print '</tr>';
3544 } elseif (count($lines[$i]->details_entrepot) > 1) {
3545 print '<!-- case edit 3 -->';
3546 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
3547 print '<tr>';
3548 // Qty to ship or shipped
3549 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>';
3550 // Warehouse source
3551 print '<td>' . $formproduct->selectWarehouses($detail_entrepot->entrepot_id, 'entl' . $detail_entrepot->line_id, '', 1, 0, $lines[$i]->fk_product, '', 1, 0, array(), 'minwidth200') . '</td>';
3552 // Batch number management
3553 print '<td>';
3554 if (isModEnabled('productbatch')) {
3555 print ' - ' . $langs->trans("NA");
3556 }
3557 print '</td>';
3558 print '</tr>';
3559 }
3560 } elseif ($lines[$i]->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
3561 print '<!-- case edit 4 -->';
3562 print '<tr>';
3563 // Qty to ship or shipped
3564 print '<td><input class="qtyl right" name="qtyl' . $line_id . '" id="qtyl' . $line_id . '" type="text" size="4" value="' . $lines[$i]->qty_shipped . '"></td>';
3565 print '<td><span class="opacitymedium">(' . $langs->trans("Service") . ')</span></td>';
3566 print '<td></td>';
3567 print '</tr>';
3568 } else {
3569 print '<!-- case edit 5 -->';
3570 print '<tr><td colspan="3">' . $langs->trans("ErrorStockIsNotEnough") . '</td></tr>';
3571 }
3572 } else {
3573 print '<!-- case edit 6 -->';
3574 print '<tr>';
3575 // Qty to ship or shipped
3576 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>';
3577 // Warehouse source
3578 print '<td></td>';
3579 // Batch number management
3580 print '<td></td>';
3581 print '</tr>';
3582 }
3583 } else { // both product batch and stock are not activated.
3584 print '<!-- case edit 7 -->';
3585 print '<tr>';
3586 // Qty to ship or shipped
3587 print '<td><input class="qtyl right" name="qtyl' . $line_id . '" id="qtyl' . $line_id . '" type="text" size="4" value="' . $lines[$i]->qty_shipped . '"></td>';
3588 // Warehouse source
3589 print '<td></td>';
3590 // Batch number management
3591 print '<td></td>';
3592 print '</tr>';
3593 }
3594
3595 print '</table></td>';
3596 } else {
3597 // Qty to ship or shipped
3598 print '<td class="linecolqtytoship center">' . $lines[$i]->qty_shipped . ' ' . $unit_order . '</td>';
3599
3600 // Warehouse source
3601 if (isModEnabled('stock')) {
3602 print '<td class="linecolwarehousesource tdoverflowmax200">';
3603 if ($lines[$i]->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
3604 print '<span class="opacitymedium">(' . $langs->trans("Service") . ')</span>';
3605 } elseif ($lines[$i]->entrepot_id > 0 && $lines[$i]->stockable_product == Product::ENABLED_STOCK) {
3606 $warehouse_id = $lines[$i]->entrepot_id;
3607 if (!isset($conf->cache['warehouse'][$warehouse_id])) {
3608 $warehouse = new Entrepot($db);
3609 $warehouse->fetch($warehouse_id);
3610 $conf->cache['warehouse'][$warehouse_id] = $warehouse;
3611 } else {
3612 $warehouse = $conf->cache['warehouse'][$warehouse_id];
3613 }
3614 print $warehouse->getNomUrl(1);
3615 } elseif (count($lines[$i]->details_entrepot) > 1) {
3616 $detail = '';
3617 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
3618 $warehouse_id = $detail_entrepot->entrepot_id;
3619 if ($warehouse_id > 0) {
3620 if (!isset($conf->cache['warehouse'][$warehouse_id])) {
3621 $warehouse = new Entrepot($db);
3622 $warehouse->fetch($warehouse_id);
3623 $conf->cache['warehouse'][$warehouse_id] = $warehouse;
3624 } else {
3625 $warehouse = $conf->cache['warehouse'][$warehouse_id];
3626 }
3627 $detail .= $langs->trans("DetailWarehouseFormat", $warehouse->label, $detail_entrepot->qty_shipped) . '<br>';
3628 }
3629 }
3630 print $form->textwithtooltip(img_picto('', 'object_stock') . ' ' . $langs->trans("DetailWarehouseNumber"), $detail);
3631 } elseif (count($lines[$i]->detail_children ?? []) > 1) {
3632 $detail = '';
3633 foreach ($lines[$i]->detail_children as $child_product_id => $child_stock_list) {
3634 foreach ($child_stock_list as $warehouse_id => $total_qty) {
3635 // get product from cache
3636 $child_product_label = '';
3637 if (!isset($conf->cache['product'][$child_product_id])) {
3638 $child_product = new Product($db);
3639 $child_product->fetch($child_product_id);
3640 $conf->cache['product'][$child_product_id] = $child_product;
3641 } else {
3642 $child_product = $conf->cache['product'][$child_product_id];
3643 }
3644 $child_product_label = $child_product->ref . ' ' . $child_product->label;
3645
3646 // get warehouse from cache
3647 if (!isset($conf->cache['warehouse'][$warehouse_id])) {
3648 $child_warehouse = new Entrepot($db);
3649 $child_warehouse->fetch($warehouse_id);
3650 $conf->cache['warehouse'][$warehouse_id] = $child_warehouse;
3651 } else {
3652 $child_warehouse = $conf->cache['warehouse'][$warehouse_id];
3653 }
3654 $detail .= $langs->trans('DetailChildrenFormat', $child_product_label, $child_warehouse->label, price2num($total_qty, 'MS')) . '<br>';
3655 }
3656 }
3657 print $form->textwithtooltip(img_picto('', 'object_stock') . ' ' . $langs->trans('DetailWarehouseNumber'), $detail);
3658 }
3659 print '</td>';
3660 }
3661
3662 // Batch number management
3663 if (isModEnabled('productbatch')) {
3664 if (isset($lines[$i]->detail_batch)) {
3665 print '<!-- Detail of lot -->';
3666 print '<td class="linecolbatch">';
3667 if ($lines[$i]->product_tobatch) {
3668 $detail = '';
3669 foreach ($lines[$i]->detail_batch as $dbatch) { // $dbatch is instance of ExpeditionLineBatch
3670 $detail .= $langs->trans("Batch") . ': ' . $dbatch->batch;
3671 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY')) {
3672 $detail .= ' - ' . $langs->trans("SellByDate") . ': ' . dol_print_date($dbatch->sellby, "day");
3673 }
3674 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY')) {
3675 $detail .= ' - ' . $langs->trans("EatByDate") . ': ' . dol_print_date($dbatch->eatby, "day");
3676 }
3677 $detail .= ' - ' . $langs->trans("Qty") . ': ' . $dbatch->qty;
3678 $detail .= '<br>';
3679 }
3680 print $form->textwithtooltip(img_picto('', 'object_barcode') . ' ' . $langs->trans("DetailBatchNumber"), $detail);
3681 } else {
3682 print $langs->trans("NA");
3683 }
3684 print '</td>';
3685 } else {
3686 print '<td class="linecolbatch" ></td>';
3687 }
3688 }
3689 }
3690
3691 // Weight
3692 print '<td class="center linecolweight">';
3693 if ($lines[$i]->fk_product_type == Product::TYPE_PRODUCT) {
3694 print $lines[$i]->weight * $lines[$i]->qty_shipped . ' ' . measuringUnitString(0, "weight", $lines[$i]->weight_units);
3695 } else {
3696 print '&nbsp;';
3697 }
3698 print '</td>';
3699
3700 // Volume
3701 print '<td class="center linecolvolume">';
3702 if ($lines[$i]->fk_product_type == Product::TYPE_PRODUCT) {
3703 print $lines[$i]->volume * $lines[$i]->qty_shipped . ' ' . measuringUnitString(0, "volume", $lines[$i]->volume_units);
3704 } else {
3705 print '&nbsp;';
3706 }
3707 print '</td>';
3708
3709 // Size
3710 //print '<td class="center">'.$lines[$i]->volume*$lines[$i]->qty_shipped.' '.measuringUnitString(0, "volume", $lines[$i]->volume_units).'</td>';
3711
3712 $showmovecol = ($object->status == Expedition::STATUS_DRAFT);
3713
3714 if ($action == 'editline' && $lines[$i]->id == $line_id) {
3715 print '<td class="center" colspan="2" valign="middle">';
3716 print '<input type="submit" class="button button-save" id="savelinebutton marginbottomonly" name="save" value="' . $langs->trans("Save") . '"><br>';
3717 print '<input type="submit" class="button button-cancel" id="cancellinebutton" name="cancel" value="' . $langs->trans("Cancel") . '"><br>';
3718 print '</td>';
3719 if ($showmovecol) {
3720 print '<td class="linecolmove tdlineupdown center"></td>';
3721 }
3722 } elseif ($showmovecol) {
3723 $edit_url = $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=editline&token=' . newToken() . '&lineid=' . $lines[$i]->id;
3724 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
3725 $product_id = $lines[$i]->fk_product;
3726 if (!isset($conf->cache['product'][$product_id])) {
3727 $product = new Product($db);
3728 $product->fetch($product_id);
3729 $conf->cache['product'][$product_id] = $product;
3730 } else {
3731 $product = $conf->cache['product'][$product_id];
3732 }
3733
3734 if ($product->hasFatherOrChild(1)) {
3735 $edit_url = dol_buildpath('/expedition/dispatch.php?id=' . $object->id, 1);
3736 }
3737 }
3738
3739 // edit-delete buttons
3740 print '<td class="linecoledit center">';
3741 print '<a class="editfielda reposition" href="' . $edit_url . '">' . img_edit() . '</a>';
3742 print '</td>';
3743 print '<td class="linecoldelete" width="10">';
3744 print '<a class="reposition" href="' . $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=deleteline&token=' . newToken() . '&lineid=' . $lines[$i]->id . '">' . img_delete() . '</a>';
3745 print '</td>';
3746 print '<td class="linecolmove tdlineupdown center">';
3747 if ($usercancreate && $num_prod > 1) {
3748 if ($i > 0) {
3749 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>';
3750 }
3751 if ($i < $num_prod - 1) {
3752 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>';
3753 }
3754 }
3755 print '</td>';
3756 // Display lines extrafields
3757 if (!empty($rowExtrafieldsStart)) {
3758 print $rowExtrafieldsStart;
3759 print $rowExtrafieldsView;
3760 print $rowEnd;
3761 }
3762 }
3763 print "</tr>";
3764 } elseif (empty($reshook) && $lines[$i]->product_type == "9") {
3765 $objectsrc = new OrderLine($db);
3766 $objectsrc->fetch($lines[$i]->origin_line_id);
3767 if ($objectsrc->special_code == SUBTOTALS_SPECIAL_CODE) {
3768 $line = $lines[$i];
3769 require dol_buildpath('/core/tpl/subtotal_expedition_view.tpl.php');
3770 }
3771 }
3772 }
3773 // TODO Show also lines ordered but not delivered
3774 if (empty($num_prod)) {
3775 print '<tr><td colspan="8"><span class="opacitymedium">' . $langs->trans("NoLineGoOnTabToAddSome", $langs->transnoentitiesnoconv("ShipmentDistribution")) . '</span></td></tr>';
3776 }
3777
3778 print '</tbody>';
3779 print "</table>\n";
3780 print '</div>';
3781 if ($action == 'editline') {
3782 print "</form>\n";
3783 }
3784
3785 $object->fetchObjectLinked($object->id, $object->element);
3786 }
3787
3788 /*
3789 * Boutons actions
3790 */
3791
3792 if (($user->socid == 0) && ($action != 'presend')) {
3793 print '<div class="tabsAction">';
3794
3795 $parameters = array();
3796 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
3797 // modified by hook
3798 if (empty($reshook)) {
3799 if ($object->status == Expedition::STATUS_DRAFT && $num_prod > 0) {
3800 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'creer'))
3801 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'shipping_advance', 'validate'))
3802 ) {
3803 print dolGetButtonAction('', $langs->trans('Validate'), 'default', $_SERVER["PHP_SELF"] . '?action=valid&token=' . newToken() . '&id=' . $object->id, '');
3804 } else {
3805 print dolGetButtonAction($langs->trans('NotAllowed'), $langs->trans('Validate'), 'default', $_SERVER['PHP_SELF'] . '#', '', false);
3806 }
3807 }
3808
3809 // 0=draft, 1=validated/delivered, 2=closed/delivered
3810 if ($object->status == Expedition::STATUS_VALIDATED && !getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) {
3811 if ($user->hasRight('expedition', 'creer')) {
3812 print dolGetButtonAction('', $langs->trans('SetToDraft'), 'default', $_SERVER["PHP_SELF"] . '?action=setdraft&token=' . newToken() . '&id=' . $object->id, '');
3813 }
3814 }
3815 if ($object->status == Expedition::STATUS_CLOSED) {
3816 if ($user->hasRight('expedition', 'creer')) {
3817 print dolGetButtonAction('', $langs->trans('ReOpen'), 'default', $_SERVER["PHP_SELF"] . '?action=reopen&token=' . newToken() . '&id=' . $object->id, '');
3818 }
3819 }
3820
3821 // Send
3822 if (empty($user->socid)) {
3823 if ($object->status > 0) {
3824 if (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') || $user->hasRight('expedition', 'shipping_advance', 'send')) {
3825 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER["PHP_SELF"] . '?action=presend&token=' . newToken() . '&id=' . $object->id . '&mode=init#formmailbeforetitle', '');
3826 } else {
3827 print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER['PHP_SELF'] . '#', '', false);
3828 }
3829 }
3830 }
3831
3832 // This is just to generate a delivery receipt when option to do this is on
3833 //var_dump($object->linkedObjectsIds['delivery']);
3834 if (getDolGlobalInt('MAIN_SUBMODULE_DELIVERY') && ($object->status == Expedition::STATUS_VALIDATED || $object->status == Expedition::STATUS_CLOSED) && $user->hasRight('expedition', 'delivery', 'creer') && empty($object->linkedObjectsIds['delivery'])) {
3835 print dolGetButtonAction('', $langs->trans('CreateDeliveryOrder'), 'default', $_SERVER["PHP_SELF"] . '?action=create_delivery&token=' . newToken() . '&id=' . $object->id, '');
3836 }
3837
3838 // Sign (to set to status "Signed" without using the online signature page)
3839 if ($object->status > Expedition::STATUS_DRAFT) {
3840 if ($object->signed_status != Expedition::$SIGNED_STATUSES['STATUS_SIGNED_ALL']) {
3841 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>';
3842 } else {
3843 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>';
3844 }
3845 }
3846
3847 // Create bill
3848 if (isModEnabled('invoice') && ($object->status == Expedition::STATUS_VALIDATED || $object->status == Expedition::STATUS_CLOSED)) {
3849 if ($user->hasRight('facture', 'creer')) {
3850 if (getDolGlobalString('WORKFLOW_BILL_ON_SHIPMENT') !== '0') {
3851 print dolGetButtonAction('', $langs->trans('CreateBill'), 'default', DOL_URL_ROOT . '/compta/facture/card.php?action=create&origin=' . $object->element . '&originid=' . $object->id . '&socid=' . $object->socid, '');
3852 }
3853 }
3854 }
3855
3856 // Set Billed and Closed
3857 if ($object->status == Expedition::STATUS_VALIDATED) {
3858 if ($user->hasRight('expedition', 'creer') && $object->status > 0) {
3859 if (!$object->billed && getDolGlobalString('WORKFLOW_BILL_ON_SHIPMENT') !== '0') {
3860 print dolGetButtonAction('', $langs->trans('ClassifyBilled'), 'default', $_SERVER["PHP_SELF"] . '?action=classifybilled&token=' . newToken() . '&id=' . $object->id, '');
3861 }
3862 print dolGetButtonAction('', $langs->trans("Close"), 'default', $_SERVER["PHP_SELF"] . '?action=classifyclosed&token=' . newToken() . '&id=' . $object->id, '');
3863 }
3864 }
3865
3866 // Cancel
3867 if ($object->status == Expedition::STATUS_VALIDATED) {
3868 if ($user->hasRight('expedition', 'creer')) {
3869 print dolGetButtonAction('', $langs->trans('Cancel'), 'danger', $_SERVER["PHP_SELF"] . '?action=cancel&token=' . newToken() . '&id=' . $object->id . '&mode=init#formmailbeforetitle', '');
3870 }
3871 }
3872
3873 // Delete
3874 if ($user->hasRight('expedition', 'supprimer')) {
3875 print dolGetButtonAction('', $langs->trans('Delete'), 'delete', $_SERVER["PHP_SELF"] . '?action=delete&token=' . newToken() . '&id=' . $object->id, '');
3876 }
3877 }
3878
3879 print '</div>';
3880 }
3881
3882
3883 /*
3884 * Documents generated
3885 */
3886
3887 if ($action != 'presend' && $action != 'editline') {
3888 print '<div class="fichecenter"><div class="fichehalfleft">';
3889
3890 $objectref = dol_sanitizeFileName((string) $object->ref);
3891 $filedir = $conf->expedition->dir_output . "/sending/" . $objectref;
3892
3893 $urlsource = $_SERVER["PHP_SELF"] . "?id=" . $object->id;
3894
3895 $genallowed = $user->hasRight('expedition', 'lire');
3896 $delallowed = $user->hasRight('expedition', 'creer');
3897
3898 print $formfile->showdocuments('expedition', $objectref, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', '', '', $soc->default_lang);
3899
3900
3901 // Show links to link elements
3902 $tmparray = $form->showLinkToObjectBlock($object, array(), array('shipping'), 1);
3903 $linktoelem = $tmparray['linktoelem'];
3904 $htmltoenteralink = $tmparray['htmltoenteralink'];
3905 print $htmltoenteralink;
3906
3907 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
3908
3909 // Show online signature link
3910 $useonlinesignature = getDolGlobalInt('EXPEDITION_ALLOW_ONLINESIGN');
3911
3912 if ($object->statut != Expedition::STATUS_DRAFT && $useonlinesignature) {
3913 print '<br><!-- Link to sign -->';
3914 require_once DOL_DOCUMENT_ROOT . '/core/lib/signature.lib.php';
3915 print showOnlineSignatureUrl('expedition', $object->ref, $object) . '<br>';
3916 }
3917
3918 print '</div><div class="fichehalfright">';
3919
3920
3921 // List of actions on element
3922 include_once DOL_DOCUMENT_ROOT . '/core/class/html.formactions.class.php';
3923 $formactions = new FormActions($db);
3924
3925 //button to go to messaging from the events box
3926 $MAXEVENT = 10;
3927 $morehtmlcenter = dolGetButtonTitle($langs->trans('FullConversation'), '', 'fa fa-comments imgforviewmode', DOL_URL_ROOT . '/expedition/messaging.php?id=' . $object->id);
3928
3929 $somethingshown = $formactions->showactions($object, 'shipping', $socid, 1, '', $MAXEVENT, '', $morehtmlcenter);
3930 print '</div></div>';
3931 }
3932
3933
3934 /*
3935 * Action presend
3936 */
3937
3938 //Select mail models is same action as presend
3939 if (GETPOST('modelselected')) {
3940 $action = 'presend';
3941 }
3942
3943 // Presend form
3944 $modelmail = 'shipping_send';
3945 $defaulttopic = 'SendShippingRef';
3946 $diroutput = $conf->expedition->dir_output . '/sending';
3947 $trackid = 'shi' . $object->id;
3948
3949 include DOL_DOCUMENT_ROOT . '/core/tpl/card_presend.tpl.php';
3950}
3951
3952// End of page
3953llxFooter();
3954$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:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
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...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
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.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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.
dolBuildUrl($url, $params=[], $addtoken=false, $anchor='')
Return path of url.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
img_down($titlealt='default', $selected=0, $moreclass='')
Show down arrow logo.
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.
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
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.
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
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...
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
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.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
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.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
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_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_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
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.