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