dolibarr 22.0.5
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-2025 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-2025 MDW <mdeweerd@users.noreply.github.com>
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 3 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <https://www.gnu.org/licenses/>.
31 */
32
39// Load Dolibarr environment
40require '../main.inc.php';
41require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
42require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
43require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
44require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
45require_once DOL_DOCUMENT_ROOT.'/core/lib/sendings.lib.php';
46require_once DOL_DOCUMENT_ROOT.'/core/modules/expedition/modules_expedition.php';
47require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
48require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
49require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
50require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
51require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
52if (isModEnabled("product") || isModEnabled("service")) {
53 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
54}
55if (isModEnabled("propal")) {
56 require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php';
57}
58if (isModEnabled('productbatch')) {
59 require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php';
60}
61if (isModEnabled('project')) {
62 require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
63 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formprojet.class.php';
64}
65
74// Load translation files required by the page
75$langs->loadLangs(array("sendings", "companies", "bills", 'deliveries', 'orders', 'stocks', 'other', 'propal', 'productbatch'));
76
77if (isModEnabled('incoterm')) {
78 $langs->load('incoterm');
79}
80if (isModEnabled('productbatch')) {
81 $langs->load('productbatch');
82}
83
84$origin = GETPOST('origin', 'alpha') ? GETPOST('origin', 'alpha') : 'expedition'; // Example: commande, propal
85$origin_id = GETPOSTINT('id') ? GETPOSTINT('id') : '';
86$id = $origin_id;
87if (empty($origin_id)) {
88 $origin_id = GETPOSTINT('origin_id'); // Id of order or propal
89}
90if (empty($origin_id)) {
91 $origin_id = GETPOSTINT('object_id'); // Id of order or propal
92}
93$ref = GETPOST('ref', 'alpha');
94$line_id = GETPOSTINT('lineid');
95$facid = GETPOSTINT('facid');
96
97$action = GETPOST('action', 'alpha');
98$confirm = GETPOST('confirm', 'alpha');
99$cancel = GETPOST('cancel', 'alpha');
100
101//PDF
102$hidedetails = (GETPOSTINT('hidedetails') ? GETPOSTINT('hidedetails') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS') ? 1 : 0));
103$hidedesc = (GETPOSTINT('hidedesc') ? GETPOSTINT('hidedesc') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_DESC') ? 1 : 0));
104$hideref = (GETPOSTINT('hideref') ? GETPOSTINT('hideref') : (getDolGlobalString('MAIN_GENERATE_DOCUMENTS_HIDE_REF') ? 1 : 0));
105
106$object = new Expedition($db);
107$objectorder = new Commande($db);
108$extrafields = new ExtraFields($db);
109
110// fetch optionals attributes and labels
111$extrafields->fetch_name_optionals_label($object->table_element);
112$extrafields->fetch_name_optionals_label($object->table_element_line);
113$extrafields->fetch_name_optionals_label($objectorder->table_element_line);
114
115// Load object. Make an object->fetch
116include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be 'include', not 'include_once'
117
118// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
119$hookmanager->initHooks(array('expeditioncard', 'globalcard'));
120
121$date_delivery = dol_mktime(GETPOSTINT('date_deliveryhour'), GETPOSTINT('date_deliverymin'), 0, GETPOSTINT('date_deliverymonth'), GETPOSTINT('date_deliveryday'), GETPOSTINT('date_deliveryyear'));
122
123$date_shipping = dol_mktime(GETPOSTINT('date_shippinghour'), GETPOSTINT('date_shippingmin'), 0, GETPOSTINT('date_shippingmonth'), GETPOSTINT('date_shippingday'), GETPOSTINT('date_shippingyear'));
124
125if ($id > 0 || !empty($ref)) {
126 $object->fetch($id, $ref);
127 $object->fetch_thirdparty();
128}
129
130// Security check
131$socid = '';
132if ($user->socid) {
133 $socid = $user->socid;
134}
135
136$result = restrictedArea($user, 'expedition', $object->id, '');
137
138$permissiondellink = $user->hasRight('expedition', 'delivery', 'creer'); // Used by the include of actions_dellink.inc.php
139$permissiontoadd = $user->hasRight('expedition', 'creer');
140$permissiontoeditextra = $permissiontoadd;
141if (GETPOST('attribute', 'aZ09') && isset($extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')])) {
142 // For action 'update_extras', is there a specific permission set for the attribute to update
143 $permissiontoeditextra = dol_eval((string) $extrafields->attributes[$object->table_element]['perms'][GETPOST('attribute', 'aZ09')]);
144}
145
146$upload_dir = $conf->expedition->dir_output.'/sending';
147
148$editColspan = 0;
149$objectsrc = null;
150$typeobject = null;
151
152
153/*
154 * Actions
155 */
156
157$error = 0;
158$parameters = array();
159$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
160if ($reshook < 0) {
161 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
162}
163
164if (empty($reshook)) {
165 if ($cancel) {
166 if ($origin && $origin_id > 0) {
167 if ($origin == 'commande') {
168 header("Location: ".DOL_URL_ROOT.'/expedition/shipment.php?id='.((int) $origin_id));
169 exit;
170 }
171 } else {
172 $action = '';
173 $object->fetch($id); // show shipment also after canceling modification
174 }
175 }
176
177 include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php'; // Must be 'include', not 'include_once'
178
179 // Actions to build doc
180 include DOL_DOCUMENT_ROOT.'/core/actions_builddoc.inc.php';
181
182 // Back to draft
183 if ($action == 'setdraft' && $permissiontoadd) {
184 $object->fetch($id);
185 $result = $object->setDraft($user, 0);
186 if ($result < 0) {
187 setEventMessages($object->error, $object->errors, 'errors');
188 } else {
189 header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
190 exit;
191 }
192 }
193 // Reopen
194 if ($action == 'reopen' && $permissiontoadd) {
195 $object->fetch($id);
196 $result = $object->reOpen();
197 if ($result < 0) {
198 setEventMessages($object->error, $object->errors, 'errors');
199 } else {
200 header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
201 exit;
202 }
203 }
204
205 // Set incoterm
206 if ($action == 'set_incoterms' && isModEnabled('incoterm') && $permissiontoadd) {
207 $result = $object->setIncoterms(GETPOSTINT('incoterm_id'), GETPOST('location_incoterms'));
208 }
209
210 if ($action == 'setref_customer' && $permissiontoadd) {
211 $result = $object->fetch($id);
212 if ($result < 0) {
213 setEventMessages($object->error, $object->errors, 'errors');
214 }
215
216 $result = $object->setValueFrom('ref_customer', GETPOST('ref_customer', 'alpha'), '', null, 'text', '', $user, 'SHIPPING_MODIFY');
217 if ($result < 0) {
218 setEventMessages($object->error, $object->errors, 'errors');
219 $action = 'editref_customer';
220 } else {
221 header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id);
222 exit;
223 }
224 }
225
226 if ($action == 'update_extras' && $permissiontoeditextra) {
227 $object->oldcopy = dol_clone($object, 2);
228
229 $attribute_name = GETPOST('attribute', 'aZ09');
230
231 // Fill array 'array_options' with data from update form
232 $ret = $extrafields->setOptionalsFromPost(null, $object, $attribute_name);
233 if ($ret < 0) {
234 $error++;
235 }
236
237 if (!$error) {
238 // Actions on extra fields
239 $result = $object->updateExtraField($attribute_name, 'SHIPPING_MODIFY');
240 if ($result < 0) {
241 setEventMessages($object->error, $object->errors, 'errors');
242 $error++;
243 }
244 }
245
246 if ($error) {
247 $action = 'edit_extras';
248 }
249 }
250
251 // Create shipment
252 if ($action == 'add' && $permissiontoadd) {
253 $db->begin();
254
255 $object->origin = $origin; // deprecated
256 $object->origin_type = $origin;
257 $object->origin_id = $origin_id;
258 $object->fk_project = GETPOSTINT('projectid');
259 // Weight and box dimensions are physical measurements that frequently use
260 // decimals (1.6 kg, 0.25 m, ...), so they must be read as floats and not
261 // truncated to int (see issue #34069 for the shipping module).
262 $object->weight = GETPOST('weight', 'alpha') == '' ? "NULL" : GETPOSTFLOAT('weight');
263 $object->sizeH = GETPOST('sizeH', 'alpha') == '' ? "NULL" : GETPOSTFLOAT('sizeH');
264 $object->sizeW = GETPOST('sizeW', 'alpha') == '' ? "NULL" : GETPOSTFLOAT('sizeW');
265 $object->sizeS = GETPOST('sizeS', 'alpha') == '' ? "NULL" : GETPOSTFLOAT('sizeS');
266 $object->size_units = GETPOSTINT('size_units');
267 $object->weight_units = GETPOSTINT('weight_units');
268
269 $product = new Product($db);
270
271 // We will loop on each line of the original document to complete the shipping object with various info and quantity to deliver
272 $classname = ucfirst($object->origin_type);
273 $objectsrc = new $classname($db);
274 '@phan-var-force Facture|Commande $objectsrc';
275 $objectsrc->fetch($object->origin_id);
276
277 $object->socid = $objectsrc->socid;
278 $object->ref_customer = GETPOST('ref_customer', 'alpha');
279 $object->model_pdf = GETPOST('model');
280 $object->date_delivery = $date_delivery; // Date delivery planned
281 $object->date_shipping = $date_shipping; // Sending date
282 $object->fk_delivery_address = $objectsrc->fk_delivery_address;
283 $object->shipping_method_id = GETPOSTINT('shipping_method_id');
284 $object->tracking_number = GETPOST('tracking_number', 'alpha');
285 $object->note = GETPOST('note', 'restricthtml'); // deprecated
286 $object->note_private = GETPOST('note_private', 'restricthtml');
287 $object->note_public = GETPOST('note_public', 'restricthtml');
288 $object->fk_incoterms = GETPOSTINT('incoterm_id');
289 $object->location_incoterms = GETPOST('location_incoterms', 'alpha');
290
291 $batch_line = array();
292 $stockLine = array();
293 $array_options = array();
294
295 $num = count($objectsrc->lines);
296 $totalqty = 0;
297
298 $product_batch_used = array();
299
300 for ($i = 0; $i < $num; $i++) {
301 $idl = "idl".$i;
302
303 $sub_qty = array();
304 $subtotalqty = 0;
305
306 $j = 0;
307
308 $batch = "batchl".$i."_0";
309 $stockLocation = "ent1".$i."_0";
310 $qty = "qtyl".$i;
311
312 $is_batch_or_serial = 0;
313 if (!empty($objectsrc->lines[$i]->fk_product)) {
314 $resultFetch = $product->fetch($objectsrc->lines[$i]->fk_product, '', '', '', 1, 1, 1);
315 if ($resultFetch < 0) {
316 setEventMessages($product->error, $product->errors, 'errors');
317 }
318 $is_batch_or_serial = $product->status_batch;
319 }
320
321 // If product need a batch or serial number
322 if (isModEnabled('productbatch') && $objectsrc->lines[$i]->product_tobatch) {
323 if (GETPOSTISSET($batch)) {
324 //shipment line with batch-enable product
325 $qty .= '_'.$j;
326 while (GETPOSTISSET($batch)) {
327 // save line of detail into sub_qty
328 $sub_qty[$j]['q'] = price2num(GETPOST($qty, 'alpha'), 'MS'); // the qty we want to move for this stock record
329 $sub_qty[$j]['id_batch'] = GETPOSTINT($batch); // the id into llx_product_batch of stock record to move
330 $subtotalqty += $sub_qty[$j]['q'];
331
332 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)))) {
333 setEventMessages($langs->trans("TooManyQtyForSerialNumber", $product->ref, ''), null, 'errors');
334 $totalqty = 0;
335 break 2;
336 }
337
338 if ($is_batch_or_serial == 2 && $sub_qty[$j]['q'] > 0) {
339 // we stock the batch id to test later if the same serial is shipped on another line for the same product
340 $product_batch_used[$j] = $sub_qty[$j]['id_batch'];
341 }
342
343 $j++;
344 $batch = "batchl".$i."_".$j;
345 $qty = "qtyl".$i.'_'.$j;
346 }
347
348 $batch_line[$i]['detail'] = $sub_qty; // array of details
349 $batch_line[$i]['qty'] = $subtotalqty;
350 $batch_line[$i]['ix_l'] = GETPOSTINT($idl);
351
352 $totalqty += $subtotalqty;
353 } else {
354 // No detail were provided for lots, so if a qty was provided, we can throw an error.
355 if (GETPOST($qty)) {
356 // We try to set an amount
357 // Case we don't use the list of available qty for each warehouse/lot
358 // GUI does not allow this yet
359 setEventMessages($langs->trans("StockIsRequiredToChooseWhichLotToUse").' ('.$langs->trans("Line").' '.GETPOSTINT($idl).')', null, 'errors');
360 $error++;
361 }
362 }
363 } elseif (GETPOSTISSET($stockLocation)) {
364 //shipment line from multiple stock locations
365 $qty .= '_'.$j;
366 while (GETPOSTISSET($stockLocation)) {
367 // save sub line of warehouse
368 $stockLine[$i][$j]['qty'] = price2num(GETPOST($qty, 'alpha'), 'MS');
369 $stockLine[$i][$j]['warehouse_id'] = GETPOSTINT($stockLocation);
370 $stockLine[$i][$j]['ix_l'] = GETPOSTINT($idl);
371
372 $totalqty += price2num(GETPOST($qty, 'alpha'), 'MS');
373 $subtotalqty += price2num(GETPOST($qty, 'alpha'), 'MS');
374
375 $j++;
376 $stockLocation = "ent1".$i."_".$j;
377 $qty = "qtyl".$i.'_'.$j;
378 }
379 } else {
380 //shipment line for product with no batch management and no multiple stock location
381 if (GETPOSTFLOAT($qty) > 0) {
382 $totalqty += price2num(GETPOST($qty, 'alpha'), 'MS');
383 $subtotalqty = price2num(GETPOST($qty, 'alpha'), 'MS');
384 }
385 }
386
387 // check qty shipped not greater than ordered
388 if (getDolGlobalInt("MAIN_DONT_SHIP_MORE_THAN_ORDERED") && $subtotalqty > $objectsrc->lines[$i]->qty) {
389 setEventMessages($langs->trans("ErrorTooMuchShipped", $i + 1), null, 'errors');
390 $error++;
391 continue;
392 }
393
394 // Extrafields
395 $array_options[$i] = $extrafields->getOptionalsFromPost($object->table_element_line, (string) $i);
396 // Unset extrafield
397 if (isset($extrafields->attributes[$object->table_element_line]['label']) && is_array($extrafields->attributes[$object->table_element_line]['label'])) {
398 // Get extra fields
399 foreach ($extrafields->attributes[$object->table_element_line]['label'] as $key => $value) {
400 unset($_POST["options_".$key]);
401 }
402 }
403 }
404
405 if (($totalqty > 0 || getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) && !$error) { // There is at least one thing to ship and no error
406 $selected_subtotal_lines = GETPOST('subtotal_toselect', 'array');
407 for ($i = 0; $i < $num; $i++) {
408 $qty = "qtyl".$i;
409
410 if (!isset($batch_line[$i])) {
411 // not batch mode
412 if (isset($stockLine[$i])) {
413 //shipment from multiple stock locations
414 $nbstockline = count($stockLine[$i]);
415 for ($j = 0; $j < $nbstockline; $j++) {
416 if ($stockLine[$i][$j]['qty'] > 0 || ($stockLine[$i][$j]['qty'] == 0 && getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS'))) {
417 $ret = $object->addline($stockLine[$i][$j]['warehouse_id'], $stockLine[$i][$j]['ix_l'], (float) $stockLine[$i][$j]['qty'], $array_options[$i]);
418 if ($ret < 0) {
419 setEventMessages($object->error, $object->errors, 'errors');
420 $error++;
421 }
422 }
423 }
424 } else {
425 if (GETPOSTFLOAT($qty) > 0 || getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
426 $ent = "entl".$i;
427 $idl = "idl".$i;
428 $entrepot_id = is_numeric(GETPOSTINT($ent)) ? GETPOSTINT($ent) : GETPOSTINT('entrepot_id');
429 if ($entrepot_id < 0) {
430 $entrepot_id = '';
431 }
432 if (!($objectsrc->lines[$i]->fk_product > 0)) {
433 $entrepot_id = 0;
434 }
435
436 $ret = $object->addline($entrepot_id, GETPOSTINT($idl), (float) price2num(GETPOSTFLOAT($qty), 'MS'), $array_options[$i]);
437 if ($ret < 0) {
438 setEventMessages($object->error, $object->errors, 'errors');
439 $error++;
440 }
441 }
442 if (isModEnabled('subtotals') && $objectsrc->lines[$i]->special_code == SUBTOTALS_SPECIAL_CODE && in_array($objectsrc->lines[$i]->id, $selected_subtotal_lines)) {
443 $object->addSubtotalLine($langs, $objectsrc->lines[$i]->desc, (int) $objectsrc->lines[$i]->qty, $objectsrc->lines[$i]->extraparams, $objectsrc->lines[$i]->id);
444 }
445 }
446 } else {
447 // batch mode
448 if ($batch_line[$i]['qty'] > 0 || ($batch_line[$i]['qty'] == 0 && getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS'))) {
449 $origin_line_id = (int) $batch_line[$i]['ix_l'];
450 $origin_line = new OrderLine($db);
451 $res = $origin_line->fetch($origin_line_id);
452 if ($res <= 0) {
453 $error++;
454 setEventMessages($origin_line->error, $origin_line->errors, 'errors');
455 }
456 $ret = $object->addline_batch($batch_line[$i], $array_options[$i], $origin_line);
457 if ($ret < 0) {
458 setEventMessages($object->error, $object->errors, 'errors');
459 $error++;
460 }
461 }
462 }
463 }
464 // Fill array 'array_options' with data from add form
465 $ret = $extrafields->setOptionalsFromPost(null, $object);
466 if ($ret < 0) {
467 $error++;
468 }
469
470 if (!$error) {
471 $ret = $object->create($user); // This create shipment (like Odoo picking) and lines of shipments. Stock movement will be done when validating or closing shipment.
472 if ($ret <= 0) {
473 setEventMessages($object->error, $object->errors, 'errors');
474 $error++;
475 }
476 }
477 } elseif (!$error) {
478 $labelfieldmissing = $langs->transnoentitiesnoconv("QtyToShip");
479 if (isModEnabled('stock')) {
480 $labelfieldmissing .= '/'.$langs->transnoentitiesnoconv("Warehouse");
481 }
482 setEventMessages($langs->trans("ErrorFieldRequired", $labelfieldmissing), null, 'errors');
483 $error++;
484 }
485
486 if (!$error) {
487 $db->commit();
488 $object->fetch_lines();
489 foreach ($object->lines as $line) {
490 $objectsrc_line = new $objectsrc->class_element_line($db);
491 '@phan-var-force CommonObjectLine $objectsrc_line';
492 $objectsrc_line->fetch($line->origin_line_id);
493 $line->extraparams = $objectsrc_line->extraparams;
494 $line->setExtraParameters();
495 }
496 header("Location: card.php?id=".$object->id);
497 exit;
498 } else {
499 $db->rollback();
500 //$_GET["commande_id"] = GETPOSTINT('commande_id');
501 $action = 'create';
502 }
503 } elseif ($action == 'create_delivery' && getDolGlobalInt('MAIN_SUBMODULE_DELIVERY') && $user->hasRight('expedition', 'delivery', 'creer')) {
504 // Build a receiving receipt
505 $db->begin();
506
507 $result = $object->create_delivery($user);
508 if ($result > 0) {
509 $db->commit();
510
511 header("Location: ".DOL_URL_ROOT.'/delivery/card.php?action=create_delivery&token='.newToken().'&id='.$result);
512 exit;
513 } else {
514 $db->rollback();
515
516 setEventMessages($object->error, $object->errors, 'errors');
517 }
518 } elseif ($action == 'confirm_valid' && $confirm == 'yes' && ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'creer'))
519 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'shipping_advance', 'validate')))
520 ) {
521 $object->fetch_thirdparty();
522
523 $result = $object->valid($user);
524
525 if ($result < 0) {
526 setEventMessages($object->error, $object->errors, 'errors');
527 } else {
528 // Define output language
529 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
530 $outputlangs = $langs;
531 $newlang = '';
532 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
533 $newlang = GETPOST('lang_id', 'aZ09');
534 }
535 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
536 $newlang = $object->thirdparty->default_lang;
537 }
538 if (!empty($newlang)) {
539 $outputlangs = new Translate("", $conf);
540 $outputlangs->setDefaultLang($newlang);
541 }
542 $model = $object->model_pdf;
543 $ret = $object->fetch($id); // Reload to get new records
544
545 $result = $object->generateDocument($model, $outputlangs, $hidedetails, $hidedesc, $hideref);
546 if ($result < 0) {
547 dol_print_error($db, $object->error, $object->errors);
548 }
549 }
550 }
551 } elseif ($action == 'confirm_cancel' && $confirm == 'yes' && $user->hasRight('expedition', 'supprimer')) {
552 $also_update_stock = (GETPOST('alsoUpdateStock', 'alpha') ? 1 : 0);
553 $result = $object->cancel(0, (bool) $also_update_stock);
554 if ($result > 0) {
555 $result = $object->setStatut(-1);
556 } else {
557 setEventMessages($object->error, $object->errors, 'errors');
558 }
559 } elseif ($action == 'confirm_delete' && $confirm == 'yes' && $user->hasRight('expedition', 'supprimer')) {
560 $also_update_stock = (GETPOST('alsoUpdateStock', 'alpha') ? 1 : 0);
561 $result = $object->delete($user, 0, (bool) $also_update_stock);
562 if ($result > 0) {
563 header("Location: ".DOL_URL_ROOT.'/expedition/index.php');
564 exit;
565 } else {
566 setEventMessages($object->error, $object->errors, 'errors');
567 }
568 // TODO add alternative status
569 //} elseif ($action == 'reopen' && ($user->hasRight('expedition', 'creer') || $user->hasRight('expedition', 'shipping_advance', 'validate')))
570 //{
571 // $result = $object->setStatut(0);
572 // if ($result < 0)
573 // {
574 // setEventMessages($object->error, $object->errors, 'errors');
575 // }
576 //}
577 } elseif ($action == 'confirm_delete_subtotalline' && $confirm == 'yes' && $permissiontoadd) {
578 $result = $object->deleteSubtotalLine($langs, GETPOSTINT('lineid'), (bool) GETPOST('deletecorrespondingsubtotalline'), $user);
579 if ($result > 0) {
580 // reorder lines
581 $object->line_order(true, 'ASC', false);
582 // Define output language
583 $outputlangs = $langs;
584 $newlang = '';
585 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
586 $newlang = GETPOST('lang_id', 'aZ09');
587 }
588 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
589 $newlang = $object->thirdparty->default_lang;
590 }
591 if (!empty($newlang)) {
592 $outputlangs = new Translate("", $conf);
593 $outputlangs->setDefaultLang($newlang);
594 }
595 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
596 $ret = $object->fetch($object->id); // Reload to get new records
597 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
598 }
599
600 header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id);
601 exit;
602 } else {
603 setEventMessages($object->error, $object->errors, 'errors');
604 }
605 } elseif ($action == 'confirm_sign' && $confirm == 'yes' && $user->hasRight('expedition', 'creer')) {
606 $result = $object->setSignedStatus($user, GETPOSTINT('signed_status'), 0, 'SHIPPING_MODIFY');
607 if ($result >= 0) {
608 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
609 exit;
610 } else {
611 setEventMessages($object->error, $object->errors, 'errors');
612 }
613 } elseif ($action == 'confirm_unsign' && $confirm == 'yes' && $user->hasRight('expedition', 'creer')) {
614 $result = $object->setSignedStatus($user, Expedition::$SIGNED_STATUSES['STATUS_NO_SIGNATURE'], 0, 'SHIPPING_MODIFY');
615 if ($result >= 0) {
616 header('Location: ' . $_SERVER["PHP_SELF"] . '?id=' . $object->id);
617 exit;
618 } else {
619 setEventMessages($object->error, $object->errors, 'errors');
620 }
621 } elseif ($action == 'setdate_livraison' && $user->hasRight('expedition', 'creer')) {
622 $datedelivery = dol_mktime(GETPOSTINT('liv_hour'), GETPOSTINT('liv_min'), 0, GETPOSTINT('liv_month'), GETPOSTINT('liv_day'), GETPOSTINT('liv_year'));
623
624 $object->fetch($id);
625 $result = $object->setDeliveryDate($user, $datedelivery); // Set the planned delivery date
626 if ($result < 0) {
627 setEventMessages($object->error, $object->errors, 'errors');
628 }
629 } elseif ($action == 'setdate_shipping' && $user->hasRight('expedition', 'creer')) {
630 $dateshipping = dol_mktime(GETPOSTINT('ship_hour'), GETPOSTINT('ship_min'), 0, GETPOSTINT('ship_month'), GETPOSTINT('ship_day'), GETPOSTINT('ship_year'));
631
632 $object->fetch($id);
633 $result = $object->setShippingDate($user, $dateshipping);
634 if ($result < 0) {
635 setEventMessages($object->error, $object->errors, 'errors');
636 }
637 } elseif (in_array($action, array('settracking_number', 'settracking_url', 'settrueWeight', 'settrueWidth', 'settrueHeight', 'settrueDepth', 'setshipping_method_id')) && $user->hasRight('expedition', 'creer')) {
638 // Action update
639 $error = 0;
640
641 if ($action == 'settracking_number') { // Test on permission not required
642 $object->tracking_number = trim(GETPOST('tracking_number', 'alpha'));
643 }
644 if ($action == 'settracking_url') { // Test on permission not required
645 $object->tracking_url = trim(GETPOST('tracking_url', 'restricthtml'));
646 }
647
648 if ($action == 'settrueWeight') { // Test on permission not required
649 $object->trueWeight = GETPOSTFLOAT('trueWeight');
650 $object->weight_units = GETPOSTINT('weight_units');
651 }
652 if ($action == 'settrueWidth') { // Test on permission not required
653 $object->trueWidth = GETPOSTFLOAT('trueWidth');
654 }
655 if ($action == 'settrueHeight') { // Test on permission not required
656 $object->trueHeight = GETPOSTFLOAT('trueHeight');
657 $object->size_units = GETPOSTINT('size_units');
658 }
659 if ($action == 'settrueDepth') { // Test on permission not required
660 $object->trueDepth = GETPOSTFLOAT('trueDepth');
661 }
662 if ($action == 'setshipping_method_id') { // Test on permission not required
663 $object->shipping_method_id = GETPOSTINT('shipping_method_id');
664 }
665
666 if (!$error) {
667 if ($object->update($user) >= 0) {
668 header("Location: card.php?id=".$object->id);
669 exit;
670 }
671 setEventMessages($object->error, $object->errors, 'errors');
672 }
673
674 $action = "";
675 } elseif ($action == 'classifybilled' && $permissiontoadd) {
676 $object->fetch($id);
677 $result = $object->setBilled();
678 if ($result >= 0) {
679 header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id);
680 exit();
681 }
682 setEventMessages($object->error, $object->errors, 'errors');
683 } elseif ($action == 'classifyclosed' && $permissiontoadd) {
684 $object->fetch($id);
685 $result = $object->setClosed();
686 if ($result >= 0) {
687 header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id);
688 exit();
689 }
690 setEventMessages($object->error, $object->errors, 'errors');
691 } elseif ($action == 'deleteline' && !empty($line_id) && $permissiontoadd) {
692 // delete a line
693 $object->fetch($id);
694 $lines = $object->lines;
695 $line = new ExpeditionLigne($db);
696 $line->fk_expedition = $object->id;
697
698 $num_prod = count($lines);
699 for ($i = 0; $i < $num_prod; $i++) {
700 if ($lines[$i]->id == $line_id) {
701 if (count($lines[$i]->details_entrepot) > 1) {
702 // delete multi warehouse lines
703 foreach ($lines[$i]->details_entrepot as $details_entrepot) {
704 $line->id = $details_entrepot->line_id;
705 if (!$error && $line->delete($user) < 0) {
706 $error++;
707 }
708 }
709 } else {
710 // delete single warehouse line
711 $line->id = $line_id;
712 if (!$error && $line->delete($user) < 0) {
713 $error++;
714 }
715 }
716 }
717 unset($_POST["lineid"]);
718 }
719
720 if (!$error) {
721 header('Location: '.$_SERVER["PHP_SELF"].'?id='.$object->id);
722 exit();
723 } else {
724 setEventMessages($line->error, $line->errors, 'errors');
725 }
726 } elseif ($action == 'updateline' && $permissiontoadd && GETPOST('save')) {
727 // Update a line
728 $qty = 0;
729 $entrepot_id = 0;
730 $batch_id = 0;
731
732 $lines = $object->lines;
733 $num_prod = count($lines);
734 for ($i = 0; $i < $num_prod; $i++) {
735 if ($lines[$i]->id == $line_id) { // we have found line to update
736 $update_done = false;
737 $line = new ExpeditionLigne($db);
738 $line->fk_expedition = $object->id;
739
740 // Extrafields Lines
741 $line->array_options = $extrafields->getOptionalsFromPost($object->table_element_line);
742 // Unset extrafield POST Data
743 if (is_array($extrafields->attributes[$object->table_element_line]['label'])) {
744 foreach ($extrafields->attributes[$object->table_element_line]['label'] as $key => $value) {
745 unset($_POST["options_".$key]);
746 }
747 }
748 $line->fk_product = $lines[$i]->fk_product;
749 if (is_array($lines[$i]->detail_batch) && count($lines[$i]->detail_batch) > 0) {
750 // line with lot
751 foreach ($lines[$i]->detail_batch as $detail_batch) {
752 $lotStock = new Productbatch($db);
753 $batch = "batchl".$detail_batch->fk_expeditiondet."_".$detail_batch->fk_origin_stock;
754 $qty = "qtyl".$detail_batch->fk_expeditiondet.'_'.$detail_batch->id;
755 $batch_id = GETPOSTINT($batch);
756 $batch_qty = GETPOSTFLOAT($qty);
757 if (!empty($batch_id)) {
758 if ($lotStock->fetch($batch_id) > 0 && $line->fetch($detail_batch->fk_expeditiondet) > 0) { // $line is ExpeditionLine
759 if ($lines[$i]->entrepot_id != 0) {
760 // allow update line entrepot_id if not multi warehouse shipping
761 $line->entrepot_id = $lotStock->warehouseid;
762 }
763
764 // detail_batch can be an object with keys, or an array of ExpeditionLineBatch
765 if (empty($line->detail_batch)) {
766 $line->detail_batch = new stdClass();
767 }
768
769 $line->detail_batch->fk_origin_stock = $batch_id;
770 $line->detail_batch->batch = $lotStock->batch;
771 $line->detail_batch->id = $detail_batch->id;
772 $line->detail_batch->entrepot_id = $lotStock->warehouseid;
773 $line->detail_batch->qty = $batch_qty;
774 if ($line->update($user) < 0) {
775 setEventMessages($line->error, $line->errors, 'errors');
776 $error++;
777 } else {
778 $update_done = true;
779 }
780 } else {
781 setEventMessages($lotStock->error, $lotStock->errors, 'errors');
782 $error++;
783 }
784 }
785 unset($_POST[$batch]);
786 unset($_POST[$qty]);
787 }
788 // add new batch
789 $lotStock = new Productbatch($db);
790 $batch = "batchl".$line_id."_0";
791 $qty = "qtyl".$line_id."_0";
792 $batch_id = GETPOSTINT($batch);
793 $batch_qty = GETPOSTFLOAT($qty);
794 $lineIdToAddLot = 0;
795 if ($batch_qty > 0 && !empty($batch_id)) {
796 if ($lotStock->fetch($batch_id) > 0) {
797 // check if lotStock warehouse id is same as line warehouse id
798 if ($lines[$i]->entrepot_id > 0) {
799 // single warehouse shipment line
800 if ($lines[$i]->entrepot_id == $lotStock->warehouseid) {
801 $lineIdToAddLot = $line_id;
802 }
803 } elseif (count($lines[$i]->details_entrepot) > 1) {
804 // multi warehouse shipment lines
805 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
806 if ($detail_entrepot->entrepot_id == $lotStock->warehouseid) {
807 $lineIdToAddLot = $detail_entrepot->line_id;
808 }
809 }
810 }
811 if ($lineIdToAddLot) {
812 // add lot to existing line
813 if ($line->fetch($lineIdToAddLot) > 0) {
814 $line->detail_batch->fk_origin_stock = $batch_id;
815 $line->detail_batch->batch = $lotStock->batch;
816 $line->detail_batch->entrepot_id = $lotStock->warehouseid;
817 $line->detail_batch->qty = $batch_qty;
818 if ($line->update($user) < 0) {
819 setEventMessages($line->error, $line->errors, 'errors');
820 $error++;
821 } else {
822 $update_done = true;
823 }
824 } else {
825 setEventMessages($line->error, $line->errors, 'errors');
826 $error++;
827 }
828 } else {
829 // create new line with new lot
830 $line->origin_line_id = $lines[$i]->origin_line_id;
831 $line->entrepot_id = $lotStock->warehouseid;
832 $line->detail_batch[0] = new ExpeditionLineBatch($db);
833 $line->detail_batch[0]->fk_origin_stock = $batch_id;
834 $line->detail_batch[0]->batch = $lotStock->batch;
835 $line->detail_batch[0]->entrepot_id = $lotStock->warehouseid;
836 $line->detail_batch[0]->qty = $batch_qty;
837 if ($object->create_line_batch($line, $line->array_options) < 0) {
838 setEventMessages($object->error, $object->errors, 'errors');
839 $error++;
840 } else {
841 $update_done = true;
842 }
843 }
844 } else {
845 setEventMessages($lotStock->error, $lotStock->errors, 'errors');
846 $error++;
847 }
848 }
849 } else {
850 if ($lines[$i]->fk_product > 0) {
851 // line without lot
852 if ($lines[$i]->entrepot_id == 0) {
853 // single warehouse shipment line or line in several warehouses context but with warehouse not defined
854 $stockLocation = "entl".$line_id;
855 $qty = "qtyl".$line_id;
856 $line->id = $line_id;
857 $line->entrepot_id = GETPOSTINT((string) $stockLocation);
858 $line->qty = GETPOSTFLOAT($qty);
859 if ($line->update($user) < 0) {
860 setEventMessages($line->error, $line->errors, 'errors');
861 $error++;
862 }
863 unset($_POST[$stockLocation]);
864 unset($_POST[$qty]);
865 } elseif ($lines[$i]->entrepot_id > 0) {
866 // single warehouse shipment line
867 $stockLocation = "entl".$line_id;
868 $qty = "qtyl".$line_id;
869 $line->id = $line_id;
870 $line->entrepot_id = GETPOSTINT($stockLocation);
871 $line->qty = GETPOSTFLOAT($qty);
872 if ($line->update($user) < 0) {
873 setEventMessages($line->error, $line->errors, 'errors');
874 $error++;
875 }
876 unset($_POST[$stockLocation]);
877 unset($_POST[$qty]);
878 } elseif (count($lines[$i]->details_entrepot) > 1) {
879 // multi warehouse shipment lines
880 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
881 if (!$error) {
882 $stockLocation = "entl".$detail_entrepot->line_id;
883 $qty = "qtyl".$detail_entrepot->line_id;
884 $warehouse = GETPOSTINT($stockLocation);
885 if (!empty($warehouse)) {
886 $line->id = $detail_entrepot->line_id;
887 $line->entrepot_id = $warehouse;
888 $line->qty = GETPOSTFLOAT($qty);
889 if ($line->update($user) < 0) {
890 setEventMessages($line->error, $line->errors, 'errors');
891 $error++;
892 } else {
893 $update_done = true;
894 }
895 }
896 unset($_POST[$stockLocation]);
897 unset($_POST[$qty]);
898 }
899 }
900 } elseif (!isModEnabled('stock') && empty($conf->productbatch->enabled)) { // both product batch and stock are not activated.
901 $qty = "qtyl".$line_id;
902 $line->id = $line_id;
903 $line->qty = GETPOSTFLOAT($qty);
904 $line->entrepot_id = 0;
905 if ($line->update($user) < 0) {
906 setEventMessages($line->error, $line->errors, 'errors');
907 $error++;
908 } else {
909 $update_done = true;
910 }
911 unset($_POST[$qty]);
912 }
913 } else {
914 // Product no predefined
915 $qty = "qtyl".$line_id;
916 $line->id = $line_id;
917 $line->qty = GETPOSTFLOAT($qty);
918 $line->entrepot_id = 0;
919 if ($line->update($user) < 0) {
920 setEventMessages($line->error, $line->errors, 'errors');
921 $error++;
922 } else {
923 $update_done = true;
924 }
925 unset($_POST[$qty]);
926 }
927 }
928
929 if (empty($update_done)) {
930 $line->id = $lines[$i]->id;
931 $line->insertExtraFields();
932 }
933 }
934 }
935
936 unset($_POST["lineid"]);
937
938 if (!$error) {
939 if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE')) {
940 // Define output language
941 $outputlangs = $langs;
942 $newlang = '';
943 if (getDolGlobalInt('MAIN_MULTILANGS') /* && empty($newlang) */ && GETPOST('lang_id', 'aZ09')) {
944 $newlang = GETPOST('lang_id', 'aZ09');
945 }
946 if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang)) {
947 $newlang = $object->thirdparty->default_lang;
948 }
949 if (!empty($newlang)) {
950 $outputlangs = new Translate("", $conf);
951 $outputlangs->setDefaultLang($newlang);
952 }
953
954 $ret = $object->fetch($object->id); // Reload to get new records
955 $object->generateDocument($object->model_pdf, $outputlangs, $hidedetails, $hidedesc, $hideref);
956 }
957 } else {
958 header('Location: '.$_SERVER['PHP_SELF'].'?id='.$object->id); // To redisplay the form being edited
959 exit();
960 }
961 } elseif ($action == 'updateline' && $permissiontoadd && GETPOST('cancel', 'alpha') == $langs->trans("Cancel")) {
962 header('Location: '.$_SERVER['PHP_SELF'].'?id='.$object->id); // To redisplay the form being edited
963 exit();
964 }
965
966 include DOL_DOCUMENT_ROOT.'/core/actions_printing.inc.php';
967
968 // Actions to send emails
969 if (empty($id)) {
970 $id = $facid;
971 }
972 $triggersendname = 'SHIPPING_SENTBYMAIL';
973 $paramname = 'id';
974 $autocopy = 'MAIN_MAIL_AUTOCOPY_SHIPMENT_TO';
975 $mode = 'emailfromshipment';
976 $trackid = 'shi'.$object->id;
977 include DOL_DOCUMENT_ROOT.'/core/actions_sendmails.inc.php';
978}
979
980
981/*
982 * View
983 */
984
985$title = $object->ref.' - '.$langs->trans("Shipment");
986if ($action == 'create2') {
987 $title = $langs->trans("CreateShipment");
988}
989$help_url = 'EN:Module_Shipments|FR:Module_Expéditions|ES:M&oacute;dulo_Expediciones|DE:Modul_Lieferungen';
990
991llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-expedition page-card');
992
993if (empty($action)) {
994 $action = 'view';
995}
996
997$form = new Form($db);
998$formfile = new FormFile($db);
999$formproduct = new FormProduct($db);
1000if (isModEnabled('project')) {
1001 $formproject = new FormProjets($db);
1002} else {
1003 $formproject = null;
1004}
1005
1006$product_static = new Product($db);
1007$shipment_static = new Expedition($db);
1008$warehousestatic = new Entrepot($db);
1009
1010if ($action == 'create2') {
1011 print load_fiche_titre($langs->trans("CreateShipment"), '', 'dolly');
1012
1013 print '<br>'.$langs->trans("ShipmentCreationIsDoneFromOrder");
1014 $action = '';
1015 $id = '';
1016 $ref = '';
1017}
1018
1019// Mode creation.
1020if ($action == 'create') {
1021 $expe = new Expedition($db);
1022
1023 print load_fiche_titre($langs->trans("CreateShipment"), '', 'dolly');
1024
1025 if (!$origin) {
1026 setEventMessages($langs->trans("ErrorBadParameters"), null, 'errors');
1027 }
1028
1029 if ($origin) {
1030 $classname = ucfirst($origin);
1031
1032 $object = new $classname($db);
1033 /* @var Commande|Facture $object */
1034 '@phan-var-force Commande|Facture $object';
1035
1036 if ($object->fetch($origin_id)) { // This include the fetch_lines
1037 $soc = new Societe($db);
1038 $soc->fetch($object->socid);
1039
1040 $author = new User($db);
1041 $author->fetch($object->user_author_id);
1042
1043 if (isModEnabled('stock')) {
1044 $entrepot = new Entrepot($db);
1045 }
1046
1047 print '<form action="'.$_SERVER["PHP_SELF"].'" method="post">';
1048 print '<input type="hidden" name="token" value="'.newToken().'">';
1049 print '<input type="hidden" name="action" value="add">';
1050 print '<input type="hidden" name="origin" value="'.$origin.'">';
1051 print '<input type="hidden" name="origin_id" value="'.$object->id.'">';
1052 if (GETPOSTINT('entrepot_id')) {
1053 print '<input type="hidden" name="entrepot_id" value="'.GETPOSTINT('entrepot_id').'">';
1054 }
1055
1056 print dol_get_fiche_head([]);
1057
1058 print '<table class="border centpercent">';
1059
1060 // Ref
1061 print '<tr><td class="titlefieldcreate fieldrequired">';
1062 if ($origin == 'commande' && isModEnabled('order')) {
1063 print $langs->trans("RefOrder");
1064 }
1065 if ($origin == 'propal' && isModEnabled("propal")) {
1066 print $langs->trans("RefProposal");
1067 }
1068 print '</td><td colspan="3">';
1069 print $object->getNomUrl(1);
1070 print '</td>';
1071 print "</tr>\n";
1072
1073 // Ref client
1074 print '<tr><td>';
1075 if ($origin == 'commande') {
1076 print $langs->trans('RefCustomerOrder');
1077 } elseif ($origin == 'propal') {
1078 print $langs->trans('RefCustomerOrder');
1079 } else {
1080 print $langs->trans('RefCustomer');
1081 }
1082 print '</td><td colspan="3">';
1083 print '<input type="text" name="ref_customer" value="'.$object->ref_client.'" />';
1084 print '</td>';
1085 print '</tr>';
1086
1087 // Tiers
1088 print '<tr><td class="titlefieldcreate fieldrequired">'.$langs->trans('Company').'</td>';
1089 print '<td colspan="3">'.$soc->getNomUrl(1).'</td>';
1090 print '</tr>';
1091
1092 // Project
1093 if (isModEnabled('project') && is_object($formproject)) {
1094 $projectid = GETPOSTINT('projectid');
1095 if (empty($projectid) && !empty($object->fk_project)) {
1096 $projectid = $object->fk_project;
1097 }
1098 if ($origin == 'project') {
1099 $projectid = ($object->id ? $object->id : 0);
1100 }
1101
1102 $langs->load("projects");
1103 print '<tr>';
1104 print '<td>'.$langs->trans("Project").'</td><td colspan="2">';
1105 print img_picto('', 'project', 'class="pictofixedwidth"');
1106 print $formproject->select_projects($soc->id, $projectid, 'projectid', 0, 0, 1, 0, 0, 0, 0, '', 1, 0, 'widthcentpercentminusxx');
1107 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>';
1108 print '</td>';
1109 print '</tr>';
1110 }
1111
1112 // Date delivery planned
1113 print '<tr><td>'.$langs->trans("DateDeliveryPlanned").'</td>';
1114 print '<td colspan="3">';
1115 print img_picto('', 'action', 'class="pictofixedwidth"');
1116 $date_delivery = ($date_delivery ? $date_delivery : $object->delivery_date); // $date_delivery comes from GETPOST
1117 print $form->selectDate($date_delivery ? $date_delivery : -1, 'date_delivery', 1, 1, 1);
1118 print "</td>\n";
1119 print '</tr>';
1120
1121 // Date shipment (sending)
1122 print '<tr><td>' . $langs->trans("DateShipping") . '</td>';
1123 print '<td colspan="3">';
1124 print img_picto('', 'action', 'class="pictofixedwidth"');
1125 //$date_shipping = ($date_shipping ? $date_shipping : $object->date_shipping); // $date_shipping comes from GETPOST
1126 print $form->selectDate($date_shipping ? $date_shipping : -1, 'date_shipping', 1, 1, 1);
1127 print "</td>\n";
1128 print '</tr>';
1129
1130 // Note Public
1131 print '<tr><td>'.$langs->trans("NotePublic").'</td>';
1132 print '<td colspan="3">';
1133 $doleditor = new DolEditor('note_public', $object->note_public, '', 60, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PUBLIC') ? 0 : 1, ROWS_3, '90%');
1134 print $doleditor->Create(1);
1135 print "</td></tr>";
1136
1137 // Note Private
1138 if ($object->note_private && !$user->socid) {
1139 print '<tr><td>'.$langs->trans("NotePrivate").'</td>';
1140 print '<td colspan="3">';
1141 $doleditor = new DolEditor('note_private', $object->note_private, '', 60, 'dolibarr_notes', 'In', false, false, !getDolGlobalString('FCKEDITOR_ENABLE_NOTE_PRIVATE') ? 0 : 1, ROWS_3, '90%');
1142 print $doleditor->Create(1);
1143 print "</td></tr>";
1144 }
1145
1146 // Weight
1147 print '<tr><td>';
1148 print $langs->trans("Weight");
1149 print '</td><td colspan="3">';
1150 print img_picto('', 'fa-balance-scale', 'class="pictofixedwidth"');
1151 print '<input name="weight" size="4" value="'.dol_escape_htmltag(GETPOST('weight', 'alpha')).'"> ';
1152 $text = $formproduct->selectMeasuringUnits("weight_units", "weight", (string) GETPOSTINT('weight_units'), 0, 2);
1153 $htmltext = $langs->trans("KeepEmptyForAutoCalculation");
1154 print $form->textwithpicto($text, $htmltext);
1155 print '</td></tr>';
1156 // Dim
1157 print '<tr><td>';
1158 print $langs->trans("Width").' x '.$langs->trans("Height").' x '.$langs->trans("Depth");
1159 print ' </td><td colspan="3">';
1160 print img_picto('', 'fa-ruler', 'class="pictofixedwidth"');
1161 print '<input name="sizeW" size="4" value="'.GETPOSTINT('sizeW').'">';
1162 print ' x <input name="sizeH" size="4" value="'.GETPOSTINT('sizeH').'">';
1163 print ' x <input name="sizeS" size="4" value="'.GETPOSTINT('sizeS').'">';
1164 print ' ';
1165 $text = $formproduct->selectMeasuringUnits("size_units", "size", (string) GETPOSTINT('size_units'), 0, 2);
1166 $htmltext = $langs->trans("KeepEmptyForAutoCalculation");
1167 print $form->textwithpicto($text, $htmltext);
1168 print '</td></tr>';
1169
1170 // Delivery method
1171 print "<tr><td>".$langs->trans("DeliveryMethod")."</td>";
1172 print '<td colspan="3">';
1173 $expe->fetch_delivery_methods();
1174 print img_picto('', 'dolly', 'class="pictofixedwidth"');
1175 print $form->selectarray("shipping_method_id", $expe->meths, GETPOSTINT('shipping_method_id'), 1, 0, 0, "", 1, 0, 0, '', 'widthcentpercentminusxx');
1176 if ($user->admin) {
1177 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1178 }
1179 print "</td></tr>\n";
1180
1181 // Tracking number
1182 print "<tr><td>".$langs->trans("TrackingNumber")."</td>";
1183 print '<td colspan="3">';
1184 print img_picto('', 'barcode', 'class="pictofixedwidth"');
1185 print '<input name="tracking_number" size="20" value="'.GETPOST('tracking_number', 'alpha').'">';
1186 print "</td></tr>\n";
1187
1188 // Incoterms
1189 if (isModEnabled('incoterm')) {
1190 print '<tr>';
1191 print '<td><label for="incoterm_id">'.$form->textwithpicto($langs->trans("IncotermLabel"), $object->label_incoterms, 1).'</label></td>';
1192 print '<td colspan="3" class="maxwidthonsmartphone">';
1193 print img_picto('', 'incoterm', 'class="pictofixedwidth"');
1194 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''));
1195 print '</td></tr>';
1196 }
1197
1198 // Document model
1199 include_once DOL_DOCUMENT_ROOT.'/core/modules/expedition/modules_expedition.php';
1201 if (is_array($list) && count($list) > 1) {
1202 print "<tr><td>".$langs->trans("DefaultModel")."</td>";
1203 print '<td colspan="3">';
1204 print img_picto('', 'pdf', 'class="pictofixedwidth"');
1205 print $form->selectarray('model', $list, getDolGlobalString('EXPEDITION_ADDON_PDF'), 0, 0, 0, '', 0, 0, 0, '', 'widthcentpercentminusx');
1206 print "</td></tr>\n";
1207 }
1208
1209 // Other attributes. Fields from hook formObjectOptions and Extrafields.
1210 // $objectsrc is Commande|Facture
1211 $objectsav = $object; // Because Expedition is $expe and not $object that is wrongly a duplicate of $objectsrc.
1212 $object = $expe;
1213 // Propagate extrafieldsvalue from source object to shipment object
1214 if (isset($extrafields->attributes[$object->table_element]['label'])) {
1215 foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $val) {
1216 if (array_key_exists('options_' . $key, $objectsav->array_options)) { // We take value from order only if extrafield has the same name/key.
1217 $object->array_options['options_' . $key] = $objectsav->array_options['options_' . $key];
1218 }
1219 }
1220 }
1221 $parameters = array('objectsrc' => isset($objectsrc) ? $objectsrc : '', 'cols' => '3', 'socid' => $socid);
1222 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_add.tpl.php';
1223 $object = $objectsav;
1224
1225 print "</table>";
1226
1227 print dol_get_fiche_end();
1228
1229
1230 // Shipment lines
1231
1232 $numAsked = count($object->lines);
1233
1234 print '<script type="text/javascript">'."\n";
1235 print 'jQuery(document).ready(function() {'."\n";
1236 print 'jQuery("#autofill").click(function() {';
1237 $i = 0;
1238 while ($i < $numAsked) {
1239 print 'jQuery("#qtyl'.$i.'").val(jQuery("#qtyasked'.$i.'").val() - jQuery("#qtydelivered'.$i.'").val());'."\n";
1240 if (isModEnabled('productbatch')) {
1241 print 'jQuery("#qtyl'.$i.'_'.$i.'").val(jQuery("#qtyasked'.$i.'").val() - jQuery("#qtydelivered'.$i.'").val());'."\n";
1242 }
1243 $i++;
1244 }
1245 print 'return false; });'."\n";
1246 print 'jQuery("#autoreset").click(function() { console.log("Reset values to 0"); jQuery(".qtyl").val(0);'."\n";
1247 print 'return false; });'."\n";
1248 print '});'."\n";
1249 print '</script>'."\n";
1250
1251 print '<br>';
1252
1253 print '<div class="div-table-responsive-no-min">';
1254 print '<table class="noborder centpercent">';
1255
1256 // Load shipments already done for same order
1257 $object->loadExpeditions();
1258
1259
1260 $alreadyQtyBatchSetted = $alreadyQtySetted = array();
1261
1262 $title_lines_to_disable = array();
1263
1264 if ($numAsked) {
1265 if (isModEnabled('subtotals')) {
1266 if (!(getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES') || getDolGlobalString('STOCK_SUPPORTS_SERVICES'))) {
1267 $title_lines_to_disable = $object->getDisabledShippmentSubtotalLines();
1268 }
1269 $selectedLines = GETPOST('subtotal_toselect', 'array:int');
1270 foreach ($object->lines as $line) {
1271 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1272 $show_check_add_buttons = true;
1273 break;
1274 }
1275 }
1276 }
1277 print '<tr class="liste_titre">';
1278 print '<td>';
1279 if (isset($show_check_add_buttons)) {
1280 print $form->showCheckAddButtons('checkforselect');
1281 }
1282 print $langs->trans("Description");
1283 print '</td>';
1284 print '<td class="center">'.$langs->trans("QtyOrdered").'</td>';
1285 print '<td class="center">'.$langs->trans("QtyShipped").'</td>';
1286 print '<td class="center">'.$langs->trans("QtyToShip");
1287 if (empty($conf->productbatch->enabled)) {
1288 print '<br><a href="#" id="autofill" class="opacitymedium link cursor cursorpointer">'.img_picto($langs->trans("Autofill"), 'autofill', 'class="paddingrightonly"').'</a>';
1289 print ' / ';
1290 } else {
1291 print '<br>';
1292 }
1293 print '<span id="autoreset" class="opacitymedium link cursor cursorpointer">'.img_picto($langs->trans("Reset"), 'eraser').'</span>';
1294 print '</td>';
1295 if (isModEnabled('stock')) {
1296 if (empty($conf->productbatch->enabled)) {
1297 print '<td class="left">'.$langs->trans("Warehouse").' ('.$langs->trans("Stock").')</td>';
1298 } else {
1299 print '<td class="left">'.$langs->trans("Warehouse").' / '.$langs->trans("Batch").' ('.$langs->trans("Stock").')</td>';
1300 }
1301 }
1302 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1303 print '<td class="left">'.$langs->trans('StockEntryDate').'</td>';
1304 }
1305 print "</tr>\n";
1306 }
1307
1308 $warehouse_id = GETPOSTINT('entrepot_id');
1309 $warehousePicking = array();
1310 // get all warehouse children for picking
1311 if ($warehouse_id > 0) {
1312 $warehousePicking[] = $warehouse_id;
1313 $warehouseObj = new Entrepot($db);
1314 $warehouseObj->get_children_warehouses($warehouse_id, $warehousePicking);
1315 }
1316
1317 $indiceAsked = 0;
1318 while ($indiceAsked < $numAsked) {
1319 $product = new Product($db);
1320
1321 $line = $object->lines[$indiceAsked];
1322
1323 $parameters = array('i' => $indiceAsked, 'line' => $line, 'num' => $numAsked);
1324 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $object, $action);
1325 if ($reshook < 0) {
1326 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1327 }
1328
1329 if (empty($reshook) && $line->special_code != SUBTOTALS_SPECIAL_CODE) {
1330 // Show product and description
1331 $type = $line->product_type ? $line->product_type : $line->fk_product_type;
1332 // Try to enhance type detection using date_start and date_end for free lines where type
1333 // was not saved.
1334 if (!empty($line->date_start)) {
1335 $type = 1;
1336 }
1337 if (!empty($line->date_end)) {
1338 $type = 1;
1339 }
1340
1341 print '<!-- line for order line '.$line->id.' -->'."\n";
1342 print '<tr class="oddeven" id="row-'.$line->id.'">'."\n";
1343
1344 $qtyProdCom = $line->qty;
1345 $productChildrenNb = 0;
1346 // Product label
1347 if ($line->fk_product > 0) { // If predefined product
1348 $res = $product->fetch($line->fk_product);
1349 if ($res < 0) {
1350 dol_print_error($db, $product->error, $product->errors);
1351 }
1352 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
1353 $productChildrenNb = $product->hasFatherOrChild(1);
1354 }
1355 if ($productChildrenNb > 0) {
1356 $product->loadStockForVirtualProduct('warehouseopen', $qtyProdCom);
1357 } else {
1358 $product->load_stock('warehouseopen'); // Load all $product->stock_warehouse[idwarehouse]->detail_batch
1359 }
1360 //var_dump($product->stock_warehouse[1]);
1361
1362 print '<td>';
1363 print '<a name="'.$line->id.'"></a>'; // ancre pour retourner sur la ligne
1364
1365 // Show product and description
1366 $product_static->type = $line->fk_product_type;
1367 $product_static->id = $line->fk_product;
1368 $product_static->ref = $line->ref;
1369 $product_static->status = $line->product_tosell;
1370 $product_static->status_buy = $line->product_tobuy;
1371 $product_static->status_batch = $line->product_tobatch;
1372
1373 $showdescinproductdesc = getDolGlobalString('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE');
1374
1375 $text = $product_static->getNomUrl(1);
1376 $text .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
1377 $description = ($showdescinproductdesc ? '' : dol_htmlentitiesbr($line->desc));
1378 $description .= empty($product->stockable_product) ? $langs->trans('StockDisabled') : $langs->trans('StockEnabled');
1379 print $form->textwithtooltip($text, $description, 3, 0, '', (string) $i);
1380
1381 // Show range
1382 print_date_range($db->jdate($line->date_start), $db->jdate($line->date_end));
1383
1384 // Add description in form
1385 if ($showdescinproductdesc) {
1386 print ($line->desc && $line->desc != $line->product_label) ? '<br>'.dol_htmlentitiesbr($line->desc) : '';
1387 }
1388
1389 print '</td>';
1390 } else {
1391 print "<td>";
1392 if ($type == 1) {
1393 $text = img_object($langs->trans('Service'), 'service');
1394 } else {
1395 $text = img_object($langs->trans('Product'), 'product');
1396 }
1397
1398 if (!empty($line->label)) {
1399 $text .= ' <strong>'.$line->label.'</strong>';
1400 print $form->textwithtooltip($text, $line->desc, 3, 0, '', (string) $i);
1401 } else {
1402 print $text.' '.nl2br($line->desc);
1403 }
1404
1405 // Show range
1406 print_date_range($db->jdate($line->date_start), $db->jdate($line->date_end));
1407 print "</td>\n";
1408 }
1409
1410 // unit of order
1411 $unit_order = '';
1412 if (getDolGlobalString('PRODUCT_USE_UNITS')) {
1413 $unit_order = measuringUnitString($line->fk_unit);
1414 }
1415
1416 // Qty
1417 print '<td class="center">'.$line->qty;
1418 print '<input name="qtyasked'.$indiceAsked.'" id="qtyasked'.$indiceAsked.'" type="hidden" value="'.$line->qty.'">';
1419 print ''.$unit_order.'</td>';
1420
1421 // Qty already shipped
1422 print '<td class="center">';
1423 $quantityDelivered = isset($object->expeditions[$line->id]) ? $object->expeditions[$line->id] : '';
1424 print $quantityDelivered;
1425 print '<input name="qtydelivered'.$indiceAsked.'" id="qtydelivered'.$indiceAsked.'" type="hidden" value="'.$quantityDelivered.'">';
1426 print ''.$unit_order.'</td>';
1427
1428 // Qty to ship
1429 $quantityAsked = $line->qty;
1430 if ($line->product_type == Product::TYPE_SERVICE && !getDolGlobalString('STOCK_SUPPORTS_SERVICES') && !getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
1431 $quantityToBeDelivered = 0;
1432 } else {
1433 if (is_numeric($quantityDelivered)) {
1434 $quantityToBeDelivered = $quantityAsked - $quantityDelivered;
1435 } else {
1436 $quantityToBeDelivered = $quantityAsked;
1437 }
1438 }
1439
1440 $warehouseObject = null;
1441 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
1442 print '<!-- Case warehouse already known or product not a predefined product -->';
1443 //ship from preselected location
1444 $stock = + (isset($product->stock_warehouse[$warehouse_id]->real) ? $product->stock_warehouse[$warehouse_id]->real : 0); // Convert to number
1445 if ($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
1446 $deliverableQty = $quantityToBeDelivered;
1447 } else {
1448 $deliverableQty = min($quantityToBeDelivered, $stock);
1449 }
1450 if ($deliverableQty < 0) {
1451 $deliverableQty = 0;
1452 }
1453 if (empty($conf->productbatch->enabled) || !$product->hasbatch()) {
1454 // Quantity to send
1455 print '<td class="center">';
1456 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || ($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES'))) {
1457 if (GETPOSTISSET('qtyl'.$indiceAsked) && GETPOST('qtyl'.$indiceAsked) !== '') {
1458 $deliverableQty = GETPOSTFLOAT('qtyl'.$indiceAsked, 'MS');
1459 }
1460 print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
1461 $qtylValue = $deliverableQty;
1462 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
1463 $qtylValue = '';
1464 }
1465 print '<input name="qtyl'.$indiceAsked.'" id="qtyl'.$indiceAsked.'" class="qtyl right" type="text" size="4" value="'.$qtylValue.'">';
1466 } else {
1467 if (getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
1468 print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
1469 print '<input name="qtyl'.$indiceAsked.'" id="qtyl'.$indiceAsked.'" type="hidden" value="0">';
1470 }
1471
1472 print $langs->trans("NA");
1473 }
1474 print '</td>';
1475
1476 // Stock
1477 if (isModEnabled('stock')) {
1478 print '<td class="left">';
1479 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) { // Type of product need stock change ?
1480 // Show warehouse combo list
1481 $ent = "entl".$indiceAsked;
1482 $idl = "idl".$indiceAsked;
1483 $tmpentrepot_id = is_numeric(GETPOST($ent)) ? GETPOSTINT($ent) : $warehouse_id;
1484 if ($line->fk_product > 0) {
1485 print '<!-- Show warehouse selection -->';
1486
1487 $stockMin = false;
1488 if (getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
1489 $stockMin = 0;
1490 }
1491 if ($productChildrenNb > 0) {
1492 print $formproduct->selectWarehouses($tmpentrepot_id, 'entl'.$indiceAsked, '', 1, 0, 0, '', 0, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
1493 } else {
1494 if ($product->stockable_product == Product::ENABLED_STOCK) {
1495 print $formproduct->selectWarehouses($tmpentrepot_id, 'entl'.$indiceAsked, '', 1, 0, $line->fk_product, '', 1, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
1496 } else {
1497 print img_warning().' '.$langs->trans('StockDisabled');
1498 }
1499 }
1500
1501 if ($tmpentrepot_id > 0 && $tmpentrepot_id == $warehouse_id) {
1502 //print $stock.' '.$quantityToBeDelivered;
1503 if ($stock < $quantityToBeDelivered) {
1504 print ' '.img_warning($langs->trans("StockTooLow")); // Stock too low for this $warehouse_id but you can change warehouse
1505 }
1506 }
1507 }
1508 } else {
1509 print '<span class="opacitymedium">('.$langs->trans("Service").')</span><input name="entl'.$indiceAsked.'" id="entl'.$indiceAsked.'" type="hidden" value="0">';
1510 }
1511 print '</td>';
1512 }
1513 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1514 print '<td></td>';
1515 } //StockEntrydate
1516 print "</tr>\n";
1517
1518 // Show subproducts of product
1519 if (getDolGlobalString('PRODUIT_SOUSPRODUITS') && $line->fk_product > 0) {
1520 $product->get_sousproduits_arbo();
1521 $prods_arbo = $product->get_arbo_each_prod($qtyProdCom);
1522 if (count($prods_arbo) > 0) {
1523 foreach ($prods_arbo as $key => $value) {
1524 //print $value[0];
1525 $img = '';
1526 if ($value['stock'] < $value['stock_alert']) {
1527 $img = img_warning($langs->trans("StockTooLow"));
1528 }
1529 print "<tr class=\"oddeven\"><td>&nbsp; &nbsp; &nbsp; ->
1530 <a href=\"".DOL_URL_ROOT."/product/card.php?id=".$value['id']."\">".$value['fullpath']."
1531 </a> (".$value['nb'].")</td><td class=\"center\"> ".$value['nb_total']."</td><td>&nbsp;</td><td>&nbsp;</td>
1532 <td class=\"center\">".$value['stock']." ".$img."</td>";
1533 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1534 print '<td></td>';
1535 } //StockEntrydate
1536 print "</tr>";
1537 }
1538 }
1539 }
1540 } else {
1541 // Product need lot
1542 print '<td></td><td></td>';
1543 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1544 print '<td></td>';
1545 } //StockEntrydate
1546 print '</tr>'; // end line and start a new one for lot/serial
1547 print '<!-- Case product need lot -->';
1548
1549 $staticwarehouse = new Entrepot($db);
1550 if ($warehouse_id > 0) {
1551 $staticwarehouse->fetch($warehouse_id);
1552 }
1553
1554 $subj = 0;
1555 // Define nb of lines suggested for this order line
1556 $nbofsuggested = 0;
1557 if (is_object($product->stock_warehouse[$warehouse_id]) && !empty($product->stock_warehouse[$warehouse_id]->detail_batch)) {
1558 foreach ($product->stock_warehouse[$warehouse_id]->detail_batch as $dbatch) {
1559 $nbofsuggested++;
1560 }
1561 }
1562 print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
1563 if (is_object($product->stock_warehouse[$warehouse_id]) && !empty($product->stock_warehouse[$warehouse_id]->detail_batch)) {
1564 foreach ($product->stock_warehouse[$warehouse_id]->detail_batch as $dbatch) { // $dbatch is instance of Productbatch
1565 //var_dump($dbatch);
1566 $batchStock = + $dbatch->qty; // To get a numeric
1567 $deliverableQty = min($quantityToBeDelivered, $batchStock);
1568
1569 // Now we will check if we have to reduce the deliverableQty by taking into account the qty already suggested in previous line
1570 if (isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
1571 $deliverableQty = min($quantityToBeDelivered, $batchStock - $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)]);
1572 } else {
1573 if (!isset($alreadyQtyBatchSetted[$line->fk_product])) {
1574 $alreadyQtyBatchSetted[$line->fk_product] = array();
1575 }
1576
1577 if (!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])) {
1578 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch] = array();
1579 }
1580
1581 $deliverableQty = min($quantityToBeDelivered, $batchStock);
1582 }
1583
1584 if ($deliverableQty < 0) {
1585 $deliverableQty = 0;
1586 }
1587
1588 $inputName = 'qtyl'.$indiceAsked.'_'.$subj;
1589 if (GETPOSTISSET($inputName)) {
1590 $deliverableQty = GETPOST($inputName, 'int');
1591 }
1592
1593 $tooltipClass = $tooltipTitle = '';
1594 if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
1595 $tooltipClass = ' classfortooltip';
1596 $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
1597 } else {
1598 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = 0 ;
1599 }
1600 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = $deliverableQty + $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
1601
1602 print '<!-- subj='.$subj.'/'.$nbofsuggested.' --><tr '.((($subj + 1) == $nbofsuggested) ? 'oddeven' : '').'>';
1603 print '<td colspan="3" ></td><td class="center">';
1604 $qtylValue = $deliverableQty;
1605 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
1606 $qtylValue = '';
1607 }
1608 print '<input class="qtyl '.$tooltipClass.' right" title="'.$tooltipTitle.'" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="'.$qtylValue.'">';
1609 print '</td>';
1610
1611 print '<!-- Show details of lot -->';
1612 print '<td class="left">';
1613
1614 print $staticwarehouse->getNomUrl(0).' / ';
1615
1616 print '<input name="batchl'.$indiceAsked.'_'.$subj.'" type="hidden" value="'.$dbatch->id.'">';
1617
1618 $detail = '';
1619 $detail .= $langs->trans("Batch").': '.$dbatch->batch;
1620 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY') && !empty($dbatch->sellby)) {
1621 $detail .= ' - '.$langs->trans("SellByDate").': '.dol_print_date($dbatch->sellby, "day");
1622 }
1623 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY') && !empty($dbatch->eatby)) {
1624 $detail .= ' - '.$langs->trans("EatByDate").': '.dol_print_date($dbatch->eatby, "day");
1625 }
1626 $detail .= ' - '.$langs->trans("Qty").': '.$dbatch->qty;
1627 $detail .= '<br>';
1628 print $detail;
1629
1630 $quantityToBeDelivered -= $deliverableQty;
1631 if ($quantityToBeDelivered < 0) {
1632 $quantityToBeDelivered = 0;
1633 }
1634 $subj++;
1635 print '</td>';
1636 if (getDolGlobalInt('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1637 print '<td>'.dol_print_date($dbatch->context['stock_entry_date'], 'day').'</td>'; //StockEntrydate
1638 }
1639 print '</tr>';
1640 }
1641 } else {
1642 print '<!-- Case there is no details of lot at all -->';
1643 print '<tr class="oddeven"><td colspan="3"></td><td class="center">';
1644 print '<input class="qtyl right" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="0" disabled="disabled"> ';
1645 print '</td>';
1646
1647 print '<td class="left">';
1648 print img_warning().' '.$langs->trans("NoProductToShipFoundIntoStock", $staticwarehouse->label);
1649 print '</td>';
1650 if (getDolGlobalInt('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1651 print '<td></td>';
1652 } //StockEntrydate
1653 print '</tr>';
1654 }
1655 }
1656 } else {
1657 // ship from multiple locations
1658 if (empty($conf->productbatch->enabled) || !$product->hasbatch()) {
1659 print '<!-- Case warehouse not already known and product does not need lot -->';
1660 print '<td></td><td></td>';
1661 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1662 print '<td></td>';
1663 }//StockEntrydate
1664 print '</tr>'."\n"; // end line and start a new one for each warehouse
1665
1666 print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
1667 $subj = 0;
1668 // Define nb of lines suggested for this order line
1669 $nbofsuggested = 0;
1670
1671 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
1672 if ($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
1673 $nbofsuggested++;
1674 }
1675 }
1676 $tmpwarehouseObject = new Entrepot($db);
1677 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) { // $stock_warehouse is product_stock
1678 $var = $subj % 2;
1679 if (!empty($warehousePicking) && !in_array($warehouse_id, $warehousePicking)) {
1680 // if a warehouse was selected by user, picking is limited to this warehouse and his children
1681 continue;
1682 }
1683
1684 $tmpwarehouseObject->fetch($warehouse_id);
1685 if ($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
1686 $stock = + $stock_warehouse->real; // Convert it to number
1687 $deliverableQty = min($quantityToBeDelivered, $stock);
1688 $deliverableQty = max(0, $deliverableQty);
1689 // Quantity to send
1690 print '<!-- subj='.$subj.'/'.$nbofsuggested.' --><tr '.((($subj + 1) == $nbofsuggested) ? 'oddeven' : '').'>';
1691 print '<td colspan="3" ></td><td class="center"><!-- qty to ship (no lot management for product line indiceAsked='.$indiceAsked.') -->';
1692 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
1693 // For a virtual product (kit), $deliverableQty is already min($quantityToBeDelivered, $stock) computed above
1694 // and the per-warehouse "stock" returned by loadStockForVirtualProduct() is $qtyWish, not a shared physical
1695 // stock. The cross-line $alreadyQtySetted cap must not be applied for kits, otherwise the 2nd and next order
1696 // lines of the same kit would be wrongly pre-filled with 0.
1697 $tooltipClass = $tooltipTitle = '';
1698 if ($productChildrenNb <= 0) {
1699 if (isset($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])) {
1700 $deliverableQty = min($quantityToBeDelivered, $stock - $alreadyQtySetted[$line->fk_product][intval($warehouse_id)]);
1701 } else {
1702 if (!isset($alreadyQtySetted[$line->fk_product])) {
1703 $alreadyQtySetted[$line->fk_product] = array();
1704 }
1705
1706 $deliverableQty = min($quantityToBeDelivered, $stock);
1707 }
1708
1709 if ($deliverableQty < 0) {
1710 $deliverableQty = 0;
1711 }
1712
1713 if (!empty($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])) {
1714 $tooltipClass = ' classfortooltip';
1715 $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$alreadyQtySetted[$line->fk_product][intval($warehouse_id)];
1716 } else {
1717 $alreadyQtySetted[$line->fk_product][intval($warehouse_id)] = 0;
1718 }
1719
1720 $alreadyQtySetted[$line->fk_product][intval($warehouse_id)] = $deliverableQty + $alreadyQtySetted[$line->fk_product][intval($warehouse_id)];
1721 }
1722
1723 $inputName = 'qtyl'.$indiceAsked.'_'.$subj;
1724 if (GETPOSTISSET($inputName)) {
1725 $deliverableQty = GETPOSTINT($inputName);
1726 }
1727 $qtylValue = $deliverableQty;
1728 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
1729 $qtylValue = '';
1730 }
1731 print '<input class="qtyl'.$tooltipClass.' right" title="'.$tooltipTitle.'" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'" type="text" size="4" value="'.$qtylValue.'">';
1732 print '<input name="ent1'.$indiceAsked.'_'.$subj.'" type="hidden" value="'.$warehouse_id.'">';
1733 } else {
1734 if (getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
1735 print '<input name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'" type="hidden" value="0">';
1736 }
1737
1738 print $langs->trans("NA");
1739 }
1740 print '</td>';
1741
1742 // Stock
1743 if (isModEnabled('stock')) {
1744 print '<td class="left">';
1745 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
1746 if ($product->stockable_product == Product::ENABLED_STOCK || $productChildrenNb > 0) {
1747 print $tmpwarehouseObject->getNomUrl(0).' ';
1748 if ($productChildrenNb <= 0) {
1749 print '<!-- Show details of stock -->';
1750 print '('.$stock.')';
1751 }
1752 } else {
1753 print img_warning().' '.$langs->trans('StockDisabled');
1754 }
1755 } else {
1756 print '<span class="opacitymedium">('.$langs->trans("Service").')</span>';
1757 }
1758 print '</td>';
1759 }
1760 $quantityToBeDelivered -= $deliverableQty;
1761 if ($quantityToBeDelivered < 0) {
1762 $quantityToBeDelivered = 0;
1763 }
1764 $subj++;
1765 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1766 print '<td></td>';
1767 }//StockEntrydate
1768 print "</tr>\n";
1769 }
1770 }
1771 // Show subproducts of product (not recommended)
1772 if (getDolGlobalString('PRODUIT_SOUSPRODUITS') && $line->fk_product > 0) {
1773 $product->get_sousproduits_arbo();
1774 $prods_arbo = $product->get_arbo_each_prod($qtyProdCom);
1775 if (count($prods_arbo) > 0) {
1776 foreach ($prods_arbo as $key => $value) {
1777 //print $value[0];
1778 $img = '';
1779 if ($value['stock'] < $value['stock_alert']) {
1780 $img = img_warning($langs->trans("StockTooLow"));
1781 }
1782 print '<tr class"oddeven"><td>';
1783 print "&nbsp; &nbsp; &nbsp; ->
1784 <a href=\"".DOL_URL_ROOT."/product/card.php?id=".$value['id']."\">".$value['fullpath']."
1785 </a> (".$value['nb'].")</td><td class=\"center\"> ".$value['nb_total']."</td><td>&nbsp;</td><td>&nbsp;</td>
1786 <td class=\"center\">".$value['stock']." ".$img."</td>";
1787 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1788 print '<td></td>';
1789 }//StockEntrydate
1790 print "</tr>";
1791 }
1792 }
1793 }
1794 } else {
1795 print '<!-- Case warehouse not already known and product need lot -->';
1796 print '<td></td><td></td>';
1797 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1798 print '<td></td>';
1799 }//StockEntrydate
1800 print '</tr>'; // end line and start a new one for lot/serial
1801
1802 $subj = 0;
1803 print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
1804
1805 $tmpwarehouseObject = new Entrepot($db);
1806 $productlotObject = new Productlot($db);
1807
1808 // Define nb of lines suggested for this order line
1809 $nbofsuggested = 0;
1810 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
1811 if (($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) && (!empty($stock_warehouse->detail_batch))) {
1812 $nbofsuggested += count($stock_warehouse->detail_batch);
1813 }
1814 }
1815
1816 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
1817 $var = $subj % 2;
1818 if (!empty($warehousePicking) && !in_array($warehouse_id, $warehousePicking)) {
1819 // if a warehouse was selected by user, picking is limited to this warehouse and his children
1820 continue;
1821 }
1822
1823 $tmpwarehouseObject->fetch($warehouse_id);
1824 if (($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) && (!empty($stock_warehouse->detail_batch))) {
1825 foreach ($stock_warehouse->detail_batch as $dbatch) {
1826 $batchStock = + $dbatch->qty; // To get a numeric
1827 if (isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
1828 $deliverableQty = min($quantityToBeDelivered, $batchStock - $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)]);
1829 } else {
1830 if (!isset($alreadyQtyBatchSetted[$line->fk_product])) {
1831 $alreadyQtyBatchSetted[$line->fk_product] = array();
1832 }
1833
1834 if (!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])) {
1835 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch] = array();
1836 }
1837
1838 $deliverableQty = min($quantityToBeDelivered, $batchStock);
1839 }
1840
1841 if ($deliverableQty < 0) {
1842 $deliverableQty = 0;
1843 }
1844
1845 $inputName = 'qtyl'.$indiceAsked.'_'.$subj;
1846 if (GETPOSTISSET($inputName)) {
1847 $deliverableQty = GETPOSTINT($inputName);
1848 }
1849
1850 $tooltipClass = $tooltipTitle = '';
1851 if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
1852 $tooltipClass = ' classfortooltip';
1853 $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
1854 } else {
1855 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = 0 ;
1856 }
1857 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = $deliverableQty + $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
1858
1859 print '<!-- subj='.$subj.'/'.$nbofsuggested.' --><tr '.((($subj + 1) == $nbofsuggested) ? 'oddeven' : '').'><td colspan="3"></td><td class="center">';
1860 $qtylValue = $deliverableQty;
1861 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
1862 $qtylValue = '';
1863 }
1864 print '<input class="qtyl right '.$tooltipClass.'" title="'.$tooltipTitle.'" name="'.$inputName.'" id="'.$inputName.'" type="text" size="4" value="'.$qtylValue.'">';
1865 print '</td>';
1866
1867 print '<td class="left">';
1868
1869 print $tmpwarehouseObject->getNomUrl(0).' / ';
1870
1871 print '<!-- Show details of lot -->';
1872 print '<input name="batchl'.$indiceAsked.'_'.$subj.'" type="hidden" value="'.$dbatch->id.'">';
1873
1874 //print '|'.$line->fk_product.'|'.$dbatch->batch.'|<br>';
1875 print $langs->trans("Batch").': ';
1876 $result = $productlotObject->fetch(0, $line->fk_product, $dbatch->batch);
1877 if ($result > 0) {
1878 print $productlotObject->getNomUrl(1);
1879 } else {
1880 print $langs->trans("TableLotIncompleteRunRepairWithParamStandardEqualConfirmed");
1881 }
1882 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY') && !empty($dbatch->sellby)) {
1883 print ' - '.$langs->trans("SellByDate").': '.dol_print_date($dbatch->sellby, "day");
1884 }
1885 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY') && !empty($dbatch->eatby)) {
1886 print ' - '.$langs->trans("EatByDate").': '.dol_print_date($dbatch->eatby, "day");
1887 }
1888 print ' ('.$dbatch->qty.')';
1889 $quantityToBeDelivered -= $deliverableQty;
1890 if ($quantityToBeDelivered < 0) {
1891 $quantityToBeDelivered = 0;
1892 }
1893 //dol_syslog('deliverableQty = '.$deliverableQty.' batchStock = '.$batchStock);
1894 $subj++;
1895 print '</td>';
1896 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1897 print '<td class="left">'.dol_print_date($dbatch->context['stock_entry_date'], 'day').'</td>';
1898 }
1899 print '</tr>';
1900 }
1901 }
1902 }
1903 }
1904 if ($subj == 0) { // Line not shown yet, we show it
1905 $warehouse_selected_id = GETPOSTINT('entrepot_id');
1906
1907 print '<!-- line not shown yet, we show it -->';
1908 print '<tr class="oddeven"><td colspan="3"></td><td class="center">';
1909
1910 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
1911 $disabled = '';
1912 if (isModEnabled('productbatch') && $product->hasbatch()) {
1913 $disabled = 'disabled="disabled"';
1914 }
1915 if ($warehouse_selected_id <= 0) { // We did not force a given warehouse, so we won't have no warehouse to change qty.
1916 $disabled = 'disabled="disabled"';
1917 }
1918 // finally we overwrite the input with the product status stockable_product if it's disabled
1919 if ($product->stockable_product == Product::DISABLED_STOCK) {
1920 $disabled = '';
1921 }
1922 print '<input class="qtyl right" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="0"'.($disabled ? ' '.$disabled : '').'> ';
1923 if (empty($disabled) && (!getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER') || $product->stockable_product == Product::DISABLED_STOCK)) {
1924 print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $warehouse_selected_id . '">';
1925 }
1926 } elseif ($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
1927 $disabled = '';
1928 if (isModEnabled('productbatch') && $product->hasbatch()) {
1929 $disabled = 'disabled="disabled"';
1930 }
1931 if ($warehouse_selected_id <= 0) { // We did not force a given warehouse, so we won't have no warehouse to change qty.
1932 $disabled = 'disabled="disabled"';
1933 }
1934 print '<input class="qtyl right" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="'.$quantityToBeDelivered.'"'.($disabled ? ' '.$disabled : '').'> ';
1935 if (empty($disabled)) {
1936 print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $warehouse_selected_id . '">';
1937 }
1938 } else {
1939 print $langs->trans("NA");
1940 }
1941 print '</td>';
1942
1943 print '<td class="left">';
1944 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
1945 if ($warehouse_selected_id > 0 && $product->stockable_product == Product::ENABLED_STOCK) {
1946 $warehouseObject = new Entrepot($db);
1947 $warehouseObject->fetch($warehouse_selected_id);
1948 print img_warning().' '.$langs->trans("NoProductToShipFoundIntoStock", $warehouseObject->label);
1949 } else {
1950 if ($line->fk_product) {
1951 if ($product->stockable_product == Product::ENABLED_STOCK) {
1952 print img_warning().' '.$langs->trans('StockTooLow');
1953 } else {
1954 print img_warning().' '.$langs->trans('StockDisabled');
1955 }
1956 } else {
1957 print '';
1958 }
1959 }
1960 } else {
1961 print '<span class="opacitymedium">('.$langs->trans("Service").')</span>';
1962 }
1963 print '</td>';
1964 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1965 print '<td></td>';
1966 }//StockEntrydate
1967 print '</tr>';
1968 }
1969 }
1970
1971 // Display lines for extrafields of the Shipment line
1972 // $line is a 'Order line'
1973 if (!empty($extrafields)) {
1974 //var_dump($line);
1975 $colspan = 5;
1976 $expLine = new ExpeditionLigne($db);
1977
1978 $srcLine = new OrderLine($db);
1979 $srcLine->id = $line->id;
1980 $srcLine->fetch_optionals(); // fetch extrafields also available in orderline
1981
1982 $expLine->array_options = array_merge($expLine->array_options, $srcLine->array_options);
1983
1984 print $expLine->showOptionals($extrafields, 'edit', array('style' => 'class="drag drop oddeven"', 'colspan' => $colspan), (string) $indiceAsked, '', '1');
1985 }
1986 } elseif (empty($reshook) && $line->special_code == SUBTOTALS_SPECIAL_CODE && !in_array($line->id, $title_lines_to_disable)) {
1987 require dol_buildpath('/core/tpl/subtotalline_select.tpl.php');
1988 }
1989
1990 $indiceAsked++;
1991 }
1992
1993 print "</table>";
1994 print '</div>';
1995
1996 print '<br>';
1997
1998 print $form->buttonsSaveCancel("Create");
1999
2000 print '</form>';
2001
2002 print '<br>';
2003 } else {
2004 dol_print_error($db);
2005 }
2006 }
2007} elseif ($object->id > 0) {
2008 '@phan-var-force Expedition $object'; // Need to force it (type overridden earlier)
2009
2010 // Edit and view mode
2011
2012 $lines = $object->lines;
2013
2014 $num_prod = count($lines);
2015
2016 if (!empty($object->origin) && $object->origin_id > 0) {
2017 $typeobject = $object->origin;
2018 $origin = $object->origin;
2019 $origin_id = $object->origin_id;
2020
2021 $object->fetch_origin(); // Load property $object->origin_object (old $object->commande, $object->propal, ...)
2022 }
2023
2024 $soc = new Societe($db);
2025 $soc->fetch($object->socid);
2026
2027 $res = $object->fetch_optionals();
2028
2030 print dol_get_fiche_head($head, 'shipping', $langs->trans("Shipment"), -1, $object->picto);
2031
2032 $formconfirm = '';
2033
2034 // Confirm deletion
2035 if ($action == 'delete') {
2036 $formquestion = array();
2037 if ($object->status == Expedition::STATUS_CLOSED && getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) {
2038 $formquestion = array(
2039 array(
2040 'label' => $langs->trans('ShipmentIncrementStockOnDelete'),
2041 'name' => 'alsoUpdateStock',
2042 'type' => 'checkbox',
2043 'value' => 0
2044 ),
2045 );
2046 }
2047 $formconfirm = $form->formconfirm(
2048 $_SERVER['PHP_SELF'].'?id='.$object->id,
2049 $langs->trans('DeleteSending'),
2050 $langs->trans("ConfirmDeleteSending", $object->ref),
2051 'confirm_delete',
2052 $formquestion,
2053 0,
2054 1
2055 );
2056 }
2057
2058 // Confirm delete of subtotal line
2059 if ($action == 'ask_subtotal_deleteline') {
2060 $lineid = GETPOSTINT('lineid');
2061 $langs->load("subtotals");
2062 $title = "DeleteSubtotalLine";
2063 $question = "ConfirmDeleteSubtotalLine";
2064 if (GETPOST('type') == 'title') {
2065 $formconfirm = array(array('type' => 'checkbox', 'name' => 'deletecorrespondingsubtotalline', 'label' => $langs->trans("DeleteCorrespondingSubtotalLine"), 'value' => 0));
2066 $title = "DeleteTitleLine";
2067 $question = "ConfirmDeleteTitleLine";
2068 }
2069 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans($title), $langs->trans($question), 'confirm_delete_subtotalline', $formconfirm, 'no', 1);
2070 }
2071
2072 // Confirm validation
2073 if ($action == 'valid') {
2074 $objectref = substr($object->ref, 1, 4);
2075 if ($objectref == 'PROV') {
2076 $numref = $object->getNextNumRef($soc);
2077 } else {
2078 $numref = $object->ref;
2079 }
2080
2081 $text = $langs->trans("ConfirmValidateSending", $numref);
2082 if (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) {
2083 $text .= '<br>'.img_picto('', 'movement', 'class="pictofixedwidth"').$langs->trans("StockMovementWillBeRecorded").'.';
2084 } elseif (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) {
2085 $text .= '<br>'.img_picto('', 'movement', 'class="pictofixedwidth"').$langs->trans("StockMovementNotYetRecorded").'.';
2086 }
2087
2088 if (isModEnabled('notification')) {
2089 require_once DOL_DOCUMENT_ROOT.'/core/class/notify.class.php';
2090 $notify = new Notify($db);
2091 $text .= '<br>';
2092 $text .= $notify->confirmMessage('SHIPPING_VALIDATE', $object->socid, $object);
2093 }
2094
2095 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id, $langs->trans('ValidateSending'), $text, 'confirm_valid', '', 0, 1, 260);
2096 }
2097
2098 // Confirm cancellation
2099 if ($action == 'cancel') {
2100 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id, $langs->trans('CancelSending'), $langs->trans("ConfirmCancelSending", $object->ref), 'confirm_cancel', '', 0, 1);
2101 }
2102
2103 // Confirm sign
2104 if ($action == 'sign') {
2105 $text = $langs->trans('ConfirmSignShipping');
2106 if (isModEnabled('notification')) {
2107 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2108 $notify = new Notify($db);
2109 $text .= '<br>';
2110 $text .= $notify->confirmMessage('SHIPPING_MODIFY', $object->socid, $object);
2111 }
2112 $formquestion = [];
2113 $formquestion[] = [
2114 'type' => 'select',
2115 'name' => 'signed_status',
2116 'select_show_empty' => 0,
2117 'label' => '<span class="fieldrequired">' . $langs->trans('SignStatus') . '</span>',
2118 'values' => $object->getSignedStatusLocalisedArray()
2119 ];
2120 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('SignShipping'), $text, 'confirm_sign', $formquestion, 0, 1);
2121 }
2122
2123 // Confirm unsign
2124 if ($action == 'unsign') {
2125 $text = $langs->trans('ConfirmUnsignShipping');
2126 if (isModEnabled('notification')) {
2127 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2128 $notify = new Notify($db);
2129 $text .= '<br>';
2130 $text .= $notify->confirmMessage('SHIPPING_MODIFY', $object->socid, $object);
2131 }
2132 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('UnsignShipping'), $text, 'confirm_unsign', '', 0, 1);
2133 }
2134
2135 // Call Hook formConfirm
2136 $parameters = array('formConfirm' => $formconfirm);
2137 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2138 if (empty($reshook)) {
2139 $formconfirm .= $hookmanager->resPrint;
2140 } elseif ($reshook > 0) {
2141 $formconfirm = $hookmanager->resPrint;
2142 }
2143
2144 // Print form confirm
2145 print $formconfirm;
2146
2147 // Calculate totalWeight and totalVolume for all products
2148 // by adding weight and volume of each product line.
2149 $tmparray = $object->getTotalWeightVolume();
2150 $totalWeight = $tmparray['weight'];
2151 $totalVolume = $tmparray['volume'];
2152
2153 if (!empty($typeobject) && $typeobject === 'commande' && is_object($object->origin_object) && $object->origin_object->id && isModEnabled('order')) {
2154 $objectsrc = new Commande($db);
2155 $objectsrc->fetch($object->origin_object->id);
2156 }
2157 if (!empty($typeobject) && $typeobject === 'propal' && is_object($object->origin_object) && $object->origin_object->id && isModEnabled("propal")) {
2158 $objectsrc = new Propal($db);
2159 $objectsrc->fetch($object->origin_object->id);
2160 }
2161
2162 // Shipment card
2163 $linkback = '<a href="'.DOL_URL_ROOT.'/expedition/list.php?restore_lastsearch_values=1'.(!empty($socid) ? '&socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
2164 $morehtmlref = '<div class="refidno">';
2165 // Ref customer shipment
2166 $morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_customer', $object->ref_customer, $object, $user->hasRight('expedition', 'creer'), 'string', '', 0, 1);
2167 $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);
2168 // Thirdparty
2169 $morehtmlref .= '<br>'.$object->thirdparty->getNomUrl(1);
2170 // Project
2171 if (isModEnabled('project')) {
2172 $langs->load("projects");
2173 $morehtmlref .= '<br>';
2174 if (0) { // Do not change on shipment
2175 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
2176 if ($action != 'classify') {
2177 $morehtmlref .= '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=classify&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
2178 }
2179 $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');
2180 } else {
2181 if (!empty($objectsrc) && !empty($objectsrc->fk_project)) {
2182 $proj = new Project($db);
2183 $proj->fetch($objectsrc->fk_project);
2184 $morehtmlref .= $proj->getNomUrl(1);
2185 if ($proj->title) {
2186 $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
2187 }
2188 }
2189 }
2190 }
2191 $morehtmlref .= '</div>';
2192
2193
2194 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
2195
2196
2197 print '<div class="fichecenter">';
2198 print '<div class="fichehalfleft">';
2199 print '<div class="underbanner clearboth"></div>';
2200
2201 print '<table class="border tableforfield centpercent">';
2202
2203 // Linked documents
2204 if (!empty($typeobject) && $typeobject == 'commande' && $object->origin_object->id && isModEnabled('order')) {
2205 print '<tr><td>';
2206 print $langs->trans("RefOrder").'</td>';
2207 print '<td>';
2208 print $objectsrc->getNomUrl(1, 'commande');
2209 print "</td>\n";
2210 print '</tr>';
2211 }
2212 if (!empty($typeobject) && $typeobject == 'propal' && $object->origin_object->id && isModEnabled("propal")) {
2213 print '<tr><td>';
2214 print $langs->trans("RefProposal").'</td>';
2215 print '<td>';
2216 print $objectsrc->getNomUrl(1, 'expedition');
2217 print "</td>\n";
2218 print '</tr>';
2219 }
2220
2221 // Date creation
2222 print '<tr><td class="titlefieldmiddle">'.$langs->trans("DateCreation").'</td>';
2223 print '<td>'.dol_print_date($object->date_creation, "dayhour")."</td>\n";
2224 print '</tr>';
2225
2226 // Delivery date planned
2227 print '<tr><td height="10">';
2228 print '<table class="nobordernopadding centpercent"><tr><td>';
2229 print $langs->trans('DateDeliveryPlanned');
2230 print '</td>';
2231 if ($action != 'editdate_livraison') {
2232 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>';
2233 }
2234 print '</tr></table>';
2235 print '</td><td>';
2236 if ($action == 'editdate_livraison') {
2237 print '<form name="setdate_livraison" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="post">';
2238 print '<input type="hidden" name="token" value="'.newToken().'">';
2239 print '<input type="hidden" name="action" value="setdate_livraison">';
2240 print $form->selectDate($object->date_delivery ? $object->date_delivery : -1, 'liv_', 1, 1, 0, "setdate_livraison", 1, 0);
2241 print '<input type="submit" class="button button-edit smallpaddingimp" value="'.$langs->trans('Modify').'">';
2242 print '</form>';
2243 } else {
2244 print $object->date_delivery ? dol_print_date($object->date_delivery, 'dayhour') : '&nbsp;';
2245 }
2246 print '</td>';
2247 print '</tr>';
2248
2249 // Delivery sending date
2250 print '<tr><td height="10">';
2251 print '<table class="nobordernopadding centpercent"><tr><td>';
2252 print $langs->trans('DateShipping');
2253 print '</td>';
2254 if ($action != 'editdate_shipping') {
2255 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>';
2256 }
2257 print '</tr></table>';
2258 print '</td><td>';
2259 if ($action == 'editdate_shipping') {
2260 print '<form name="setdate_shipping" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="post">';
2261 print '<input type="hidden" name="token" value="'.newToken().'">';
2262 print '<input type="hidden" name="action" value="setdate_shipping">';
2263 print $form->selectDate($object->date_shipping ? $object->date_shipping : -1, 'ship_', 1, 1, 0, "setdate_shipping", 1, 0);
2264 print '<input type="submit" class="button button-edit smallpaddingimp" value="'.$langs->trans('Modify').'">';
2265 print '</form>';
2266 } else {
2267 print $object->date_shipping ? dol_print_date($object->date_shipping, 'dayhour') : '&nbsp;';
2268 }
2269 print '</td>';
2270 print '</tr>';
2271
2272 // Weight
2273 print '<tr><td>';
2274 print $form->editfieldkey("Weight", 'trueWeight', $object->trueWeight, $object, $user->hasRight('expedition', 'creer'));
2275 print '</td><td>';
2276
2277 if ($action == 'edittrueWeight') {
2278 print '<form name="settrueweight" action="'.$_SERVER["PHP_SELF"].'" method="post">';
2279 print '<input name="action" value="settrueWeight" type="hidden">';
2280 print '<input name="id" value="'.$object->id.'" type="hidden">';
2281 print '<input type="hidden" name="token" value="'.newToken().'">';
2282 print '<input id="trueWeight" name="trueWeight" value="'.$object->trueWeight.'" type="text" class="width50 valignmiddle">';
2283 print $formproduct->selectMeasuringUnits("weight_units", "weight", (string) $object->weight_units, 0, 2, 'maxwidth125 valignmiddle');
2284 print ' <input class="button smallpaddingimp valignmiddle" name="modify" value="'.$langs->trans("Modify").'" type="submit">';
2285 print ' <input class="button button-cancel smallpaddingimp valignmiddle" name="cancel" value="'.$langs->trans("Cancel").'" type="submit">';
2286 print '</form>';
2287 } else {
2288 print $object->trueWeight;
2289 print ($object->trueWeight && $object->weight_units != '') ? ' '.measuringUnitString(0, "weight", $object->weight_units) : '';
2290 }
2291
2292 // Calculated
2293 if ($totalWeight > 0) {
2294 if (!empty($object->trueWeight)) {
2295 print ' ('.$langs->trans("SumOfProductWeights").': ';
2296 }
2297 print showDimensionInBestUnit($totalWeight, 0, "weight", $langs, getDolGlobalInt('MAIN_WEIGHT_DEFAULT_ROUND', -1), isset($conf->global->MAIN_WEIGHT_DEFAULT_UNIT) ? $conf->global->MAIN_WEIGHT_DEFAULT_UNIT : 'no');
2298 if (!empty($object->trueWeight)) {
2299 print ')';
2300 }
2301 }
2302 print '</td></tr>';
2303
2304 // Width
2305 print '<tr><td>'.$form->editfieldkey("Width", 'trueWidth', $object->trueWidth, $object, $user->hasRight('expedition', 'creer')).'</td><td>';
2306 print $form->editfieldval("Width", 'trueWidth', $object->trueWidth, $object, $user->hasRight('expedition', 'creer'));
2307 print ($object->trueWidth && $object->width_units != '') ? ' '.measuringUnitString(0, "size", $object->width_units) : '';
2308 print '</td></tr>';
2309
2310 // Height
2311 print '<tr><td>'.$form->editfieldkey("Height", 'trueHeight', $object->trueHeight, $object, $user->hasRight('expedition', 'creer')).'</td><td>';
2312 if ($action == 'edittrueHeight') {
2313 print '<form name="settrueHeight" action="'.$_SERVER["PHP_SELF"].'" method="post">';
2314 print '<input name="action" value="settrueHeight" type="hidden">';
2315 print '<input name="id" value="'.$object->id.'" type="hidden">';
2316 print '<input type="hidden" name="token" value="'.newToken().'">';
2317 print '<input id="trueHeight" name="trueHeight" value="'.$object->trueHeight.'" type="text" class="width50">';
2318 print $formproduct->selectMeasuringUnits("size_units", "size", $object->size_units, 0, 2);
2319 print ' <input class="button smallpaddingimp" name="modify" value="'.$langs->trans("Modify").'" type="submit">';
2320 print ' <input class="button button-cancel smallpaddingimp" name="cancel" value="'.$langs->trans("Cancel").'" type="submit">';
2321 print '</form>';
2322 } else {
2323 print $object->trueHeight;
2324 print ($object->trueHeight && $object->height_units != '') ? ' '.measuringUnitString(0, "size", $object->height_units) : '';
2325 }
2326
2327 print '</td></tr>';
2328
2329 // Depth
2330 print '<tr><td>'.$form->editfieldkey("Depth", 'trueDepth', $object->trueDepth, $object, $user->hasRight('expedition', 'creer')).'</td><td>';
2331 print $form->editfieldval("Depth", 'trueDepth', $object->trueDepth, $object, $user->hasRight('expedition', 'creer'));
2332 print ($object->trueDepth && $object->depth_units != '') ? ' '.measuringUnitString(0, "size", $object->depth_units) : '';
2333 print '</td></tr>';
2334
2335 // Volume
2336 print '<tr><td>';
2337 print $langs->trans("Volume");
2338 print '</td>';
2339 print '<td>';
2340 $calculatedVolume = 0;
2341 $volumeUnit = 0;
2342 if ($object->trueWidth && $object->trueHeight && $object->trueDepth) {
2343 $calculatedVolume = ($object->trueWidth * $object->trueHeight * $object->trueDepth);
2344 $volumeUnit = $object->size_units * 3;
2345 }
2346 // If sending volume not defined we use sum of products
2347 if ($calculatedVolume > 0) {
2348 if ($volumeUnit < 50) {
2349 print showDimensionInBestUnit($calculatedVolume, $volumeUnit, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'));
2350 } else {
2351 print $calculatedVolume.' '.measuringUnitString(0, "volume", $volumeUnit);
2352 }
2353 }
2354 if ($totalVolume > 0) {
2355 if ($calculatedVolume) {
2356 print ' ('.$langs->trans("SumOfProductVolumes").': ';
2357 }
2358 print showDimensionInBestUnit($totalVolume, 0, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'));
2359 //if (empty($calculatedVolume)) print ' ('.$langs->trans("Calculated").')';
2360 if ($calculatedVolume) {
2361 print ')';
2362 }
2363 }
2364 print "</td>\n";
2365 print '</tr>';
2366
2367 // Other attributes
2368 //$cols = 2;
2369 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
2370
2371 print '</table>';
2372
2373 print '</div>';
2374 print '<div class="fichehalfright">';
2375 print '<div class="underbanner clearboth"></div>';
2376
2377 print '<table class="border centpercent tableforfield">';
2378
2379 // Sending method
2380 print '<tr><td>';
2381 print '<table class="nobordernopadding centpercent"><tr><td>';
2382 print $langs->trans('SendingMethod');
2383 print '</td>';
2384
2385 if ($action != 'editshipping_method_id' && $permissiontoadd) {
2386 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>';
2387 }
2388 print '</tr></table>';
2389 print '</td><td>';
2390 if ($action == 'editshipping_method_id') {
2391 print '<form name="setshipping_method_id" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="post">';
2392 print '<input type="hidden" name="token" value="'.newToken().'">';
2393 print '<input type="hidden" name="action" value="setshipping_method_id">';
2394 $object->fetch_delivery_methods();
2395 print $form->selectarray("shipping_method_id", $object->meths, $object->shipping_method_id, 1, 0, 0, "", 1);
2396 if ($user->admin) {
2397 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
2398 }
2399 print '<input type="submit" class="button button-edit smallpaddingimp" value="'.$langs->trans('Modify').'">';
2400 print '</form>';
2401 } else {
2402 if ($object->shipping_method_id > 0) {
2403 // Get code using getLabelFromKey
2404 $code = $langs->getLabelFromKey($db, (string) $object->shipping_method_id, 'c_shipment_mode', 'rowid', 'code');
2405 print $langs->trans("SendingMethod".strtoupper($code));
2406 }
2407 }
2408 print '</td>';
2409 print '</tr>';
2410
2411 // Tracking Number
2412 print '<tr><td class="titlefieldmiddle">'.$form->editfieldkey("TrackingNumber", 'tracking_number', $object->tracking_number, $object, $user->hasRight('expedition', 'creer')).'</td><td>';
2413 print $form->editfieldval("TrackingNumber", 'tracking_number', $object->tracking_url, $object, $user->hasRight('expedition', 'creer'), 'safehtmlstring', $object->tracking_number);
2414 print '</td></tr>';
2415
2416 // Incoterms
2417 if (isModEnabled('incoterm')) {
2418 print '<tr><td>';
2419 print '<table class="nobordernopadding centpercent"><tr><td>';
2420 print $langs->trans('IncotermLabel');
2421 print '<td><td class="right">';
2422 if ($permissiontoadd) {
2423 print '<a class="editfielda" href="'.DOL_URL_ROOT.'/expedition/card.php?id='.$object->id.'&action=editincoterm&token='.newToken().'">'.img_edit().'</a>';
2424 } else {
2425 print '&nbsp;';
2426 }
2427 print '</td></tr></table>';
2428 print '</td>';
2429 print '<td>';
2430 if ($action != 'editincoterm') {
2431 print $form->textwithpicto($object->display_incoterms(), $object->label_incoterms, 1);
2432 } else {
2433 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''), $_SERVER['PHP_SELF'].'?id='.$object->id);
2434 }
2435 print '</td></tr>';
2436 }
2437
2438 // Other attributes
2439 $parameters = array();
2440 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2441 print $hookmanager->resPrint;
2442
2443 print "</table>";
2444
2445 print '</div>';
2446 print '</div>';
2447
2448 print '<div class="clearboth"></div>';
2449
2450
2451 // Lines of products
2452
2453 if ($action == 'editline') {
2454 print ' <form name="updateline" id="updateline" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&amp;lineid='.$line_id.'" method="POST">
2455 <input type="hidden" name="token" value="' . newToken().'">
2456 <input type="hidden" name="action" value="updateline">
2457 <input type="hidden" name="mode" value="">
2458 <input type="hidden" name="id" value="' . $object->id.'">
2459 ';
2460 }
2461 print '<br>';
2462
2463 print '<div class="div-table-responsive-no-min">';
2464 print '<table class="noborder centpercent" id="tablelines" >';
2465 print '<thead>';
2466 print '<tr class="liste_titre">';
2467 // Adds a line numbering column
2468 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
2469 print '<td width="5" class="center linecolnum">&nbsp;</td>';
2470 }
2471 // Product/Service
2472 print '<td class="linecoldescription" >'.$langs->trans("Products").'</td>';
2473 // Qty
2474 print '<td class="center linecolqty">'.$langs->trans("QtyOrdered").'</td>';
2475 if ($origin && $origin_id > 0) {
2476 print '<td class="center linecolqtyinothershipments">'.$langs->trans("QtyInOtherShipments").'</td>';
2477 }
2478 if ($action == 'editline') {
2479 $editColspan = 3;
2480 if (!isModEnabled('stock')) {
2481 $editColspan--;
2482 }
2483 if (empty($conf->productbatch->enabled)) {
2484 $editColspan--;
2485 }
2486 print '<td class="center linecoleditlineotherinfo" colspan="'.$editColspan.'">';
2487 if ($object->status <= 1) {
2488 print $langs->trans("QtyToShip");
2489 } else {
2490 print $langs->trans("QtyShipped");
2491 }
2492 if (isModEnabled('stock')) {
2493 print ' - '.$langs->trans("WarehouseSource");
2494 }
2495 if (isModEnabled('productbatch')) {
2496 print ' - '.$langs->trans("Batch");
2497 }
2498 print '</td>';
2499 } else {
2500 if ($object->status <= 1) {
2501 print '<td class="center linecolqtytoship">'.$langs->trans("QtyToShip").'</td>';
2502 } else {
2503 print '<td class="center linecolqtyshipped">'.$langs->trans("QtyShipped").'</td>';
2504 }
2505 if (isModEnabled('stock')) {
2506 print '<td class="left linecolwarehousesource">'.$langs->trans("WarehouseSource").'</td>';
2507 }
2508
2509 if (isModEnabled('productbatch')) {
2510 print '<td class="left linecolbatch">'.$langs->trans("Batch").'</td>';
2511 }
2512 }
2513 print '<td class="center linecolweight">'.$langs->trans("CalculatedWeight").'</td>';
2514 print '<td class="center linecolvolume">'.$langs->trans("CalculatedVolume").'</td>';
2515 //print '<td class="center">'.$langs->trans("Size").'</td>';
2516 if ($object->status == 0) {
2517 print '<td class="linecoledit"></td>';
2518 print '<td class="linecoldelete" width="10"></td>';
2519 }
2520 print "</tr>\n";
2521 print '</thead>';
2522
2523 $outputlangs = $langs;
2524
2525 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
2526 $object->fetch_thirdparty();
2527 $newlang = '';
2528 if (/* empty($newlang) && */ GETPOST('lang_id', 'aZ09')) {
2529 $newlang = GETPOST('lang_id', 'aZ09');
2530 }
2531 if (empty($newlang)) {
2532 $newlang = $object->thirdparty->default_lang;
2533 }
2534 if (!empty($newlang)) {
2535 $outputlangs = new Translate("", $conf);
2536 $outputlangs->setDefaultLang($newlang);
2537 }
2538 }
2539
2540 // Get list of products already sent for same source object into $alreadysent
2541 $alreadysent = array();
2542 if ($origin && $origin_id > 0) {
2543 $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";
2544 $sql .= ", ed.rowid as shipmentline_id, ed.qty as qty_shipped, ed.fk_expedition as expedition_id, ed.fk_elementdet, ed.fk_entrepot";
2545 $sql .= ", e.rowid as shipment_id, e.ref as shipment_ref, e.date_creation, e.date_valid, e.date_delivery, e.date_expedition";
2546 //if (getDolGlobalInt('MAIN_SUBMODULE_DELIVERY')) $sql .= ", l.rowid as livraison_id, l.ref as livraison_ref, l.date_delivery, ld.qty as qty_received";
2547 $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';
2548 $sql .= ', p.description as product_desc';
2549 $sql .= " FROM ".MAIN_DB_PREFIX."expeditiondet as ed";
2550 $sql .= ", ".MAIN_DB_PREFIX."expedition as e";
2551 $sql .= ", ".MAIN_DB_PREFIX.$origin."det as obj";
2552 //if (getDolGlobalInt('MAIN_SUBMODULE_DELIVERY')) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."delivery as l ON l.fk_expedition = e.rowid LEFT JOIN ".MAIN_DB_PREFIX."deliverydet as ld ON ld.fk_delivery = l.rowid AND obj.rowid = ld.fk_origin_line";
2553 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON obj.fk_product = p.rowid";
2554 $sql .= " WHERE e.entity IN (".getEntity('expedition').")";
2555 $sql .= " AND obj.fk_".$origin." = ".((int) $origin_id);
2556 $sql .= " AND obj.rowid = ed.fk_elementdet";
2557 $sql .= " AND ed.fk_expedition = e.rowid";
2558 //if ($filter) $sql.= $filter;
2559 $sql .= " ORDER BY obj.fk_product";
2560
2561 dol_syslog("expedition/card.php get list of shipment lines", LOG_DEBUG);
2562 $resql = $db->query($sql);
2563 if ($resql) {
2564 $num = $db->num_rows($resql);
2565 $i = 0;
2566
2567 while ($i < $num) {
2568 $obj = $db->fetch_object($resql);
2569 if ($obj) {
2570 // $obj->rowid is rowid in $origin."det" table
2571 $alreadysent[$obj->rowid][$obj->shipmentline_id] = array(
2572 'shipment_ref' => $obj->shipment_ref, 'shipment_id' => $obj->shipment_id, 'warehouse' => $obj->fk_entrepot, 'qty_shipped' => $obj->qty_shipped,
2573 'product_tosell' => $obj->product_tosell, 'product_tobuy' => $obj->product_tobuy, 'product_tobatch' => $obj->product_tobatch,
2574 'date_valid' => $db->jdate($obj->date_valid), 'date_delivery' => $db->jdate($obj->date_delivery));
2575 }
2576 $i++;
2577 }
2578 }
2579 //var_dump($alreadysent);
2580 }
2581
2582 print '<tbody>';
2583
2584 // Loop on each product to send/sent
2585 $conf->cache['product'] = array();
2586 $conf->cache['warehouse'] = array();
2587 for ($i = 0; $i < $num_prod; $i++) {
2588 $parameters = array('i' => $i, 'line' => $lines[$i], 'line_id' => $line_id, 'num' => $num_prod, 'alreadysent' => $alreadysent, 'editColspan' => !empty($editColspan) ? $editColspan : 0, 'outputlangs' => $outputlangs);
2589 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $object, $action);
2590 if ($reshook < 0) {
2591 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2592 }
2593
2594 if (empty($reshook) && $lines[$i]->product_type != "9") {
2595 print '<!-- origin line id = '.$lines[$i]->origin_line_id.' -->'; // id of order line
2596 print '<tr class="oddeven" id="row-'.$lines[$i]->id.'" data-id="'.$lines[$i]->id.'" data-element="'.$lines[$i]->element.'" >';
2597
2598 // #
2599 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
2600 print '<td class="center linecolnum">'.($i + 1).'</td>';
2601 }
2602
2603 // Predefined product or service
2604 if ($lines[$i]->fk_product > 0) {
2605 // Define output language
2606 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
2607 $product_id = $lines[$i]->fk_product;
2608 if (!isset($conf->cache['product'][$product_id])) {
2609 $prod = new Product($db);
2610 $prod->fetch($product_id);
2611 $conf->cache['product'][$product_id] = $prod;
2612 } else {
2613 $prod = $conf->cache['product'][$product_id];
2614 }
2615 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $lines[$i]->product_label;
2616 } else {
2617 $label = (!empty($lines[$i]->label) ? $lines[$i]->label : $lines[$i]->product_label);
2618 }
2619
2620 print '<td class="linecoldescription">';
2621
2622 // Show product and description
2623 $product_static->type = $lines[$i]->fk_product_type;
2624 $product_static->id = $lines[$i]->fk_product;
2625 $product_static->ref = $lines[$i]->ref;
2626 $product_static->status = $lines[$i]->product_tosell;
2627 $product_static->status_buy = $lines[$i]->product_tobuy;
2628 $product_static->status_batch = $lines[$i]->product_tobatch;
2629
2630 $product_static->weight = $lines[$i]->weight;
2631 $product_static->weight_units = $lines[$i]->weight_units;
2632 $product_static->length = $lines[$i]->length;
2633 $product_static->length_units = $lines[$i]->length_units;
2634 $product_static->width = !empty($lines[$i]->width) ? $lines[$i]->width : 0;
2635 $product_static->width_units = !empty($lines[$i]->width_units) ? $lines[$i]->width_units : 0;
2636 $product_static->height = !empty($lines[$i]->height) ? $lines[$i]->height : 0;
2637 $product_static->height_units = !empty($lines[$i]->height_units) ? $lines[$i]->height_units : 0;
2638 $product_static->surface = $lines[$i]->surface;
2639 $product_static->surface_units = $lines[$i]->surface_units;
2640 $product_static->volume = $lines[$i]->volume;
2641 $product_static->volume_units = $lines[$i]->volume_units;
2642 $product_static->stockable_product = $lines[$i]->stockable_product;
2643
2644 $text = $product_static->getNomUrl(1);
2645 $text .= ' - '.$label;
2646 $description = (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE') ? '' : dol_htmlentitiesbr($lines[$i]->description));
2647 print $form->textwithtooltip($text, $description, 3, 0, '', (string) $i);
2648 print_date_range(!empty($lines[$i]->date_start) ? $lines[$i]->date_start : '', !empty($lines[$i]->date_end) ? $lines[$i]->date_end : '');
2649 if (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE')) {
2650 print (!empty($lines[$i]->description) && $lines[$i]->description != $lines[$i]->product) ? '<br>'.dol_htmlentitiesbr($lines[$i]->description) : '';
2651 }
2652 print "</td>\n";
2653 } else {
2654 print '<td class="linecoldescription" >';
2655 if ($lines[$i]->product_type == Product::TYPE_SERVICE) {
2656 $text = img_object($langs->trans('Service'), 'service');
2657 } else {
2658 $text = img_object($langs->trans('Product'), 'product');
2659 }
2660
2661 if (!empty($lines[$i]->label)) {
2662 $text .= ' <strong>'.$lines[$i]->label.'</strong>';
2663 print $form->textwithtooltip($text, $lines[$i]->description, 3, 0, '', (string) $i);
2664 } else {
2665 print $text.' '.nl2br($lines[$i]->description);
2666 }
2667
2668 print_date_range($lines[$i]->date_start, $lines[$i]->date_end);
2669 print "</td>\n";
2670 }
2671
2672 $unit_order = '';
2673 if (getDolGlobalString('PRODUCT_USE_UNITS')) {
2674 $unit_order = measuringUnitString((int) $lines[$i]->fk_unit);
2675 }
2676
2677 // Qty ordered
2678 print '<td class="center linecolqty">'.$lines[$i]->qty_asked.' '.$unit_order.'</td>';
2679
2680 // Qty in other shipments (with shipment and warehouse used)
2681 if ($origin && $origin_id > 0) {
2682 print '<td class="linecolqtyinothershipments center nowrap">';
2683 $htmltooltip = '';
2684 $qtyalreadysent = 0;
2685 foreach ($alreadysent as $key => $val) {
2686 if ($lines[$i]->fk_elementdet == $key) {
2687 $j = 0;
2688 foreach ($val as $shipmentline_id => $shipmentline_var) {
2689 if ($shipmentline_var['shipment_id'] == $lines[$i]->fk_expedition) {
2690 continue; // We want to show only "other shipments"
2691 }
2692
2693 $j++;
2694 if ($j > 1) {
2695 $htmltooltip .= '<br>';
2696 }
2697 $shipment_static->fetch($shipmentline_var['shipment_id']);
2698 $htmltooltip .= $shipment_static->getNomUrl(1, '', 0, 0, 1);
2699 $htmltooltip .= ' - '.$shipmentline_var['qty_shipped'];
2700 $htmltooltip .= ' - '.$langs->trans("DateValidation").' : '.(empty($shipmentline_var['date_valid']) ? $langs->trans("Draft") : dol_print_date($shipmentline_var['date_valid'], 'dayhour'));
2701 /*if (isModEnabled('stock') && $shipmentline_var['warehouse'] > 0) {
2702 $warehousestatic->fetch($shipmentline_var['warehouse']);
2703 $htmltext .= '<br>'.$langs->trans("FromLocation").' : '.$warehousestatic->getNomUrl(1, '', 0, 1);
2704 }*/
2705 //print ' '.$form->textwithpicto('', $htmltext, 1);
2706
2707 $qtyalreadysent += $shipmentline_var['qty_shipped'];
2708 }
2709 if ($j) {
2710 $htmltooltip = $langs->trans("QtyInOtherShipments").'...<br><br>'.$htmltooltip.'<br><input type="submit" name="dummyhiddenbuttontogetfocus" style="display:none" autofocus>';
2711 }
2712 }
2713 }
2714 print $form->textwithpicto((string) $qtyalreadysent, $htmltooltip, 1, 'info', '', 0, 3, 'tooltip'.$lines[$i]->id);
2715 print '</td>';
2716 }
2717
2718 if ($action == 'editline' && $lines[$i]->id == $line_id) {
2719 // edit mode
2720 print '<td colspan="'.$editColspan.'" class="center"><table class="nobordernopadding centpercent">';
2721 if (is_array($lines[$i]->detail_batch) && count($lines[$i]->detail_batch) > 0) {
2722 print '<!-- case edit 1 -->';
2723 $line = new ExpeditionLigne($db);
2724 foreach ($lines[$i]->detail_batch as $detail_batch) {
2725 print '<tr>';
2726 // Qty to ship or shipped
2727 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>';
2728 // Batch number management
2729 if ($lines[$i]->entrepot_id == 0) {
2730 // only show lot numbers from src warehouse when shipping from multiple warehouses
2731 $line->fetch($detail_batch->fk_expeditiondet);
2732 }
2733 $entrepot_id = !empty($detail_batch->entrepot_id) ? $detail_batch->entrepot_id : $lines[$i]->entrepot_id;
2734 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>';
2735 print '</tr>';
2736 }
2737 // add a 0 qty lot row to be able to add a lot
2738 print '<tr>';
2739 // Qty to ship or shipped
2740 print '<td><input class="qtyl" name="qtyl'.$line_id.'_0" id="qtyl'.$line_id.'_0" type="text" size="4" value="0"></td>';
2741 // Batch number management
2742 print '<td>'.$formproduct->selectLotStock('', 'batchl'.$line_id.'_0', '', 1, 0, $lines[$i]->fk_product).'</td>';
2743 print '</tr>';
2744 } elseif (isModEnabled('stock')) {
2745 if ($lines[$i]->fk_product > 0) {
2746 if ($lines[$i]->entrepot_id > 0) {
2747 print '<!-- case edit 2 -->';
2748 print '<tr>';
2749 // Qty to ship or shipped
2750 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>';
2751 // Warehouse source
2752 print '<td>'.$formproduct->selectWarehouses($lines[$i]->entrepot_id, 'entl'.$line_id, '', 1, 0, $lines[$i]->fk_product, '', 1, 0, array(), 'minwidth200').'</td>';
2753 // Batch number management
2754 print '<td>';
2755 if (isModEnabled('productbatch')) {
2756 print ' - '.$langs->trans("NA");
2757 }
2758 print '</td>';
2759 print '</tr>';
2760 } elseif (count($lines[$i]->details_entrepot) > 1) {
2761 print '<!-- case edit 3 -->';
2762 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
2763 print '<tr>';
2764 // Qty to ship or shipped
2765 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>';
2766 // Warehouse source
2767 print '<td>'.$formproduct->selectWarehouses($detail_entrepot->entrepot_id, 'entl'.$detail_entrepot->line_id, '', 1, 0, $lines[$i]->fk_product, '', 1, 0, array(), 'minwidth200').'</td>';
2768 // Batch number management
2769 print '<td>';
2770 if (isModEnabled('productbatch')) {
2771 print ' - '.$langs->trans("NA");
2772 }
2773 print '</td>';
2774 print '</tr>';
2775 }
2776 } elseif ($lines[$i]->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
2777 print '<!-- case edit 4 -->';
2778 print '<tr>';
2779 // Qty to ship or shipped
2780 print '<td><input class="qtyl right" name="qtyl'.$line_id.'" id="qtyl'.$line_id.'" type="text" size="4" value="'.$lines[$i]->qty_shipped.'"></td>';
2781 print '<td><span class="opacitymedium">('.$langs->trans("Service").')</span></td>';
2782 print '<td></td>';
2783 print '</tr>';
2784 } else {
2785 print '<!-- case edit 5 -->';
2786 print '<tr><td colspan="3">'.$langs->trans("ErrorStockIsNotEnough").'</td></tr>';
2787 }
2788 } else {
2789 print '<!-- case edit 6 -->';
2790 print '<tr>';
2791 // Qty to ship or shipped
2792 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>';
2793 // Warehouse source
2794 print '<td></td>';
2795 // Batch number management
2796 print '<td></td>';
2797 print '</tr>';
2798 }
2799 } elseif (!isModEnabled('stock') && empty($conf->productbatch->enabled)) { // both product batch and stock are not activated.
2800 print '<!-- case edit 7 -->';
2801 print '<tr>';
2802 // Qty to ship or shipped
2803 print '<td><input class="qtyl right" name="qtyl'.$line_id.'" id="qtyl'.$line_id.'" type="text" size="4" value="'.$lines[$i]->qty_shipped.'"></td>';
2804 // Warehouse source
2805 print '<td></td>';
2806 // Batch number management
2807 print '<td></td>';
2808 print '</tr>';
2809 }
2810
2811 print '</table></td>';
2812 } else {
2813 // Qty to ship or shipped
2814 print '<td class="linecolqtytoship center">'.$lines[$i]->qty_shipped.' '.$unit_order.'</td>';
2815
2816 // Warehouse source
2817 if (isModEnabled('stock')) {
2818 print '<td class="linecolwarehousesource tdoverflowmax200">';
2819 if ($lines[$i]->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
2820 print '<span class="opacitymedium">('.$langs->trans("Service").')</span>';
2821 } elseif ($lines[$i]->entrepot_id > 0 && $lines[$i]->stockable_product == Product::ENABLED_STOCK) {
2822 $warehouse_id = $lines[$i]->entrepot_id;
2823 if (!isset($conf->cache['warehouse'][$warehouse_id])) {
2824 $warehouse = new Entrepot($db);
2825 $warehouse->fetch($warehouse_id);
2826 $conf->cache['warehouse'][$warehouse_id] = $warehouse;
2827 } else {
2828 $warehouse = $conf->cache['warehouse'][$warehouse_id];
2829 }
2830 print $warehouse->getNomUrl(1);
2831 } elseif (count($lines[$i]->details_entrepot) > 1) {
2832 $detail = '';
2833 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
2834 $warehouse_id = $detail_entrepot->entrepot_id;
2835 if ($warehouse_id > 0) {
2836 if (!isset($conf->cache['warehouse'][$warehouse_id])) {
2837 $warehouse = new Entrepot($db);
2838 $warehouse->fetch($warehouse_id);
2839 $conf->cache['warehouse'][$warehouse_id] = $warehouse;
2840 } else {
2841 $warehouse = $conf->cache['warehouse'][$warehouse_id];
2842 }
2843 $detail .= $langs->trans("DetailWarehouseFormat", $warehouse->label, $detail_entrepot->qty_shipped).'<br>';
2844 }
2845 }
2846 print $form->textwithtooltip(img_picto('', 'object_stock').' '.$langs->trans("DetailWarehouseNumber"), $detail);
2847 } elseif (count($lines[$i]->detail_children ?? []) > 1) {
2848 $detail = '';
2849 foreach ($lines[$i]->detail_children as $child_product_id => $child_stock_list) {
2850 foreach ($child_stock_list as $warehouse_id => $total_qty) {
2851 // get product from cache
2852 $child_product_label = '';
2853 if (!isset($conf->cache['product'][$child_product_id])) {
2854 $child_product = new Product($db);
2855 $child_product->fetch($child_product_id);
2856 $conf->cache['product'][$child_product_id] = $child_product;
2857 } else {
2858 $child_product = $conf->cache['product'][$child_product_id];
2859 }
2860 $child_product_label = $child_product->ref . ' ' . $child_product->label;
2861
2862 // get warehouse from cache
2863 if (!isset($conf->cache['warehouse'][$warehouse_id])) {
2864 $child_warehouse = new Entrepot($db);
2865 $child_warehouse->fetch($warehouse_id);
2866 $conf->cache['warehouse'][$warehouse_id] = $child_warehouse;
2867 } else {
2868 $child_warehouse = $conf->cache['warehouse'][$warehouse_id];
2869 }
2870
2871 $detail .= $langs->trans('DetailChildrenFormat', $child_product_label, $child_warehouse->label, price2num($total_qty, 'MS')).'<br>';
2872 }
2873 }
2874 print $form->textwithtooltip(img_picto('', 'object_stock').' '.$langs->trans('DetailWarehouseNumber'), $detail);
2875 }
2876 print '</td>';
2877 }
2878
2879 // Batch number management
2880 if (isModEnabled('productbatch')) {
2881 if (isset($lines[$i]->detail_batch)) {
2882 print '<!-- Detail of lot -->';
2883 print '<td class="linecolbatch">';
2884 if ($lines[$i]->product_tobatch) {
2885 $detail = '';
2886 foreach ($lines[$i]->detail_batch as $dbatch) { // $dbatch is instance of ExpeditionLineBatch
2887 $detail .= $langs->trans("Batch").': '.$dbatch->batch;
2888 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY')) {
2889 $detail .= ' - '.$langs->trans("SellByDate").': '.dol_print_date($dbatch->sellby, "day");
2890 }
2891 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY')) {
2892 $detail .= ' - '.$langs->trans("EatByDate").': '.dol_print_date($dbatch->eatby, "day");
2893 }
2894 $detail .= ' - '.$langs->trans("Qty").': '.$dbatch->qty;
2895 $detail .= '<br>';
2896 }
2897 print $form->textwithtooltip(img_picto('', 'object_barcode').' '.$langs->trans("DetailBatchNumber"), $detail);
2898 } else {
2899 print $langs->trans("NA");
2900 }
2901 print '</td>';
2902 } else {
2903 print '<td class="linecolbatch" ></td>';
2904 }
2905 }
2906 }
2907
2908 // Weight
2909 print '<td class="center linecolweight">';
2910 if ($lines[$i]->fk_product_type == Product::TYPE_PRODUCT) {
2911 print $lines[$i]->weight * $lines[$i]->qty_shipped.' '.measuringUnitString(0, "weight", $lines[$i]->weight_units);
2912 } else {
2913 print '&nbsp;';
2914 }
2915 print '</td>';
2916
2917 // Volume
2918 print '<td class="center linecolvolume">';
2919 if ($lines[$i]->fk_product_type == Product::TYPE_PRODUCT) {
2920 print $lines[$i]->volume * $lines[$i]->qty_shipped.' '.measuringUnitString(0, "volume", $lines[$i]->volume_units);
2921 } else {
2922 print '&nbsp;';
2923 }
2924 print '</td>';
2925
2926 // Size
2927 //print '<td class="center">'.$lines[$i]->volume*$lines[$i]->qty_shipped.' '.measuringUnitString(0, "volume", $lines[$i]->volume_units).'</td>';
2928
2929 if ($action == 'editline' && $lines[$i]->id == $line_id) {
2930 print '<td class="center" colspan="2" valign="middle">';
2931 print '<input type="submit" class="button button-save" id="savelinebutton marginbottomonly" name="save" value="'.$langs->trans("Save").'"><br>';
2932 print '<input type="submit" class="button button-cancel" id="cancellinebutton" name="cancel" value="'.$langs->trans("Cancel").'"><br>';
2933 print '</td>';
2934 } elseif ($object->status == Expedition::STATUS_DRAFT) {
2935 $edit_url = $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=editline&token='.newToken().'&lineid='.$lines[$i]->id;
2936 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
2937 $product_id = $lines[$i]->fk_product;
2938 if (!isset($conf->cache['product'][$product_id])) {
2939 $product = new Product($db);
2940 $product->fetch($product_id);
2941 $conf->cache['product'][$product_id] = $product;
2942 } else {
2943 $product = $conf->cache['product'][$product_id];
2944 }
2945
2946 if ($product->hasFatherOrChild(1)) {
2947 $edit_url = dol_buildpath('/expedition/dispatch.php?id='.$object->id, 1);
2948 }
2949 }
2950
2951 // edit-delete buttons
2952 print '<td class="linecoledit center">';
2953 print '<a class="editfielda reposition" href="'.$edit_url.'">'.img_edit().'</a>';
2954 print '</td>';
2955 print '<td class="linecoldelete" width="10">';
2956 print '<a class="reposition" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=deleteline&token='.newToken().'&lineid='.$lines[$i]->id.'">'.img_delete().'</a>';
2957 print '</td>';
2958
2959 // Display lines extrafields
2960 if (!empty($rowExtrafieldsStart)) {
2961 print $rowExtrafieldsStart;
2962 print $rowExtrafieldsView;
2963 print $rowEnd;
2964 }
2965 }
2966 print "</tr>";
2967
2968 // Display lines extrafields.
2969 // $line is a line of shipment
2970 if (!empty($extrafields)) {
2971 $colspan = 6;
2972 if ($origin && $origin_id > 0) {
2973 $colspan++;
2974 }
2975 if (isModEnabled('productbatch')) {
2976 $colspan++;
2977 }
2978 if (isModEnabled('stock')) {
2979 $colspan++;
2980 }
2981
2982 $line = $lines[$i];
2983 $line->fetch_optionals();
2984
2985 // TODO Show all in same line by setting $display_type = 'line'
2986 if ($action == 'editline' && $line->id == $line_id) {
2987 print $lines[$i]->showOptionals($extrafields, 'edit', array('colspan' => $colspan), !empty($indiceAsked) ? $indiceAsked : '', '', '', 'card');
2988 } else {
2989 print $lines[$i]->showOptionals($extrafields, 'view', array('colspan' => $colspan), !empty($indiceAsked) ? $indiceAsked : '', '', '', 'card');
2990 }
2991 }
2992 } elseif (empty($reshook) && $lines[$i]->product_type == "9") {
2993 $objectsrc = new OrderLine($db);
2994 $objectsrc->fetch($lines[$i]->origin_line_id);
2995 if ($objectsrc->special_code == SUBTOTALS_SPECIAL_CODE) {
2996 $line = $lines[$i];
2997 require dol_buildpath('/core/tpl/subtotal_expedition_view.tpl.php');
2998 }
2999 }
3000 }
3001
3002 // TODO Show also lines ordered but not delivered
3003
3004 if (empty($num_prod)) {
3005 print '<tr><td colspan="8"><span class="opacitymedium">'.$langs->trans("NoLineGoOnTabToAddSome", $langs->transnoentitiesnoconv("ShipmentDistribution")).'</span></td></tr>';
3006 }
3007
3008 print "</table>\n";
3009 print '</tbody>';
3010 print '</div>';
3011
3012
3013 print dol_get_fiche_end();
3014
3015
3016 $object->fetchObjectLinked($object->id, $object->element);
3017
3018
3019 /*
3020 * Boutons actions
3021 */
3022
3023 if (($user->socid == 0) && ($action != 'presend')) {
3024 print '<div class="tabsAction">';
3025
3026 $parameters = array();
3027 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
3028 // modified by hook
3029 if (empty($reshook)) {
3030 if ($object->status == Expedition::STATUS_DRAFT && $num_prod > 0) {
3031 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'creer'))
3032 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'shipping_advance', 'validate'))) {
3033 print dolGetButtonAction('', $langs->trans('Validate'), 'default', $_SERVER["PHP_SELF"].'?action=valid&token='.newToken().'&id='.$object->id, '');
3034 } else {
3035 print dolGetButtonAction($langs->trans('NotAllowed'), $langs->trans('Validate'), 'default', $_SERVER['PHP_SELF']. '#', '', false);
3036 }
3037 }
3038
3039 // 0=draft, 1=validated/delivered, 2=closed/delivered
3040 if ($object->status == Expedition::STATUS_VALIDATED && !getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) {
3041 if ($user->hasRight('expedition', 'creer')) {
3042 print dolGetButtonAction('', $langs->trans('SetToDraft'), 'default', $_SERVER["PHP_SELF"].'?action=setdraft&token='.newToken().'&id='.$object->id, '');
3043 }
3044 }
3045 if ($object->status == Expedition::STATUS_CLOSED) {
3046 if ($user->hasRight('expedition', 'creer')) {
3047 print dolGetButtonAction('', $langs->trans('ReOpen'), 'default', $_SERVER["PHP_SELF"].'?action=reopen&token='.newToken().'&id='.$object->id, '');
3048 }
3049 }
3050
3051 // Send
3052 if (empty($user->socid)) {
3053 if ($object->status > 0) {
3054 if (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') || $user->hasRight('expedition', 'shipping_advance', 'send')) {
3055 print dolGetButtonAction('', $langs->trans('SendMail'), 'default', $_SERVER["PHP_SELF"].'?action=presend&token='.newToken().'&id='.$object->id.'&mode=init#formmailbeforetitle', '');
3056 } else {
3057 print dolGetButtonAction('', $langs->trans('SendMail'), 'default', $_SERVER['PHP_SELF']. '#', '', false);
3058 }
3059 }
3060 }
3061
3062 // This is just to generate a delivery receipt when option to do this is on
3063 //var_dump($object->linkedObjectsIds['delivery']);
3064 if (getDolGlobalInt('MAIN_SUBMODULE_DELIVERY') && ($object->status == Expedition::STATUS_VALIDATED || $object->status == Expedition::STATUS_CLOSED) && $user->hasRight('expedition', 'delivery', 'creer') && empty($object->linkedObjectsIds['delivery'])) {
3065 print dolGetButtonAction('', $langs->trans('CreateDeliveryOrder'), 'default', $_SERVER["PHP_SELF"] . '?action=create_delivery&token=' . newToken() . '&id=' . $object->id, '');
3066 }
3067
3068 // Sign (to set to status "Signed" without using the online signature page)
3069 if ($object->status > Expedition::STATUS_DRAFT) {
3070 if ($object->signed_status != Expedition::$SIGNED_STATUSES['STATUS_SIGNED_ALL']) {
3071 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>';
3072 } else {
3073 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>';
3074 }
3075 }
3076
3077 // Create bill
3078 if (isModEnabled('invoice') && ($object->status == Expedition::STATUS_VALIDATED || $object->status == Expedition::STATUS_CLOSED)) {
3079 if ($user->hasRight('facture', 'creer')) {
3080 if (getDolGlobalString('WORKFLOW_BILL_ON_SHIPMENT') !== '0') {
3081 print dolGetButtonAction('', $langs->trans('CreateBill'), 'default', DOL_URL_ROOT.'/compta/facture/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid, '');
3082 }
3083 }
3084 }
3085
3086 // Set Billed and Closed
3087 if ($object->status == Expedition::STATUS_VALIDATED) {
3088 if ($user->hasRight('expedition', 'creer') && $object->status > 0) {
3089 if (!$object->billed && getDolGlobalString('WORKFLOW_BILL_ON_SHIPMENT') !== '0') {
3090 print dolGetButtonAction('', $langs->trans('ClassifyBilled'), 'default', $_SERVER["PHP_SELF"].'?action=classifybilled&token='.newToken().'&id='.$object->id, '');
3091 }
3092 print dolGetButtonAction('', $langs->trans("Close"), 'default', $_SERVER["PHP_SELF"] . '?action=classifyclosed&token=' . newToken() . '&id=' . $object->id, '');
3093 }
3094 }
3095
3096 // Cancel
3097 if ($object->status == Expedition::STATUS_VALIDATED) {
3098 if ($user->hasRight('expedition', 'creer')) {
3099 print dolGetButtonAction('', $langs->trans('Cancel'), 'danger', $_SERVER["PHP_SELF"].'?action=cancel&token='.newToken().'&id='.$object->id.'&mode=init#formmailbeforetitle', '');
3100 }
3101 }
3102
3103 // Delete
3104 if ($user->hasRight('expedition', 'supprimer')) {
3105 print dolGetButtonAction('', $langs->trans('Delete'), 'delete', $_SERVER["PHP_SELF"].'?action=delete&token='.newToken().'&id='.$object->id, '');
3106 }
3107 }
3108
3109 print '</div>';
3110 }
3111
3112
3113 /*
3114 * Documents generated
3115 */
3116
3117 if ($action != 'presend' && $action != 'editline') {
3118 print '<div class="fichecenter"><div class="fichehalfleft">';
3119
3120 $objectref = dol_sanitizeFileName($object->ref);
3121 $filedir = $conf->expedition->dir_output."/sending/".$objectref;
3122
3123 $urlsource = $_SERVER["PHP_SELF"]."?id=".$object->id;
3124
3125 $genallowed = $user->hasRight('expedition', 'lire');
3126 $delallowed = $user->hasRight('expedition', 'creer');
3127
3128 print $formfile->showdocuments('expedition', $objectref, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', '', '', $soc->default_lang);
3129
3130
3131 // Show links to link elements
3132 $tmparray = $form->showLinkToObjectBlock($object, array(), array('shipping'), 1);
3133 $linktoelem = $tmparray['linktoelem'];
3134 $htmltoenteralink = $tmparray['htmltoenteralink'];
3135 print $htmltoenteralink;
3136
3137 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
3138
3139 // Show online signature link
3140 $useonlinesignature = getDolGlobalInt('EXPEDITION_ALLOW_ONLINESIGN');
3141
3142 if ($object->status != Expedition::STATUS_DRAFT && $useonlinesignature) {
3143 print '<br><!-- Link to sign -->';
3144 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
3145 print showOnlineSignatureUrl('expedition', $object->ref, $object).'<br>';
3146 }
3147
3148 print '</div><div class="fichehalfright">';
3149
3150 // List of actions on element
3151 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
3152 $formactions = new FormActions($db);
3153 $somethingshown = $formactions->showactions($object, 'shipping', $socid, 1);
3154
3155 print '</div></div>';
3156 }
3157
3158
3159 /*
3160 * Action presend
3161 */
3162
3163 //Select mail models is same action as presend
3164 if (GETPOST('modelselected')) {
3165 $action = 'presend';
3166 }
3167
3168 // Presend form
3169 $modelmail = 'shipping_send';
3170 $defaulttopic = 'SendShippingRef';
3171 $diroutput = $conf->expedition->dir_output.'/sending';
3172 $trackid = 'shi'.$object->id;
3173
3174 include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php';
3175}
3176
3177// End of page
3178llxFooter();
3179$db->close();
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:67
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_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 standard extra fields.
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 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.
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.
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...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
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.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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 '.
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)
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
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'.
dolGetButtonAction($label, $text='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
print_date_range($date_start, $date_end, $format='', $outputlangs=null)
Format output for start and end date.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
GETPOSTFLOAT($paramname, $rounding='')
Return the value of a $_GET or $_POST supervariable, converted into float.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
dol_clone($object, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0)
Clean a string to use it as a file name.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='')
Show information in HTML for admin users or standard users.
img_edit($titlealt='default', $float=0, $other='')
Show logo edit/modify fiche.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
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.