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 foreach ($object->lines as $line) {
1270 if ($line->special_code == SUBTOTALS_SPECIAL_CODE) {
1271 $show_check_add_buttons = true;
1272 break;
1273 }
1274 }
1275 }
1276 print '<tr class="liste_titre">';
1277 print '<td>';
1278 if (isset($show_check_add_buttons)) {
1279 print $form->showCheckAddButtons('checkforselect');
1280 }
1281 print $langs->trans("Description");
1282 print '</td>';
1283 print '<td class="center">'.$langs->trans("QtyOrdered").'</td>';
1284 print '<td class="center">'.$langs->trans("QtyShipped").'</td>';
1285 print '<td class="center">'.$langs->trans("QtyToShip");
1286 if (empty($conf->productbatch->enabled)) {
1287 print '<br><a href="#" id="autofill" class="opacitymedium link cursor cursorpointer">'.img_picto($langs->trans("Autofill"), 'autofill', 'class="paddingrightonly"').'</a>';
1288 print ' / ';
1289 } else {
1290 print '<br>';
1291 }
1292 print '<span id="autoreset" class="opacitymedium link cursor cursorpointer">'.img_picto($langs->trans("Reset"), 'eraser').'</span>';
1293 print '</td>';
1294 if (isModEnabled('stock')) {
1295 if (empty($conf->productbatch->enabled)) {
1296 print '<td class="left">'.$langs->trans("Warehouse").' ('.$langs->trans("Stock").')</td>';
1297 } else {
1298 print '<td class="left">'.$langs->trans("Warehouse").' / '.$langs->trans("Batch").' ('.$langs->trans("Stock").')</td>';
1299 }
1300 }
1301 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1302 print '<td class="left">'.$langs->trans('StockEntryDate').'</td>';
1303 }
1304 print "</tr>\n";
1305 }
1306
1307 $warehouse_id = GETPOSTINT('entrepot_id');
1308 $warehousePicking = array();
1309 // get all warehouse children for picking
1310 if ($warehouse_id > 0) {
1311 $warehousePicking[] = $warehouse_id;
1312 $warehouseObj = new Entrepot($db);
1313 $warehouseObj->get_children_warehouses($warehouse_id, $warehousePicking);
1314 }
1315
1316 $indiceAsked = 0;
1317 while ($indiceAsked < $numAsked) {
1318 $product = new Product($db);
1319
1320 $line = $object->lines[$indiceAsked];
1321
1322 $parameters = array('i' => $indiceAsked, 'line' => $line, 'num' => $numAsked);
1323 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $object, $action);
1324 if ($reshook < 0) {
1325 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1326 }
1327
1328 if (empty($reshook) && $line->special_code != SUBTOTALS_SPECIAL_CODE) {
1329 // Show product and description
1330 $type = $line->product_type ? $line->product_type : $line->fk_product_type;
1331 // Try to enhance type detection using date_start and date_end for free lines where type
1332 // was not saved.
1333 if (!empty($line->date_start)) {
1334 $type = 1;
1335 }
1336 if (!empty($line->date_end)) {
1337 $type = 1;
1338 }
1339
1340 print '<!-- line for order line '.$line->id.' -->'."\n";
1341 print '<tr class="oddeven" id="row-'.$line->id.'">'."\n";
1342
1343 $qtyProdCom = $line->qty;
1344 $productChildrenNb = 0;
1345 // Product label
1346 if ($line->fk_product > 0) { // If predefined product
1347 $res = $product->fetch($line->fk_product);
1348 if ($res < 0) {
1349 dol_print_error($db, $product->error, $product->errors);
1350 }
1351 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
1352 $productChildrenNb = $product->hasFatherOrChild(1);
1353 }
1354 if ($productChildrenNb > 0) {
1355 $product->loadStockForVirtualProduct('warehouseopen', $qtyProdCom);
1356 } else {
1357 $product->load_stock('warehouseopen'); // Load all $product->stock_warehouse[idwarehouse]->detail_batch
1358 }
1359 //var_dump($product->stock_warehouse[1]);
1360
1361 print '<td>';
1362 print '<a name="'.$line->id.'"></a>'; // ancre pour retourner sur la ligne
1363
1364 // Show product and description
1365 $product_static->type = $line->fk_product_type;
1366 $product_static->id = $line->fk_product;
1367 $product_static->ref = $line->ref;
1368 $product_static->status = $line->product_tosell;
1369 $product_static->status_buy = $line->product_tobuy;
1370 $product_static->status_batch = $line->product_tobatch;
1371
1372 $showdescinproductdesc = getDolGlobalString('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE');
1373
1374 $text = $product_static->getNomUrl(1);
1375 $text .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
1376 $description = ($showdescinproductdesc ? '' : dol_htmlentitiesbr($line->desc));
1377 $description .= empty($product->stockable_product) ? $langs->trans('StockDisabled') : $langs->trans('StockEnabled');
1378 print $form->textwithtooltip($text, $description, 3, 0, '', (string) $i);
1379
1380 // Show range
1381 print_date_range($db->jdate($line->date_start), $db->jdate($line->date_end));
1382
1383 // Add description in form
1384 if ($showdescinproductdesc) {
1385 print ($line->desc && $line->desc != $line->product_label) ? '<br>'.dol_htmlentitiesbr($line->desc) : '';
1386 }
1387
1388 print '</td>';
1389 } else {
1390 print "<td>";
1391 if ($type == 1) {
1392 $text = img_object($langs->trans('Service'), 'service');
1393 } else {
1394 $text = img_object($langs->trans('Product'), 'product');
1395 }
1396
1397 if (!empty($line->label)) {
1398 $text .= ' <strong>'.$line->label.'</strong>';
1399 print $form->textwithtooltip($text, $line->desc, 3, 0, '', (string) $i);
1400 } else {
1401 print $text.' '.nl2br($line->desc);
1402 }
1403
1404 // Show range
1405 print_date_range($db->jdate($line->date_start), $db->jdate($line->date_end));
1406 print "</td>\n";
1407 }
1408
1409 // unit of order
1410 $unit_order = '';
1411 if (getDolGlobalString('PRODUCT_USE_UNITS')) {
1412 $unit_order = measuringUnitString($line->fk_unit);
1413 }
1414
1415 // Qty
1416 print '<td class="center">'.$line->qty;
1417 print '<input name="qtyasked'.$indiceAsked.'" id="qtyasked'.$indiceAsked.'" type="hidden" value="'.$line->qty.'">';
1418 print ''.$unit_order.'</td>';
1419
1420 // Qty already shipped
1421 print '<td class="center">';
1422 $quantityDelivered = isset($object->expeditions[$line->id]) ? $object->expeditions[$line->id] : '';
1423 print $quantityDelivered;
1424 print '<input name="qtydelivered'.$indiceAsked.'" id="qtydelivered'.$indiceAsked.'" type="hidden" value="'.$quantityDelivered.'">';
1425 print ''.$unit_order.'</td>';
1426
1427 // Qty to ship
1428 $quantityAsked = $line->qty;
1429 if ($line->product_type == Product::TYPE_SERVICE && !getDolGlobalString('STOCK_SUPPORTS_SERVICES') && !getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
1430 $quantityToBeDelivered = 0;
1431 } else {
1432 if (is_numeric($quantityDelivered)) {
1433 $quantityToBeDelivered = $quantityAsked - $quantityDelivered;
1434 } else {
1435 $quantityToBeDelivered = $quantityAsked;
1436 }
1437 }
1438
1439 $warehouseObject = null;
1440 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
1441 print '<!-- Case warehouse already known or product not a predefined product -->';
1442 //ship from preselected location
1443 $stock = + (isset($product->stock_warehouse[$warehouse_id]->real) ? $product->stock_warehouse[$warehouse_id]->real : 0); // Convert to number
1444 if ($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
1445 $deliverableQty = $quantityToBeDelivered;
1446 } else {
1447 $deliverableQty = min($quantityToBeDelivered, $stock);
1448 }
1449 if ($deliverableQty < 0) {
1450 $deliverableQty = 0;
1451 }
1452 if (empty($conf->productbatch->enabled) || !$product->hasbatch()) {
1453 // Quantity to send
1454 print '<td class="center">';
1455 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || ($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES'))) {
1456 if (GETPOSTISSET('qtyl'.$indiceAsked) && GETPOST('qtyl'.$indiceAsked) !== '') {
1457 $deliverableQty = GETPOSTFLOAT('qtyl'.$indiceAsked, 'MS');
1458 }
1459 print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
1460 $qtylValue = $deliverableQty;
1461 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
1462 $qtylValue = '';
1463 }
1464 print '<input name="qtyl'.$indiceAsked.'" id="qtyl'.$indiceAsked.'" class="qtyl right" type="text" size="4" value="'.$qtylValue.'">';
1465 } else {
1466 if (getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
1467 print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
1468 print '<input name="qtyl'.$indiceAsked.'" id="qtyl'.$indiceAsked.'" type="hidden" value="0">';
1469 }
1470
1471 print $langs->trans("NA");
1472 }
1473 print '</td>';
1474
1475 // Stock
1476 if (isModEnabled('stock')) {
1477 print '<td class="left">';
1478 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) { // Type of product need stock change ?
1479 // Show warehouse combo list
1480 $ent = "entl".$indiceAsked;
1481 $idl = "idl".$indiceAsked;
1482 $tmpentrepot_id = is_numeric(GETPOST($ent)) ? GETPOSTINT($ent) : $warehouse_id;
1483 if ($line->fk_product > 0) {
1484 print '<!-- Show warehouse selection -->';
1485
1486 $stockMin = false;
1487 if (getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
1488 $stockMin = 0;
1489 }
1490 if ($productChildrenNb > 0) {
1491 print $formproduct->selectWarehouses($tmpentrepot_id, 'entl'.$indiceAsked, '', 1, 0, 0, '', 0, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
1492 } else {
1493 if ($product->stockable_product == Product::ENABLED_STOCK) {
1494 print $formproduct->selectWarehouses($tmpentrepot_id, 'entl'.$indiceAsked, '', 1, 0, $line->fk_product, '', 1, 0, array(), 'minwidth200', array(), 1, $stockMin, 'stock DESC, e.ref');
1495 } else {
1496 print img_warning().' '.$langs->trans('StockDisabled');
1497 }
1498 }
1499
1500 if ($tmpentrepot_id > 0 && $tmpentrepot_id == $warehouse_id) {
1501 //print $stock.' '.$quantityToBeDelivered;
1502 if ($stock < $quantityToBeDelivered) {
1503 print ' '.img_warning($langs->trans("StockTooLow")); // Stock too low for this $warehouse_id but you can change warehouse
1504 }
1505 }
1506 }
1507 } else {
1508 print '<span class="opacitymedium">('.$langs->trans("Service").')</span><input name="entl'.$indiceAsked.'" id="entl'.$indiceAsked.'" type="hidden" value="0">';
1509 }
1510 print '</td>';
1511 }
1512 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1513 print '<td></td>';
1514 } //StockEntrydate
1515 print "</tr>\n";
1516
1517 // Show subproducts of product
1518 if (getDolGlobalString('PRODUIT_SOUSPRODUITS') && $line->fk_product > 0) {
1519 $product->get_sousproduits_arbo();
1520 $prods_arbo = $product->get_arbo_each_prod($qtyProdCom);
1521 if (count($prods_arbo) > 0) {
1522 foreach ($prods_arbo as $key => $value) {
1523 //print $value[0];
1524 $img = '';
1525 if ($value['stock'] < $value['stock_alert']) {
1526 $img = img_warning($langs->trans("StockTooLow"));
1527 }
1528 print "<tr class=\"oddeven\"><td>&nbsp; &nbsp; &nbsp; ->
1529 <a href=\"".DOL_URL_ROOT."/product/card.php?id=".$value['id']."\">".$value['fullpath']."
1530 </a> (".$value['nb'].")</td><td class=\"center\"> ".$value['nb_total']."</td><td>&nbsp;</td><td>&nbsp;</td>
1531 <td class=\"center\">".$value['stock']." ".$img."</td>";
1532 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1533 print '<td></td>';
1534 } //StockEntrydate
1535 print "</tr>";
1536 }
1537 }
1538 }
1539 } else {
1540 // Product need lot
1541 print '<td></td><td></td>';
1542 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1543 print '<td></td>';
1544 } //StockEntrydate
1545 print '</tr>'; // end line and start a new one for lot/serial
1546 print '<!-- Case product need lot -->';
1547
1548 $staticwarehouse = new Entrepot($db);
1549 if ($warehouse_id > 0) {
1550 $staticwarehouse->fetch($warehouse_id);
1551 }
1552
1553 $subj = 0;
1554 // Define nb of lines suggested for this order line
1555 $nbofsuggested = 0;
1556 if (is_object($product->stock_warehouse[$warehouse_id]) && !empty($product->stock_warehouse[$warehouse_id]->detail_batch)) {
1557 foreach ($product->stock_warehouse[$warehouse_id]->detail_batch as $dbatch) {
1558 $nbofsuggested++;
1559 }
1560 }
1561 print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
1562 if (is_object($product->stock_warehouse[$warehouse_id]) && !empty($product->stock_warehouse[$warehouse_id]->detail_batch)) {
1563 foreach ($product->stock_warehouse[$warehouse_id]->detail_batch as $dbatch) { // $dbatch is instance of Productbatch
1564 //var_dump($dbatch);
1565 $batchStock = + $dbatch->qty; // To get a numeric
1566 $deliverableQty = min($quantityToBeDelivered, $batchStock);
1567
1568 // Now we will check if we have to reduce the deliverableQty by taking into account the qty already suggested in previous line
1569 if (isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
1570 $deliverableQty = min($quantityToBeDelivered, $batchStock - $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)]);
1571 } else {
1572 if (!isset($alreadyQtyBatchSetted[$line->fk_product])) {
1573 $alreadyQtyBatchSetted[$line->fk_product] = array();
1574 }
1575
1576 if (!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])) {
1577 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch] = array();
1578 }
1579
1580 $deliverableQty = min($quantityToBeDelivered, $batchStock);
1581 }
1582
1583 if ($deliverableQty < 0) {
1584 $deliverableQty = 0;
1585 }
1586
1587 $inputName = 'qtyl'.$indiceAsked.'_'.$subj;
1588 if (GETPOSTISSET($inputName)) {
1589 $deliverableQty = GETPOST($inputName, 'int');
1590 }
1591
1592 $tooltipClass = $tooltipTitle = '';
1593 if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
1594 $tooltipClass = ' classfortooltip';
1595 $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
1596 } else {
1597 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = 0 ;
1598 }
1599 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = $deliverableQty + $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
1600
1601 print '<!-- subj='.$subj.'/'.$nbofsuggested.' --><tr '.((($subj + 1) == $nbofsuggested) ? 'oddeven' : '').'>';
1602 print '<td colspan="3" ></td><td class="center">';
1603 $qtylValue = $deliverableQty;
1604 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
1605 $qtylValue = '';
1606 }
1607 print '<input class="qtyl '.$tooltipClass.' right" title="'.$tooltipTitle.'" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="'.$qtylValue.'">';
1608 print '</td>';
1609
1610 print '<!-- Show details of lot -->';
1611 print '<td class="left">';
1612
1613 print $staticwarehouse->getNomUrl(0).' / ';
1614
1615 print '<input name="batchl'.$indiceAsked.'_'.$subj.'" type="hidden" value="'.$dbatch->id.'">';
1616
1617 $detail = '';
1618 $detail .= $langs->trans("Batch").': '.$dbatch->batch;
1619 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY') && !empty($dbatch->sellby)) {
1620 $detail .= ' - '.$langs->trans("SellByDate").': '.dol_print_date($dbatch->sellby, "day");
1621 }
1622 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY') && !empty($dbatch->eatby)) {
1623 $detail .= ' - '.$langs->trans("EatByDate").': '.dol_print_date($dbatch->eatby, "day");
1624 }
1625 $detail .= ' - '.$langs->trans("Qty").': '.$dbatch->qty;
1626 $detail .= '<br>';
1627 print $detail;
1628
1629 $quantityToBeDelivered -= $deliverableQty;
1630 if ($quantityToBeDelivered < 0) {
1631 $quantityToBeDelivered = 0;
1632 }
1633 $subj++;
1634 print '</td>';
1635 if (getDolGlobalInt('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1636 print '<td>'.dol_print_date($dbatch->context['stock_entry_date'], 'day').'</td>'; //StockEntrydate
1637 }
1638 print '</tr>';
1639 }
1640 } else {
1641 print '<!-- Case there is no details of lot at all -->';
1642 print '<tr class="oddeven"><td colspan="3"></td><td class="center">';
1643 print '<input class="qtyl right" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="0" disabled="disabled"> ';
1644 print '</td>';
1645
1646 print '<td class="left">';
1647 print img_warning().' '.$langs->trans("NoProductToShipFoundIntoStock", $staticwarehouse->label);
1648 print '</td>';
1649 if (getDolGlobalInt('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1650 print '<td></td>';
1651 } //StockEntrydate
1652 print '</tr>';
1653 }
1654 }
1655 } else {
1656 // ship from multiple locations
1657 if (empty($conf->productbatch->enabled) || !$product->hasbatch()) {
1658 print '<!-- Case warehouse not already known and product does not need lot -->';
1659 print '<td></td><td></td>';
1660 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1661 print '<td></td>';
1662 }//StockEntrydate
1663 print '</tr>'."\n"; // end line and start a new one for each warehouse
1664
1665 print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
1666 $subj = 0;
1667 // Define nb of lines suggested for this order line
1668 $nbofsuggested = 0;
1669
1670 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
1671 if ($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
1672 $nbofsuggested++;
1673 }
1674 }
1675 $tmpwarehouseObject = new Entrepot($db);
1676 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) { // $stock_warehouse is product_stock
1677 $var = $subj % 2;
1678 if (!empty($warehousePicking) && !in_array($warehouse_id, $warehousePicking)) {
1679 // if a warehouse was selected by user, picking is limited to this warehouse and his children
1680 continue;
1681 }
1682
1683 $tmpwarehouseObject->fetch($warehouse_id);
1684 if ($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) {
1685 $stock = + $stock_warehouse->real; // Convert it to number
1686 $deliverableQty = min($quantityToBeDelivered, $stock);
1687 $deliverableQty = max(0, $deliverableQty);
1688 // Quantity to send
1689 print '<!-- subj='.$subj.'/'.$nbofsuggested.' --><tr '.((($subj + 1) == $nbofsuggested) ? 'oddeven' : '').'>';
1690 print '<td colspan="3" ></td><td class="center"><!-- qty to ship (no lot management for product line indiceAsked='.$indiceAsked.') -->';
1691 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES') || getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
1692 if (isset($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])) {
1693 $deliverableQty = min($quantityToBeDelivered, $stock - $alreadyQtySetted[$line->fk_product][intval($warehouse_id)]);
1694 } else {
1695 if (!isset($alreadyQtySetted[$line->fk_product])) {
1696 $alreadyQtySetted[$line->fk_product] = array();
1697 }
1698
1699 $deliverableQty = min($quantityToBeDelivered, $stock);
1700 }
1701
1702 if ($deliverableQty < 0) {
1703 $deliverableQty = 0;
1704 }
1705
1706 $tooltipClass = $tooltipTitle = '';
1707 if (!empty($alreadyQtySetted[$line->fk_product][intval($warehouse_id)])) {
1708 $tooltipClass = ' classfortooltip';
1709 $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$alreadyQtySetted[$line->fk_product][intval($warehouse_id)];
1710 } else {
1711 $alreadyQtySetted[$line->fk_product][intval($warehouse_id)] = 0;
1712 }
1713
1714 $alreadyQtySetted[$line->fk_product][intval($warehouse_id)] = $deliverableQty + $alreadyQtySetted[$line->fk_product][intval($warehouse_id)];
1715
1716 $inputName = 'qtyl'.$indiceAsked.'_'.$subj;
1717 if (GETPOSTISSET($inputName)) {
1718 $deliverableQty = GETPOSTINT($inputName);
1719 }
1720 $qtylValue = $deliverableQty;
1721 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
1722 $qtylValue = '';
1723 }
1724 print '<input class="qtyl'.$tooltipClass.' right" title="'.$tooltipTitle.'" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'" type="text" size="4" value="'.$qtylValue.'">';
1725 print '<input name="ent1'.$indiceAsked.'_'.$subj.'" type="hidden" value="'.$warehouse_id.'">';
1726 } else {
1727 if (getDolGlobalString('SHIPMENT_GETS_ALL_ORDER_PRODUCTS')) {
1728 print '<input name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'" type="hidden" value="0">';
1729 }
1730
1731 print $langs->trans("NA");
1732 }
1733 print '</td>';
1734
1735 // Stock
1736 if (isModEnabled('stock')) {
1737 print '<td class="left">';
1738 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
1739 if ($product->stockable_product == Product::ENABLED_STOCK || $productChildrenNb > 0) {
1740 print $tmpwarehouseObject->getNomUrl(0).' ';
1741 if ($productChildrenNb <= 0) {
1742 print '<!-- Show details of stock -->';
1743 print '('.$stock.')';
1744 }
1745 } else {
1746 print img_warning().' '.$langs->trans('StockDisabled');
1747 }
1748 } else {
1749 print '<span class="opacitymedium">('.$langs->trans("Service").')</span>';
1750 }
1751 print '</td>';
1752 }
1753 $quantityToBeDelivered -= $deliverableQty;
1754 if ($quantityToBeDelivered < 0) {
1755 $quantityToBeDelivered = 0;
1756 }
1757 $subj++;
1758 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1759 print '<td></td>';
1760 }//StockEntrydate
1761 print "</tr>\n";
1762 }
1763 }
1764 // Show subproducts of product (not recommended)
1765 if (getDolGlobalString('PRODUIT_SOUSPRODUITS') && $line->fk_product > 0) {
1766 $product->get_sousproduits_arbo();
1767 $prods_arbo = $product->get_arbo_each_prod($qtyProdCom);
1768 if (count($prods_arbo) > 0) {
1769 foreach ($prods_arbo as $key => $value) {
1770 //print $value[0];
1771 $img = '';
1772 if ($value['stock'] < $value['stock_alert']) {
1773 $img = img_warning($langs->trans("StockTooLow"));
1774 }
1775 print '<tr class"oddeven"><td>';
1776 print "&nbsp; &nbsp; &nbsp; ->
1777 <a href=\"".DOL_URL_ROOT."/product/card.php?id=".$value['id']."\">".$value['fullpath']."
1778 </a> (".$value['nb'].")</td><td class=\"center\"> ".$value['nb_total']."</td><td>&nbsp;</td><td>&nbsp;</td>
1779 <td class=\"center\">".$value['stock']." ".$img."</td>";
1780 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1781 print '<td></td>';
1782 }//StockEntrydate
1783 print "</tr>";
1784 }
1785 }
1786 }
1787 } else {
1788 print '<!-- Case warehouse not already known and product need lot -->';
1789 print '<td></td><td></td>';
1790 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1791 print '<td></td>';
1792 }//StockEntrydate
1793 print '</tr>'; // end line and start a new one for lot/serial
1794
1795 $subj = 0;
1796 print '<input name="idl'.$indiceAsked.'" type="hidden" value="'.$line->id.'">';
1797
1798 $tmpwarehouseObject = new Entrepot($db);
1799 $productlotObject = new Productlot($db);
1800
1801 // Define nb of lines suggested for this order line
1802 $nbofsuggested = 0;
1803 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
1804 if (($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) && (!empty($stock_warehouse->detail_batch))) {
1805 $nbofsuggested += count($stock_warehouse->detail_batch);
1806 }
1807 }
1808
1809 foreach ($product->stock_warehouse as $warehouse_id => $stock_warehouse) {
1810 $var = $subj % 2;
1811 if (!empty($warehousePicking) && !in_array($warehouse_id, $warehousePicking)) {
1812 // if a warehouse was selected by user, picking is limited to this warehouse and his children
1813 continue;
1814 }
1815
1816 $tmpwarehouseObject->fetch($warehouse_id);
1817 if (($stock_warehouse->real > 0 || !getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER')) && (!empty($stock_warehouse->detail_batch))) {
1818 foreach ($stock_warehouse->detail_batch as $dbatch) {
1819 $batchStock = + $dbatch->qty; // To get a numeric
1820 if (isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
1821 $deliverableQty = min($quantityToBeDelivered, $batchStock - $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)]);
1822 } else {
1823 if (!isset($alreadyQtyBatchSetted[$line->fk_product])) {
1824 $alreadyQtyBatchSetted[$line->fk_product] = array();
1825 }
1826
1827 if (!isset($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch])) {
1828 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch] = array();
1829 }
1830
1831 $deliverableQty = min($quantityToBeDelivered, $batchStock);
1832 }
1833
1834 if ($deliverableQty < 0) {
1835 $deliverableQty = 0;
1836 }
1837
1838 $inputName = 'qtyl'.$indiceAsked.'_'.$subj;
1839 if (GETPOSTISSET($inputName)) {
1840 $deliverableQty = GETPOSTINT($inputName);
1841 }
1842
1843 $tooltipClass = $tooltipTitle = '';
1844 if (!empty($alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)])) {
1845 $tooltipClass = ' classfortooltip';
1846 $tooltipTitle = $langs->trans('StockQuantitiesAlreadyAllocatedOnPreviousLines').' : '.$alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
1847 } else {
1848 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = 0 ;
1849 }
1850 $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)] = $deliverableQty + $alreadyQtyBatchSetted[$line->fk_product][$dbatch->batch][intval($warehouse_id)];
1851
1852 print '<!-- subj='.$subj.'/'.$nbofsuggested.' --><tr '.((($subj + 1) == $nbofsuggested) ? 'oddeven' : '').'><td colspan="3"></td><td class="center">';
1853 $qtylValue = $deliverableQty;
1854 if (getDolGlobalBool('SHIPMENT_DONT_PREFILL_QTY', false)) {
1855 $qtylValue = '';
1856 }
1857 print '<input class="qtyl right '.$tooltipClass.'" title="'.$tooltipTitle.'" name="'.$inputName.'" id="'.$inputName.'" type="text" size="4" value="'.$qtylValue.'">';
1858 print '</td>';
1859
1860 print '<td class="left">';
1861
1862 print $tmpwarehouseObject->getNomUrl(0).' / ';
1863
1864 print '<!-- Show details of lot -->';
1865 print '<input name="batchl'.$indiceAsked.'_'.$subj.'" type="hidden" value="'.$dbatch->id.'">';
1866
1867 //print '|'.$line->fk_product.'|'.$dbatch->batch.'|<br>';
1868 print $langs->trans("Batch").': ';
1869 $result = $productlotObject->fetch(0, $line->fk_product, $dbatch->batch);
1870 if ($result > 0) {
1871 print $productlotObject->getNomUrl(1);
1872 } else {
1873 print $langs->trans("TableLotIncompleteRunRepairWithParamStandardEqualConfirmed");
1874 }
1875 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY') && !empty($dbatch->sellby)) {
1876 print ' - '.$langs->trans("SellByDate").': '.dol_print_date($dbatch->sellby, "day");
1877 }
1878 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY') && !empty($dbatch->eatby)) {
1879 print ' - '.$langs->trans("EatByDate").': '.dol_print_date($dbatch->eatby, "day");
1880 }
1881 print ' ('.$dbatch->qty.')';
1882 $quantityToBeDelivered -= $deliverableQty;
1883 if ($quantityToBeDelivered < 0) {
1884 $quantityToBeDelivered = 0;
1885 }
1886 //dol_syslog('deliverableQty = '.$deliverableQty.' batchStock = '.$batchStock);
1887 $subj++;
1888 print '</td>';
1889 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1890 print '<td class="left">'.dol_print_date($dbatch->context['stock_entry_date'], 'day').'</td>';
1891 }
1892 print '</tr>';
1893 }
1894 }
1895 }
1896 }
1897 if ($subj == 0) { // Line not shown yet, we show it
1898 $warehouse_selected_id = GETPOSTINT('entrepot_id');
1899
1900 print '<!-- line not shown yet, we show it -->';
1901 print '<tr class="oddeven"><td colspan="3"></td><td class="center">';
1902
1903 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
1904 $disabled = '';
1905 if (isModEnabled('productbatch') && $product->hasbatch()) {
1906 $disabled = 'disabled="disabled"';
1907 }
1908 if ($warehouse_selected_id <= 0) { // We did not force a given warehouse, so we won't have no warehouse to change qty.
1909 $disabled = 'disabled="disabled"';
1910 }
1911 // finally we overwrite the input with the product status stockable_product if it's disabled
1912 if ($product->stockable_product == Product::DISABLED_STOCK) {
1913 $disabled = '';
1914 }
1915 print '<input class="qtyl right" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="0"'.($disabled ? ' '.$disabled : '').'> ';
1916 if (empty($disabled) && (!getDolGlobalInt('STOCK_DISALLOW_NEGATIVE_TRANSFER') || $product->stockable_product == Product::DISABLED_STOCK)) {
1917 print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $warehouse_selected_id . '">';
1918 }
1919 } elseif ($line->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
1920 $disabled = '';
1921 if (isModEnabled('productbatch') && $product->hasbatch()) {
1922 $disabled = 'disabled="disabled"';
1923 }
1924 if ($warehouse_selected_id <= 0) { // We did not force a given warehouse, so we won't have no warehouse to change qty.
1925 $disabled = 'disabled="disabled"';
1926 }
1927 print '<input class="qtyl right" name="qtyl'.$indiceAsked.'_'.$subj.'" id="qtyl'.$indiceAsked.'_'.$subj.'" type="text" size="4" value="'.$quantityToBeDelivered.'"'.($disabled ? ' '.$disabled : '').'> ';
1928 if (empty($disabled)) {
1929 print '<input name="ent1' . $indiceAsked . '_' . $subj . '" type="hidden" value="' . $warehouse_selected_id . '">';
1930 }
1931 } else {
1932 print $langs->trans("NA");
1933 }
1934 print '</td>';
1935
1936 print '<td class="left">';
1937 if ($line->product_type == Product::TYPE_PRODUCT || getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
1938 if ($warehouse_selected_id > 0 && $product->stockable_product == Product::ENABLED_STOCK) {
1939 $warehouseObject = new Entrepot($db);
1940 $warehouseObject->fetch($warehouse_selected_id);
1941 print img_warning().' '.$langs->trans("NoProductToShipFoundIntoStock", $warehouseObject->label);
1942 } else {
1943 if ($line->fk_product) {
1944 if ($product->stockable_product == Product::ENABLED_STOCK) {
1945 print img_warning().' '.$langs->trans('StockTooLow');
1946 } else {
1947 print img_warning().' '.$langs->trans('StockDisabled');
1948 }
1949 } else {
1950 print '';
1951 }
1952 }
1953 } else {
1954 print '<span class="opacitymedium">('.$langs->trans("Service").')</span>';
1955 }
1956 print '</td>';
1957 if (getDolGlobalString('SHIPPING_DISPLAY_STOCK_ENTRY_DATE')) {
1958 print '<td></td>';
1959 }//StockEntrydate
1960 print '</tr>';
1961 }
1962 }
1963
1964 // Display lines for extrafields of the Shipment line
1965 // $line is a 'Order line'
1966 if (!empty($extrafields)) {
1967 //var_dump($line);
1968 $colspan = 5;
1969 $expLine = new ExpeditionLigne($db);
1970
1971 $srcLine = new OrderLine($db);
1972 $srcLine->id = $line->id;
1973 $srcLine->fetch_optionals(); // fetch extrafields also available in orderline
1974
1975 $expLine->array_options = array_merge($expLine->array_options, $srcLine->array_options);
1976
1977 print $expLine->showOptionals($extrafields, 'edit', array('style' => 'class="drag drop oddeven"', 'colspan' => $colspan), (string) $indiceAsked, '', '1');
1978 }
1979 } elseif (empty($reshook) && $line->special_code == SUBTOTALS_SPECIAL_CODE && !in_array($line->id, $title_lines_to_disable)) {
1980 require dol_buildpath('/core/tpl/subtotalline_select.tpl.php');
1981 }
1982
1983 $indiceAsked++;
1984 }
1985
1986 print "</table>";
1987 print '</div>';
1988
1989 print '<br>';
1990
1991 print $form->buttonsSaveCancel("Create");
1992
1993 print '</form>';
1994
1995 print '<br>';
1996 } else {
1997 dol_print_error($db);
1998 }
1999 }
2000} elseif ($object->id > 0) {
2001 '@phan-var-force Expedition $object'; // Need to force it (type overridden earlier)
2002
2003 // Edit and view mode
2004
2005 $lines = $object->lines;
2006
2007 $num_prod = count($lines);
2008
2009 if (!empty($object->origin) && $object->origin_id > 0) {
2010 $typeobject = $object->origin;
2011 $origin = $object->origin;
2012 $origin_id = $object->origin_id;
2013
2014 $object->fetch_origin(); // Load property $object->origin_object (old $object->commande, $object->propal, ...)
2015 }
2016
2017 $soc = new Societe($db);
2018 $soc->fetch($object->socid);
2019
2020 $res = $object->fetch_optionals();
2021
2023 print dol_get_fiche_head($head, 'shipping', $langs->trans("Shipment"), -1, $object->picto);
2024
2025 $formconfirm = '';
2026
2027 // Confirm deletion
2028 if ($action == 'delete') {
2029 $formquestion = array();
2030 if ($object->status == Expedition::STATUS_CLOSED && getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) {
2031 $formquestion = array(
2032 array(
2033 'label' => $langs->trans('ShipmentIncrementStockOnDelete'),
2034 'name' => 'alsoUpdateStock',
2035 'type' => 'checkbox',
2036 'value' => 0
2037 ),
2038 );
2039 }
2040 $formconfirm = $form->formconfirm(
2041 $_SERVER['PHP_SELF'].'?id='.$object->id,
2042 $langs->trans('DeleteSending'),
2043 $langs->trans("ConfirmDeleteSending", $object->ref),
2044 'confirm_delete',
2045 $formquestion,
2046 0,
2047 1
2048 );
2049 }
2050
2051 // Confirm delete of subtotal line
2052 if ($action == 'ask_subtotal_deleteline') {
2053 $lineid = GETPOSTINT('lineid');
2054 $langs->load("subtotals");
2055 $title = "DeleteSubtotalLine";
2056 $question = "ConfirmDeleteSubtotalLine";
2057 if (GETPOST('type') == 'title') {
2058 $formconfirm = array(array('type' => 'checkbox', 'name' => 'deletecorrespondingsubtotalline', 'label' => $langs->trans("DeleteCorrespondingSubtotalLine"), 'value' => 0));
2059 $title = "DeleteTitleLine";
2060 $question = "ConfirmDeleteTitleLine";
2061 }
2062 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans($title), $langs->trans($question), 'confirm_delete_subtotalline', $formconfirm, 'no', 1);
2063 }
2064
2065 // Confirm validation
2066 if ($action == 'valid') {
2067 $objectref = substr($object->ref, 1, 4);
2068 if ($objectref == 'PROV') {
2069 $numref = $object->getNextNumRef($soc);
2070 } else {
2071 $numref = $object->ref;
2072 }
2073
2074 $text = $langs->trans("ConfirmValidateSending", $numref);
2075 if (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) {
2076 $text .= '<br>'.img_picto('', 'movement', 'class="pictofixedwidth"').$langs->trans("StockMovementWillBeRecorded").'.';
2077 } elseif (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')) {
2078 $text .= '<br>'.img_picto('', 'movement', 'class="pictofixedwidth"').$langs->trans("StockMovementNotYetRecorded").'.';
2079 }
2080
2081 if (isModEnabled('notification')) {
2082 require_once DOL_DOCUMENT_ROOT.'/core/class/notify.class.php';
2083 $notify = new Notify($db);
2084 $text .= '<br>';
2085 $text .= $notify->confirmMessage('SHIPPING_VALIDATE', $object->socid, $object);
2086 }
2087
2088 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id, $langs->trans('ValidateSending'), $text, 'confirm_valid', '', 0, 1, 260);
2089 }
2090
2091 // Confirm cancellation
2092 if ($action == 'cancel') {
2093 $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?id='.$object->id, $langs->trans('CancelSending'), $langs->trans("ConfirmCancelSending", $object->ref), 'confirm_cancel', '', 0, 1);
2094 }
2095
2096 // Confirm sign
2097 if ($action == 'sign') {
2098 $text = $langs->trans('ConfirmSignShipping');
2099 if (isModEnabled('notification')) {
2100 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2101 $notify = new Notify($db);
2102 $text .= '<br>';
2103 $text .= $notify->confirmMessage('SHIPPING_MODIFY', $object->socid, $object);
2104 }
2105 $formquestion = [];
2106 $formquestion[] = [
2107 'type' => 'select',
2108 'name' => 'signed_status',
2109 'select_show_empty' => 0,
2110 'label' => '<span class="fieldrequired">' . $langs->trans('SignStatus') . '</span>',
2111 'values' => $object->getSignedStatusLocalisedArray()
2112 ];
2113 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('SignShipping'), $text, 'confirm_sign', $formquestion, 0, 1);
2114 }
2115
2116 // Confirm unsign
2117 if ($action == 'unsign') {
2118 $text = $langs->trans('ConfirmUnsignShipping');
2119 if (isModEnabled('notification')) {
2120 require_once DOL_DOCUMENT_ROOT . '/core/class/notify.class.php';
2121 $notify = new Notify($db);
2122 $text .= '<br>';
2123 $text .= $notify->confirmMessage('SHIPPING_MODIFY', $object->socid, $object);
2124 }
2125 $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"] . '?id=' . $object->id, $langs->trans('UnsignShipping'), $text, 'confirm_unsign', '', 0, 1);
2126 }
2127
2128 // Call Hook formConfirm
2129 $parameters = array('formConfirm' => $formconfirm);
2130 $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2131 if (empty($reshook)) {
2132 $formconfirm .= $hookmanager->resPrint;
2133 } elseif ($reshook > 0) {
2134 $formconfirm = $hookmanager->resPrint;
2135 }
2136
2137 // Print form confirm
2138 print $formconfirm;
2139
2140 // Calculate totalWeight and totalVolume for all products
2141 // by adding weight and volume of each product line.
2142 $tmparray = $object->getTotalWeightVolume();
2143 $totalWeight = $tmparray['weight'];
2144 $totalVolume = $tmparray['volume'];
2145
2146 if (!empty($typeobject) && $typeobject === 'commande' && is_object($object->origin_object) && $object->origin_object->id && isModEnabled('order')) {
2147 $objectsrc = new Commande($db);
2148 $objectsrc->fetch($object->origin_object->id);
2149 }
2150 if (!empty($typeobject) && $typeobject === 'propal' && is_object($object->origin_object) && $object->origin_object->id && isModEnabled("propal")) {
2151 $objectsrc = new Propal($db);
2152 $objectsrc->fetch($object->origin_object->id);
2153 }
2154
2155 // Shipment card
2156 $linkback = '<a href="'.DOL_URL_ROOT.'/expedition/list.php?restore_lastsearch_values=1'.(!empty($socid) ? '&socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
2157 $morehtmlref = '<div class="refidno">';
2158 // Ref customer shipment
2159 $morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_customer', $object->ref_customer, $object, $user->hasRight('expedition', 'creer'), 'string', '', 0, 1);
2160 $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);
2161 // Thirdparty
2162 $morehtmlref .= '<br>'.$object->thirdparty->getNomUrl(1);
2163 // Project
2164 if (isModEnabled('project')) {
2165 $langs->load("projects");
2166 $morehtmlref .= '<br>';
2167 if (0) { // Do not change on shipment
2168 $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
2169 if ($action != 'classify') {
2170 $morehtmlref .= '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=classify&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
2171 }
2172 $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');
2173 } else {
2174 if (!empty($objectsrc) && !empty($objectsrc->fk_project)) {
2175 $proj = new Project($db);
2176 $proj->fetch($objectsrc->fk_project);
2177 $morehtmlref .= $proj->getNomUrl(1);
2178 if ($proj->title) {
2179 $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
2180 }
2181 }
2182 }
2183 }
2184 $morehtmlref .= '</div>';
2185
2186
2187 dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
2188
2189
2190 print '<div class="fichecenter">';
2191 print '<div class="fichehalfleft">';
2192 print '<div class="underbanner clearboth"></div>';
2193
2194 print '<table class="border tableforfield centpercent">';
2195
2196 // Linked documents
2197 if (!empty($typeobject) && $typeobject == 'commande' && $object->origin_object->id && isModEnabled('order')) {
2198 print '<tr><td>';
2199 print $langs->trans("RefOrder").'</td>';
2200 print '<td>';
2201 print $objectsrc->getNomUrl(1, 'commande');
2202 print "</td>\n";
2203 print '</tr>';
2204 }
2205 if (!empty($typeobject) && $typeobject == 'propal' && $object->origin_object->id && isModEnabled("propal")) {
2206 print '<tr><td>';
2207 print $langs->trans("RefProposal").'</td>';
2208 print '<td>';
2209 print $objectsrc->getNomUrl(1, 'expedition');
2210 print "</td>\n";
2211 print '</tr>';
2212 }
2213
2214 // Date creation
2215 print '<tr><td class="titlefieldmiddle">'.$langs->trans("DateCreation").'</td>';
2216 print '<td>'.dol_print_date($object->date_creation, "dayhour")."</td>\n";
2217 print '</tr>';
2218
2219 // Delivery date planned
2220 print '<tr><td height="10">';
2221 print '<table class="nobordernopadding centpercent"><tr><td>';
2222 print $langs->trans('DateDeliveryPlanned');
2223 print '</td>';
2224 if ($action != 'editdate_livraison') {
2225 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>';
2226 }
2227 print '</tr></table>';
2228 print '</td><td>';
2229 if ($action == 'editdate_livraison') {
2230 print '<form name="setdate_livraison" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="post">';
2231 print '<input type="hidden" name="token" value="'.newToken().'">';
2232 print '<input type="hidden" name="action" value="setdate_livraison">';
2233 print $form->selectDate($object->date_delivery ? $object->date_delivery : -1, 'liv_', 1, 1, 0, "setdate_livraison", 1, 0);
2234 print '<input type="submit" class="button button-edit smallpaddingimp" value="'.$langs->trans('Modify').'">';
2235 print '</form>';
2236 } else {
2237 print $object->date_delivery ? dol_print_date($object->date_delivery, 'dayhour') : '&nbsp;';
2238 }
2239 print '</td>';
2240 print '</tr>';
2241
2242 // Delivery sending date
2243 print '<tr><td height="10">';
2244 print '<table class="nobordernopadding centpercent"><tr><td>';
2245 print $langs->trans('DateShipping');
2246 print '</td>';
2247 if ($action != 'editdate_shipping') {
2248 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>';
2249 }
2250 print '</tr></table>';
2251 print '</td><td>';
2252 if ($action == 'editdate_shipping') {
2253 print '<form name="setdate_shipping" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="post">';
2254 print '<input type="hidden" name="token" value="'.newToken().'">';
2255 print '<input type="hidden" name="action" value="setdate_shipping">';
2256 print $form->selectDate($object->date_shipping ? $object->date_shipping : -1, 'ship_', 1, 1, 0, "setdate_shipping", 1, 0);
2257 print '<input type="submit" class="button button-edit smallpaddingimp" value="'.$langs->trans('Modify').'">';
2258 print '</form>';
2259 } else {
2260 print $object->date_shipping ? dol_print_date($object->date_shipping, 'dayhour') : '&nbsp;';
2261 }
2262 print '</td>';
2263 print '</tr>';
2264
2265 // Weight
2266 print '<tr><td>';
2267 print $form->editfieldkey("Weight", 'trueWeight', $object->trueWeight, $object, $user->hasRight('expedition', 'creer'));
2268 print '</td><td>';
2269
2270 if ($action == 'edittrueWeight') {
2271 print '<form name="settrueweight" action="'.$_SERVER["PHP_SELF"].'" method="post">';
2272 print '<input name="action" value="settrueWeight" type="hidden">';
2273 print '<input name="id" value="'.$object->id.'" type="hidden">';
2274 print '<input type="hidden" name="token" value="'.newToken().'">';
2275 print '<input id="trueWeight" name="trueWeight" value="'.$object->trueWeight.'" type="text" class="width50 valignmiddle">';
2276 print $formproduct->selectMeasuringUnits("weight_units", "weight", (string) $object->weight_units, 0, 2, 'maxwidth125 valignmiddle');
2277 print ' <input class="button smallpaddingimp valignmiddle" name="modify" value="'.$langs->trans("Modify").'" type="submit">';
2278 print ' <input class="button button-cancel smallpaddingimp valignmiddle" name="cancel" value="'.$langs->trans("Cancel").'" type="submit">';
2279 print '</form>';
2280 } else {
2281 print $object->trueWeight;
2282 print ($object->trueWeight && $object->weight_units != '') ? ' '.measuringUnitString(0, "weight", $object->weight_units) : '';
2283 }
2284
2285 // Calculated
2286 if ($totalWeight > 0) {
2287 if (!empty($object->trueWeight)) {
2288 print ' ('.$langs->trans("SumOfProductWeights").': ';
2289 }
2290 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');
2291 if (!empty($object->trueWeight)) {
2292 print ')';
2293 }
2294 }
2295 print '</td></tr>';
2296
2297 // Width
2298 print '<tr><td>'.$form->editfieldkey("Width", 'trueWidth', $object->trueWidth, $object, $user->hasRight('expedition', 'creer')).'</td><td>';
2299 print $form->editfieldval("Width", 'trueWidth', $object->trueWidth, $object, $user->hasRight('expedition', 'creer'));
2300 print ($object->trueWidth && $object->width_units != '') ? ' '.measuringUnitString(0, "size", $object->width_units) : '';
2301 print '</td></tr>';
2302
2303 // Height
2304 print '<tr><td>'.$form->editfieldkey("Height", 'trueHeight', $object->trueHeight, $object, $user->hasRight('expedition', 'creer')).'</td><td>';
2305 if ($action == 'edittrueHeight') {
2306 print '<form name="settrueHeight" action="'.$_SERVER["PHP_SELF"].'" method="post">';
2307 print '<input name="action" value="settrueHeight" type="hidden">';
2308 print '<input name="id" value="'.$object->id.'" type="hidden">';
2309 print '<input type="hidden" name="token" value="'.newToken().'">';
2310 print '<input id="trueHeight" name="trueHeight" value="'.$object->trueHeight.'" type="text" class="width50">';
2311 print $formproduct->selectMeasuringUnits("size_units", "size", $object->size_units, 0, 2);
2312 print ' <input class="button smallpaddingimp" name="modify" value="'.$langs->trans("Modify").'" type="submit">';
2313 print ' <input class="button button-cancel smallpaddingimp" name="cancel" value="'.$langs->trans("Cancel").'" type="submit">';
2314 print '</form>';
2315 } else {
2316 print $object->trueHeight;
2317 print ($object->trueHeight && $object->height_units != '') ? ' '.measuringUnitString(0, "size", $object->height_units) : '';
2318 }
2319
2320 print '</td></tr>';
2321
2322 // Depth
2323 print '<tr><td>'.$form->editfieldkey("Depth", 'trueDepth', $object->trueDepth, $object, $user->hasRight('expedition', 'creer')).'</td><td>';
2324 print $form->editfieldval("Depth", 'trueDepth', $object->trueDepth, $object, $user->hasRight('expedition', 'creer'));
2325 print ($object->trueDepth && $object->depth_units != '') ? ' '.measuringUnitString(0, "size", $object->depth_units) : '';
2326 print '</td></tr>';
2327
2328 // Volume
2329 print '<tr><td>';
2330 print $langs->trans("Volume");
2331 print '</td>';
2332 print '<td>';
2333 $calculatedVolume = 0;
2334 $volumeUnit = 0;
2335 if ($object->trueWidth && $object->trueHeight && $object->trueDepth) {
2336 $calculatedVolume = ($object->trueWidth * $object->trueHeight * $object->trueDepth);
2337 $volumeUnit = $object->size_units * 3;
2338 }
2339 // If sending volume not defined we use sum of products
2340 if ($calculatedVolume > 0) {
2341 if ($volumeUnit < 50) {
2342 print showDimensionInBestUnit($calculatedVolume, $volumeUnit, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'));
2343 } else {
2344 print $calculatedVolume.' '.measuringUnitString(0, "volume", $volumeUnit);
2345 }
2346 }
2347 if ($totalVolume > 0) {
2348 if ($calculatedVolume) {
2349 print ' ('.$langs->trans("SumOfProductVolumes").': ';
2350 }
2351 print showDimensionInBestUnit($totalVolume, 0, "volume", $langs, getDolGlobalInt('MAIN_VOLUME_DEFAULT_ROUND', -1), getDolGlobalString('MAIN_VOLUME_DEFAULT_UNIT', 'no'));
2352 //if (empty($calculatedVolume)) print ' ('.$langs->trans("Calculated").')';
2353 if ($calculatedVolume) {
2354 print ')';
2355 }
2356 }
2357 print "</td>\n";
2358 print '</tr>';
2359
2360 // Other attributes
2361 //$cols = 2;
2362 include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_view.tpl.php';
2363
2364 print '</table>';
2365
2366 print '</div>';
2367 print '<div class="fichehalfright">';
2368 print '<div class="underbanner clearboth"></div>';
2369
2370 print '<table class="border centpercent tableforfield">';
2371
2372 // Sending method
2373 print '<tr><td>';
2374 print '<table class="nobordernopadding centpercent"><tr><td>';
2375 print $langs->trans('SendingMethod');
2376 print '</td>';
2377
2378 if ($action != 'editshipping_method_id' && $permissiontoadd) {
2379 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>';
2380 }
2381 print '</tr></table>';
2382 print '</td><td>';
2383 if ($action == 'editshipping_method_id') {
2384 print '<form name="setshipping_method_id" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="post">';
2385 print '<input type="hidden" name="token" value="'.newToken().'">';
2386 print '<input type="hidden" name="action" value="setshipping_method_id">';
2387 $object->fetch_delivery_methods();
2388 print $form->selectarray("shipping_method_id", $object->meths, $object->shipping_method_id, 1, 0, 0, "", 1);
2389 if ($user->admin) {
2390 print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
2391 }
2392 print '<input type="submit" class="button button-edit smallpaddingimp" value="'.$langs->trans('Modify').'">';
2393 print '</form>';
2394 } else {
2395 if ($object->shipping_method_id > 0) {
2396 // Get code using getLabelFromKey
2397 $code = $langs->getLabelFromKey($db, (string) $object->shipping_method_id, 'c_shipment_mode', 'rowid', 'code');
2398 print $langs->trans("SendingMethod".strtoupper($code));
2399 }
2400 }
2401 print '</td>';
2402 print '</tr>';
2403
2404 // Tracking Number
2405 print '<tr><td class="titlefieldmiddle">'.$form->editfieldkey("TrackingNumber", 'tracking_number', $object->tracking_number, $object, $user->hasRight('expedition', 'creer')).'</td><td>';
2406 print $form->editfieldval("TrackingNumber", 'tracking_number', $object->tracking_url, $object, $user->hasRight('expedition', 'creer'), 'safehtmlstring', $object->tracking_number);
2407 print '</td></tr>';
2408
2409 // Incoterms
2410 if (isModEnabled('incoterm')) {
2411 print '<tr><td>';
2412 print '<table class="nobordernopadding centpercent"><tr><td>';
2413 print $langs->trans('IncotermLabel');
2414 print '<td><td class="right">';
2415 if ($permissiontoadd) {
2416 print '<a class="editfielda" href="'.DOL_URL_ROOT.'/expedition/card.php?id='.$object->id.'&action=editincoterm&token='.newToken().'">'.img_edit().'</a>';
2417 } else {
2418 print '&nbsp;';
2419 }
2420 print '</td></tr></table>';
2421 print '</td>';
2422 print '<td>';
2423 if ($action != 'editincoterm') {
2424 print $form->textwithpicto($object->display_incoterms(), $object->label_incoterms, 1);
2425 } else {
2426 print $form->select_incoterms((!empty($object->fk_incoterms) ? $object->fk_incoterms : ''), (!empty($object->location_incoterms) ? $object->location_incoterms : ''), $_SERVER['PHP_SELF'].'?id='.$object->id);
2427 }
2428 print '</td></tr>';
2429 }
2430
2431 // Other attributes
2432 $parameters = array();
2433 $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2434 print $hookmanager->resPrint;
2435
2436 print "</table>";
2437
2438 print '</div>';
2439 print '</div>';
2440
2441 print '<div class="clearboth"></div>';
2442
2443
2444 // Lines of products
2445
2446 if ($action == 'editline') {
2447 print ' <form name="updateline" id="updateline" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&amp;lineid='.$line_id.'" method="POST">
2448 <input type="hidden" name="token" value="' . newToken().'">
2449 <input type="hidden" name="action" value="updateline">
2450 <input type="hidden" name="mode" value="">
2451 <input type="hidden" name="id" value="' . $object->id.'">
2452 ';
2453 }
2454 print '<br>';
2455
2456 print '<div class="div-table-responsive-no-min">';
2457 print '<table class="noborder centpercent" id="tablelines" >';
2458 print '<thead>';
2459 print '<tr class="liste_titre">';
2460 // Adds a line numbering column
2461 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
2462 print '<td width="5" class="center linecolnum">&nbsp;</td>';
2463 }
2464 // Product/Service
2465 print '<td class="linecoldescription" >'.$langs->trans("Products").'</td>';
2466 // Qty
2467 print '<td class="center linecolqty">'.$langs->trans("QtyOrdered").'</td>';
2468 if ($origin && $origin_id > 0) {
2469 print '<td class="center linecolqtyinothershipments">'.$langs->trans("QtyInOtherShipments").'</td>';
2470 }
2471 if ($action == 'editline') {
2472 $editColspan = 3;
2473 if (!isModEnabled('stock')) {
2474 $editColspan--;
2475 }
2476 if (empty($conf->productbatch->enabled)) {
2477 $editColspan--;
2478 }
2479 print '<td class="center linecoleditlineotherinfo" colspan="'.$editColspan.'">';
2480 if ($object->status <= 1) {
2481 print $langs->trans("QtyToShip");
2482 } else {
2483 print $langs->trans("QtyShipped");
2484 }
2485 if (isModEnabled('stock')) {
2486 print ' - '.$langs->trans("WarehouseSource");
2487 }
2488 if (isModEnabled('productbatch')) {
2489 print ' - '.$langs->trans("Batch");
2490 }
2491 print '</td>';
2492 } else {
2493 if ($object->status <= 1) {
2494 print '<td class="center linecolqtytoship">'.$langs->trans("QtyToShip").'</td>';
2495 } else {
2496 print '<td class="center linecolqtyshipped">'.$langs->trans("QtyShipped").'</td>';
2497 }
2498 if (isModEnabled('stock')) {
2499 print '<td class="left linecolwarehousesource">'.$langs->trans("WarehouseSource").'</td>';
2500 }
2501
2502 if (isModEnabled('productbatch')) {
2503 print '<td class="left linecolbatch">'.$langs->trans("Batch").'</td>';
2504 }
2505 }
2506 print '<td class="center linecolweight">'.$langs->trans("CalculatedWeight").'</td>';
2507 print '<td class="center linecolvolume">'.$langs->trans("CalculatedVolume").'</td>';
2508 //print '<td class="center">'.$langs->trans("Size").'</td>';
2509 if ($object->status == 0) {
2510 print '<td class="linecoledit"></td>';
2511 print '<td class="linecoldelete" width="10"></td>';
2512 }
2513 print "</tr>\n";
2514 print '</thead>';
2515
2516 $outputlangs = $langs;
2517
2518 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
2519 $object->fetch_thirdparty();
2520 $newlang = '';
2521 if (/* empty($newlang) && */ GETPOST('lang_id', 'aZ09')) {
2522 $newlang = GETPOST('lang_id', 'aZ09');
2523 }
2524 if (empty($newlang)) {
2525 $newlang = $object->thirdparty->default_lang;
2526 }
2527 if (!empty($newlang)) {
2528 $outputlangs = new Translate("", $conf);
2529 $outputlangs->setDefaultLang($newlang);
2530 }
2531 }
2532
2533 // Get list of products already sent for same source object into $alreadysent
2534 $alreadysent = array();
2535 if ($origin && $origin_id > 0) {
2536 $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";
2537 $sql .= ", ed.rowid as shipmentline_id, ed.qty as qty_shipped, ed.fk_expedition as expedition_id, ed.fk_elementdet, ed.fk_entrepot";
2538 $sql .= ", e.rowid as shipment_id, e.ref as shipment_ref, e.date_creation, e.date_valid, e.date_delivery, e.date_expedition";
2539 //if (getDolGlobalInt('MAIN_SUBMODULE_DELIVERY')) $sql .= ", l.rowid as livraison_id, l.ref as livraison_ref, l.date_delivery, ld.qty as qty_received";
2540 $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';
2541 $sql .= ', p.description as product_desc';
2542 $sql .= " FROM ".MAIN_DB_PREFIX."expeditiondet as ed";
2543 $sql .= ", ".MAIN_DB_PREFIX."expedition as e";
2544 $sql .= ", ".MAIN_DB_PREFIX.$origin."det as obj";
2545 //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";
2546 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON obj.fk_product = p.rowid";
2547 $sql .= " WHERE e.entity IN (".getEntity('expedition').")";
2548 $sql .= " AND obj.fk_".$origin." = ".((int) $origin_id);
2549 $sql .= " AND obj.rowid = ed.fk_elementdet";
2550 $sql .= " AND ed.fk_expedition = e.rowid";
2551 //if ($filter) $sql.= $filter;
2552 $sql .= " ORDER BY obj.fk_product";
2553
2554 dol_syslog("expedition/card.php get list of shipment lines", LOG_DEBUG);
2555 $resql = $db->query($sql);
2556 if ($resql) {
2557 $num = $db->num_rows($resql);
2558 $i = 0;
2559
2560 while ($i < $num) {
2561 $obj = $db->fetch_object($resql);
2562 if ($obj) {
2563 // $obj->rowid is rowid in $origin."det" table
2564 $alreadysent[$obj->rowid][$obj->shipmentline_id] = array(
2565 'shipment_ref' => $obj->shipment_ref, 'shipment_id' => $obj->shipment_id, 'warehouse' => $obj->fk_entrepot, 'qty_shipped' => $obj->qty_shipped,
2566 'product_tosell' => $obj->product_tosell, 'product_tobuy' => $obj->product_tobuy, 'product_tobatch' => $obj->product_tobatch,
2567 'date_valid' => $db->jdate($obj->date_valid), 'date_delivery' => $db->jdate($obj->date_delivery));
2568 }
2569 $i++;
2570 }
2571 }
2572 //var_dump($alreadysent);
2573 }
2574
2575 print '<tbody>';
2576
2577 // Loop on each product to send/sent
2578 $conf->cache['product'] = array();
2579 $conf->cache['warehouse'] = array();
2580 for ($i = 0; $i < $num_prod; $i++) {
2581 $parameters = array('i' => $i, 'line' => $lines[$i], 'line_id' => $line_id, 'num' => $num_prod, 'alreadysent' => $alreadysent, 'editColspan' => !empty($editColspan) ? $editColspan : 0, 'outputlangs' => $outputlangs);
2582 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $object, $action);
2583 if ($reshook < 0) {
2584 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2585 }
2586
2587 if (empty($reshook) && $lines[$i]->product_type != "9") {
2588 print '<!-- origin line id = '.$lines[$i]->origin_line_id.' -->'; // id of order line
2589 print '<tr class="oddeven" id="row-'.$lines[$i]->id.'" data-id="'.$lines[$i]->id.'" data-element="'.$lines[$i]->element.'" >';
2590
2591 // #
2592 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER')) {
2593 print '<td class="center linecolnum">'.($i + 1).'</td>';
2594 }
2595
2596 // Predefined product or service
2597 if ($lines[$i]->fk_product > 0) {
2598 // Define output language
2599 if (getDolGlobalInt('MAIN_MULTILANGS') && getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE')) {
2600 $product_id = $lines[$i]->fk_product;
2601 if (!isset($conf->cache['product'][$product_id])) {
2602 $prod = new Product($db);
2603 $prod->fetch($product_id);
2604 $conf->cache['product'][$product_id] = $prod;
2605 } else {
2606 $prod = $conf->cache['product'][$product_id];
2607 }
2608 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $lines[$i]->product_label;
2609 } else {
2610 $label = (!empty($lines[$i]->label) ? $lines[$i]->label : $lines[$i]->product_label);
2611 }
2612
2613 print '<td class="linecoldescription">';
2614
2615 // Show product and description
2616 $product_static->type = $lines[$i]->fk_product_type;
2617 $product_static->id = $lines[$i]->fk_product;
2618 $product_static->ref = $lines[$i]->ref;
2619 $product_static->status = $lines[$i]->product_tosell;
2620 $product_static->status_buy = $lines[$i]->product_tobuy;
2621 $product_static->status_batch = $lines[$i]->product_tobatch;
2622
2623 $product_static->weight = $lines[$i]->weight;
2624 $product_static->weight_units = $lines[$i]->weight_units;
2625 $product_static->length = $lines[$i]->length;
2626 $product_static->length_units = $lines[$i]->length_units;
2627 $product_static->width = !empty($lines[$i]->width) ? $lines[$i]->width : 0;
2628 $product_static->width_units = !empty($lines[$i]->width_units) ? $lines[$i]->width_units : 0;
2629 $product_static->height = !empty($lines[$i]->height) ? $lines[$i]->height : 0;
2630 $product_static->height_units = !empty($lines[$i]->height_units) ? $lines[$i]->height_units : 0;
2631 $product_static->surface = $lines[$i]->surface;
2632 $product_static->surface_units = $lines[$i]->surface_units;
2633 $product_static->volume = $lines[$i]->volume;
2634 $product_static->volume_units = $lines[$i]->volume_units;
2635 $product_static->stockable_product = $lines[$i]->stockable_product;
2636
2637 $text = $product_static->getNomUrl(1);
2638 $text .= ' - '.$label;
2639 $description = (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE') ? '' : dol_htmlentitiesbr($lines[$i]->description));
2640 print $form->textwithtooltip($text, $description, 3, 0, '', (string) $i);
2641 print_date_range(!empty($lines[$i]->date_start) ? $lines[$i]->date_start : '', !empty($lines[$i]->date_end) ? $lines[$i]->date_end : '');
2642 if (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE')) {
2643 print (!empty($lines[$i]->description) && $lines[$i]->description != $lines[$i]->product) ? '<br>'.dol_htmlentitiesbr($lines[$i]->description) : '';
2644 }
2645 print "</td>\n";
2646 } else {
2647 print '<td class="linecoldescription" >';
2648 if ($lines[$i]->product_type == Product::TYPE_SERVICE) {
2649 $text = img_object($langs->trans('Service'), 'service');
2650 } else {
2651 $text = img_object($langs->trans('Product'), 'product');
2652 }
2653
2654 if (!empty($lines[$i]->label)) {
2655 $text .= ' <strong>'.$lines[$i]->label.'</strong>';
2656 print $form->textwithtooltip($text, $lines[$i]->description, 3, 0, '', (string) $i);
2657 } else {
2658 print $text.' '.nl2br($lines[$i]->description);
2659 }
2660
2661 print_date_range($lines[$i]->date_start, $lines[$i]->date_end);
2662 print "</td>\n";
2663 }
2664
2665 $unit_order = '';
2666 if (getDolGlobalString('PRODUCT_USE_UNITS')) {
2667 $unit_order = measuringUnitString((int) $lines[$i]->fk_unit);
2668 }
2669
2670 // Qty ordered
2671 print '<td class="center linecolqty">'.$lines[$i]->qty_asked.' '.$unit_order.'</td>';
2672
2673 // Qty in other shipments (with shipment and warehouse used)
2674 if ($origin && $origin_id > 0) {
2675 print '<td class="linecolqtyinothershipments center nowrap">';
2676 $htmltooltip = '';
2677 $qtyalreadysent = 0;
2678 foreach ($alreadysent as $key => $val) {
2679 if ($lines[$i]->fk_elementdet == $key) {
2680 $j = 0;
2681 foreach ($val as $shipmentline_id => $shipmentline_var) {
2682 if ($shipmentline_var['shipment_id'] == $lines[$i]->fk_expedition) {
2683 continue; // We want to show only "other shipments"
2684 }
2685
2686 $j++;
2687 if ($j > 1) {
2688 $htmltooltip .= '<br>';
2689 }
2690 $shipment_static->fetch($shipmentline_var['shipment_id']);
2691 $htmltooltip .= $shipment_static->getNomUrl(1, '', 0, 0, 1);
2692 $htmltooltip .= ' - '.$shipmentline_var['qty_shipped'];
2693 $htmltooltip .= ' - '.$langs->trans("DateValidation").' : '.(empty($shipmentline_var['date_valid']) ? $langs->trans("Draft") : dol_print_date($shipmentline_var['date_valid'], 'dayhour'));
2694 /*if (isModEnabled('stock') && $shipmentline_var['warehouse'] > 0) {
2695 $warehousestatic->fetch($shipmentline_var['warehouse']);
2696 $htmltext .= '<br>'.$langs->trans("FromLocation").' : '.$warehousestatic->getNomUrl(1, '', 0, 1);
2697 }*/
2698 //print ' '.$form->textwithpicto('', $htmltext, 1);
2699
2700 $qtyalreadysent += $shipmentline_var['qty_shipped'];
2701 }
2702 if ($j) {
2703 $htmltooltip = $langs->trans("QtyInOtherShipments").'...<br><br>'.$htmltooltip.'<br><input type="submit" name="dummyhiddenbuttontogetfocus" style="display:none" autofocus>';
2704 }
2705 }
2706 }
2707 print $form->textwithpicto((string) $qtyalreadysent, $htmltooltip, 1, 'info', '', 0, 3, 'tooltip'.$lines[$i]->id);
2708 print '</td>';
2709 }
2710
2711 if ($action == 'editline' && $lines[$i]->id == $line_id) {
2712 // edit mode
2713 print '<td colspan="'.$editColspan.'" class="center"><table class="nobordernopadding centpercent">';
2714 if (is_array($lines[$i]->detail_batch) && count($lines[$i]->detail_batch) > 0) {
2715 print '<!-- case edit 1 -->';
2716 $line = new ExpeditionLigne($db);
2717 foreach ($lines[$i]->detail_batch as $detail_batch) {
2718 print '<tr>';
2719 // Qty to ship or shipped
2720 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>';
2721 // Batch number management
2722 if ($lines[$i]->entrepot_id == 0) {
2723 // only show lot numbers from src warehouse when shipping from multiple warehouses
2724 $line->fetch($detail_batch->fk_expeditiondet);
2725 }
2726 $entrepot_id = !empty($detail_batch->entrepot_id) ? $detail_batch->entrepot_id : $lines[$i]->entrepot_id;
2727 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>';
2728 print '</tr>';
2729 }
2730 // add a 0 qty lot row to be able to add a lot
2731 print '<tr>';
2732 // Qty to ship or shipped
2733 print '<td><input class="qtyl" name="qtyl'.$line_id.'_0" id="qtyl'.$line_id.'_0" type="text" size="4" value="0"></td>';
2734 // Batch number management
2735 print '<td>'.$formproduct->selectLotStock('', 'batchl'.$line_id.'_0', '', 1, 0, $lines[$i]->fk_product).'</td>';
2736 print '</tr>';
2737 } elseif (isModEnabled('stock')) {
2738 if ($lines[$i]->fk_product > 0) {
2739 if ($lines[$i]->entrepot_id > 0) {
2740 print '<!-- case edit 2 -->';
2741 print '<tr>';
2742 // Qty to ship or shipped
2743 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>';
2744 // Warehouse source
2745 print '<td>'.$formproduct->selectWarehouses($lines[$i]->entrepot_id, 'entl'.$line_id, '', 1, 0, $lines[$i]->fk_product, '', 1, 0, array(), 'minwidth200').'</td>';
2746 // Batch number management
2747 print '<td>';
2748 if (isModEnabled('productbatch')) {
2749 print ' - '.$langs->trans("NA");
2750 }
2751 print '</td>';
2752 print '</tr>';
2753 } elseif (count($lines[$i]->details_entrepot) > 1) {
2754 print '<!-- case edit 3 -->';
2755 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
2756 print '<tr>';
2757 // Qty to ship or shipped
2758 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>';
2759 // Warehouse source
2760 print '<td>'.$formproduct->selectWarehouses($detail_entrepot->entrepot_id, 'entl'.$detail_entrepot->line_id, '', 1, 0, $lines[$i]->fk_product, '', 1, 0, array(), 'minwidth200').'</td>';
2761 // Batch number management
2762 print '<td>';
2763 if (isModEnabled('productbatch')) {
2764 print ' - '.$langs->trans("NA");
2765 }
2766 print '</td>';
2767 print '</tr>';
2768 }
2769 } elseif ($lines[$i]->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
2770 print '<!-- case edit 4 -->';
2771 print '<tr>';
2772 // Qty to ship or shipped
2773 print '<td><input class="qtyl right" name="qtyl'.$line_id.'" id="qtyl'.$line_id.'" type="text" size="4" value="'.$lines[$i]->qty_shipped.'"></td>';
2774 print '<td><span class="opacitymedium">('.$langs->trans("Service").')</span></td>';
2775 print '<td></td>';
2776 print '</tr>';
2777 } else {
2778 print '<!-- case edit 5 -->';
2779 print '<tr><td colspan="3">'.$langs->trans("ErrorStockIsNotEnough").'</td></tr>';
2780 }
2781 } else {
2782 print '<!-- case edit 6 -->';
2783 print '<tr>';
2784 // Qty to ship or shipped
2785 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>';
2786 // Warehouse source
2787 print '<td></td>';
2788 // Batch number management
2789 print '<td></td>';
2790 print '</tr>';
2791 }
2792 } elseif (!isModEnabled('stock') && empty($conf->productbatch->enabled)) { // both product batch and stock are not activated.
2793 print '<!-- case edit 7 -->';
2794 print '<tr>';
2795 // Qty to ship or shipped
2796 print '<td><input class="qtyl right" name="qtyl'.$line_id.'" id="qtyl'.$line_id.'" type="text" size="4" value="'.$lines[$i]->qty_shipped.'"></td>';
2797 // Warehouse source
2798 print '<td></td>';
2799 // Batch number management
2800 print '<td></td>';
2801 print '</tr>';
2802 }
2803
2804 print '</table></td>';
2805 } else {
2806 // Qty to ship or shipped
2807 print '<td class="linecolqtytoship center">'.$lines[$i]->qty_shipped.' '.$unit_order.'</td>';
2808
2809 // Warehouse source
2810 if (isModEnabled('stock')) {
2811 print '<td class="linecolwarehousesource tdoverflowmax200">';
2812 if ($lines[$i]->product_type == Product::TYPE_SERVICE && getDolGlobalString('SHIPMENT_SUPPORTS_SERVICES')) {
2813 print '<span class="opacitymedium">('.$langs->trans("Service").')</span>';
2814 } elseif ($lines[$i]->entrepot_id > 0 && $lines[$i]->stockable_product == Product::ENABLED_STOCK) {
2815 $warehouse_id = $lines[$i]->entrepot_id;
2816 if (!isset($conf->cache['warehouse'][$warehouse_id])) {
2817 $warehouse = new Entrepot($db);
2818 $warehouse->fetch($warehouse_id);
2819 $conf->cache['warehouse'][$warehouse_id] = $warehouse;
2820 } else {
2821 $warehouse = $conf->cache['warehouse'][$warehouse_id];
2822 }
2823 print $warehouse->getNomUrl(1);
2824 } elseif (count($lines[$i]->details_entrepot) > 1) {
2825 $detail = '';
2826 foreach ($lines[$i]->details_entrepot as $detail_entrepot) {
2827 $warehouse_id = $detail_entrepot->entrepot_id;
2828 if ($warehouse_id > 0) {
2829 if (!isset($conf->cache['warehouse'][$warehouse_id])) {
2830 $warehouse = new Entrepot($db);
2831 $warehouse->fetch($warehouse_id);
2832 $conf->cache['warehouse'][$warehouse_id] = $warehouse;
2833 } else {
2834 $warehouse = $conf->cache['warehouse'][$warehouse_id];
2835 }
2836 $detail .= $langs->trans("DetailWarehouseFormat", $warehouse->label, $detail_entrepot->qty_shipped).'<br>';
2837 }
2838 }
2839 print $form->textwithtooltip(img_picto('', 'object_stock').' '.$langs->trans("DetailWarehouseNumber"), $detail);
2840 } elseif (count($lines[$i]->detail_children ?? []) > 1) {
2841 $detail = '';
2842 foreach ($lines[$i]->detail_children as $child_product_id => $child_stock_list) {
2843 foreach ($child_stock_list as $warehouse_id => $total_qty) {
2844 // get product from cache
2845 $child_product_label = '';
2846 if (!isset($conf->cache['product'][$child_product_id])) {
2847 $child_product = new Product($db);
2848 $child_product->fetch($child_product_id);
2849 $conf->cache['product'][$child_product_id] = $child_product;
2850 } else {
2851 $child_product = $conf->cache['product'][$child_product_id];
2852 }
2853 $child_product_label = $child_product->ref . ' ' . $child_product->label;
2854
2855 // get warehouse from cache
2856 if (!isset($conf->cache['warehouse'][$warehouse_id])) {
2857 $child_warehouse = new Entrepot($db);
2858 $child_warehouse->fetch($warehouse_id);
2859 $conf->cache['warehouse'][$warehouse_id] = $child_warehouse;
2860 } else {
2861 $child_warehouse = $conf->cache['warehouse'][$warehouse_id];
2862 }
2863
2864 $detail .= $langs->trans('DetailChildrenFormat', $child_product_label, $child_warehouse->label, price2num($total_qty, 'MS')).'<br>';
2865 }
2866 }
2867 print $form->textwithtooltip(img_picto('', 'object_stock').' '.$langs->trans('DetailWarehouseNumber'), $detail);
2868 }
2869 print '</td>';
2870 }
2871
2872 // Batch number management
2873 if (isModEnabled('productbatch')) {
2874 if (isset($lines[$i]->detail_batch)) {
2875 print '<!-- Detail of lot -->';
2876 print '<td class="linecolbatch">';
2877 if ($lines[$i]->product_tobatch) {
2878 $detail = '';
2879 foreach ($lines[$i]->detail_batch as $dbatch) { // $dbatch is instance of ExpeditionLineBatch
2880 $detail .= $langs->trans("Batch").': '.$dbatch->batch;
2881 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY')) {
2882 $detail .= ' - '.$langs->trans("SellByDate").': '.dol_print_date($dbatch->sellby, "day");
2883 }
2884 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY')) {
2885 $detail .= ' - '.$langs->trans("EatByDate").': '.dol_print_date($dbatch->eatby, "day");
2886 }
2887 $detail .= ' - '.$langs->trans("Qty").': '.$dbatch->qty;
2888 $detail .= '<br>';
2889 }
2890 print $form->textwithtooltip(img_picto('', 'object_barcode').' '.$langs->trans("DetailBatchNumber"), $detail);
2891 } else {
2892 print $langs->trans("NA");
2893 }
2894 print '</td>';
2895 } else {
2896 print '<td class="linecolbatch" ></td>';
2897 }
2898 }
2899 }
2900
2901 // Weight
2902 print '<td class="center linecolweight">';
2903 if ($lines[$i]->fk_product_type == Product::TYPE_PRODUCT) {
2904 print $lines[$i]->weight * $lines[$i]->qty_shipped.' '.measuringUnitString(0, "weight", $lines[$i]->weight_units);
2905 } else {
2906 print '&nbsp;';
2907 }
2908 print '</td>';
2909
2910 // Volume
2911 print '<td class="center linecolvolume">';
2912 if ($lines[$i]->fk_product_type == Product::TYPE_PRODUCT) {
2913 print $lines[$i]->volume * $lines[$i]->qty_shipped.' '.measuringUnitString(0, "volume", $lines[$i]->volume_units);
2914 } else {
2915 print '&nbsp;';
2916 }
2917 print '</td>';
2918
2919 // Size
2920 //print '<td class="center">'.$lines[$i]->volume*$lines[$i]->qty_shipped.' '.measuringUnitString(0, "volume", $lines[$i]->volume_units).'</td>';
2921
2922 if ($action == 'editline' && $lines[$i]->id == $line_id) {
2923 print '<td class="center" colspan="2" valign="middle">';
2924 print '<input type="submit" class="button button-save" id="savelinebutton marginbottomonly" name="save" value="'.$langs->trans("Save").'"><br>';
2925 print '<input type="submit" class="button button-cancel" id="cancellinebutton" name="cancel" value="'.$langs->trans("Cancel").'"><br>';
2926 print '</td>';
2927 } elseif ($object->status == Expedition::STATUS_DRAFT) {
2928 $edit_url = $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=editline&token='.newToken().'&lineid='.$lines[$i]->id;
2929 if (getDolGlobalInt('PRODUIT_SOUSPRODUITS')) {
2930 $product_id = $lines[$i]->fk_product;
2931 if (!isset($conf->cache['product'][$product_id])) {
2932 $product = new Product($db);
2933 $product->fetch($product_id);
2934 $conf->cache['product'][$product_id] = $product;
2935 } else {
2936 $product = $conf->cache['product'][$product_id];
2937 }
2938
2939 if ($product->hasFatherOrChild(1)) {
2940 $edit_url = dol_buildpath('/expedition/dispatch.php?id='.$object->id, 1);
2941 }
2942 }
2943
2944 // edit-delete buttons
2945 print '<td class="linecoledit center">';
2946 print '<a class="editfielda reposition" href="'.$edit_url.'">'.img_edit().'</a>';
2947 print '</td>';
2948 print '<td class="linecoldelete" width="10">';
2949 print '<a class="reposition" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=deleteline&token='.newToken().'&lineid='.$lines[$i]->id.'">'.img_delete().'</a>';
2950 print '</td>';
2951
2952 // Display lines extrafields
2953 if (!empty($rowExtrafieldsStart)) {
2954 print $rowExtrafieldsStart;
2955 print $rowExtrafieldsView;
2956 print $rowEnd;
2957 }
2958 }
2959 print "</tr>";
2960
2961 // Display lines extrafields.
2962 // $line is a line of shipment
2963 if (!empty($extrafields)) {
2964 $colspan = 6;
2965 if ($origin && $origin_id > 0) {
2966 $colspan++;
2967 }
2968 if (isModEnabled('productbatch')) {
2969 $colspan++;
2970 }
2971 if (isModEnabled('stock')) {
2972 $colspan++;
2973 }
2974
2975 $line = $lines[$i];
2976 $line->fetch_optionals();
2977
2978 // TODO Show all in same line by setting $display_type = 'line'
2979 if ($action == 'editline' && $line->id == $line_id) {
2980 print $lines[$i]->showOptionals($extrafields, 'edit', array('colspan' => $colspan), !empty($indiceAsked) ? $indiceAsked : '', '', '', 'card');
2981 } else {
2982 print $lines[$i]->showOptionals($extrafields, 'view', array('colspan' => $colspan), !empty($indiceAsked) ? $indiceAsked : '', '', '', 'card');
2983 }
2984 }
2985 } elseif (empty($reshook) && $lines[$i]->product_type == "9") {
2986 $objectsrc = new OrderLine($db);
2987 $objectsrc->fetch($lines[$i]->origin_line_id);
2988 if ($objectsrc->special_code == SUBTOTALS_SPECIAL_CODE) {
2989 $line = $lines[$i];
2990 require dol_buildpath('/core/tpl/subtotal_expedition_view.tpl.php');
2991 }
2992 }
2993 }
2994
2995 // TODO Show also lines ordered but not delivered
2996
2997 if (empty($num_prod)) {
2998 print '<tr><td colspan="8"><span class="opacitymedium">'.$langs->trans("NoLineGoOnTabToAddSome", $langs->transnoentitiesnoconv("ShipmentDistribution")).'</span></td></tr>';
2999 }
3000
3001 print "</table>\n";
3002 print '</tbody>';
3003 print '</div>';
3004
3005
3006 print dol_get_fiche_end();
3007
3008
3009 $object->fetchObjectLinked($object->id, $object->element);
3010
3011
3012 /*
3013 * Boutons actions
3014 */
3015
3016 if (($user->socid == 0) && ($action != 'presend')) {
3017 print '<div class="tabsAction">';
3018
3019 $parameters = array();
3020 $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
3021 // modified by hook
3022 if (empty($reshook)) {
3023 if ($object->status == Expedition::STATUS_DRAFT && $num_prod > 0) {
3024 if ((!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'creer'))
3025 || (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && $user->hasRight('expedition', 'shipping_advance', 'validate'))) {
3026 print dolGetButtonAction('', $langs->trans('Validate'), 'default', $_SERVER["PHP_SELF"].'?action=valid&token='.newToken().'&id='.$object->id, '');
3027 } else {
3028 print dolGetButtonAction($langs->trans('NotAllowed'), $langs->trans('Validate'), 'default', $_SERVER['PHP_SELF']. '#', '', false);
3029 }
3030 }
3031
3032 // 0=draft, 1=validated/delivered, 2=closed/delivered
3033 if ($object->status == Expedition::STATUS_VALIDATED && !getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')) {
3034 if ($user->hasRight('expedition', 'creer')) {
3035 print dolGetButtonAction('', $langs->trans('SetToDraft'), 'default', $_SERVER["PHP_SELF"].'?action=setdraft&token='.newToken().'&id='.$object->id, '');
3036 }
3037 }
3038 if ($object->status == Expedition::STATUS_CLOSED) {
3039 if ($user->hasRight('expedition', 'creer')) {
3040 print dolGetButtonAction('', $langs->trans('ReOpen'), 'default', $_SERVER["PHP_SELF"].'?action=reopen&token='.newToken().'&id='.$object->id, '');
3041 }
3042 }
3043
3044 // Send
3045 if (empty($user->socid)) {
3046 if ($object->status > 0) {
3047 if (!getDolGlobalString('MAIN_USE_ADVANCED_PERMS') || $user->hasRight('expedition', 'shipping_advance', 'send')) {
3048 print dolGetButtonAction('', $langs->trans('SendMail'), 'default', $_SERVER["PHP_SELF"].'?action=presend&token='.newToken().'&id='.$object->id.'&mode=init#formmailbeforetitle', '');
3049 } else {
3050 print dolGetButtonAction('', $langs->trans('SendMail'), 'default', $_SERVER['PHP_SELF']. '#', '', false);
3051 }
3052 }
3053 }
3054
3055 // This is just to generate a delivery receipt when option to do this is on
3056 //var_dump($object->linkedObjectsIds['delivery']);
3057 if (getDolGlobalInt('MAIN_SUBMODULE_DELIVERY') && ($object->status == Expedition::STATUS_VALIDATED || $object->status == Expedition::STATUS_CLOSED) && $user->hasRight('expedition', 'delivery', 'creer') && empty($object->linkedObjectsIds['delivery'])) {
3058 print dolGetButtonAction('', $langs->trans('CreateDeliveryOrder'), 'default', $_SERVER["PHP_SELF"] . '?action=create_delivery&token=' . newToken() . '&id=' . $object->id, '');
3059 }
3060
3061 // Sign (to set to status "Signed" without using the online signature page)
3062 if ($object->status > Expedition::STATUS_DRAFT) {
3063 if ($object->signed_status != Expedition::$SIGNED_STATUSES['STATUS_SIGNED_ALL']) {
3064 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>';
3065 } else {
3066 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>';
3067 }
3068 }
3069
3070 // Create bill
3071 if (isModEnabled('invoice') && ($object->status == Expedition::STATUS_VALIDATED || $object->status == Expedition::STATUS_CLOSED)) {
3072 if ($user->hasRight('facture', 'creer')) {
3073 if (getDolGlobalString('WORKFLOW_BILL_ON_SHIPMENT') !== '0') {
3074 print dolGetButtonAction('', $langs->trans('CreateBill'), 'default', DOL_URL_ROOT.'/compta/facture/card.php?action=create&origin='.$object->element.'&originid='.$object->id.'&socid='.$object->socid, '');
3075 }
3076 }
3077 }
3078
3079 // Set Billed and Closed
3080 if ($object->status == Expedition::STATUS_VALIDATED) {
3081 if ($user->hasRight('expedition', 'creer') && $object->status > 0) {
3082 if (!$object->billed && getDolGlobalString('WORKFLOW_BILL_ON_SHIPMENT') !== '0') {
3083 print dolGetButtonAction('', $langs->trans('ClassifyBilled'), 'default', $_SERVER["PHP_SELF"].'?action=classifybilled&token='.newToken().'&id='.$object->id, '');
3084 }
3085 print dolGetButtonAction('', $langs->trans("Close"), 'default', $_SERVER["PHP_SELF"] . '?action=classifyclosed&token=' . newToken() . '&id=' . $object->id, '');
3086 }
3087 }
3088
3089 // Cancel
3090 if ($object->status == Expedition::STATUS_VALIDATED) {
3091 if ($user->hasRight('expedition', 'creer')) {
3092 print dolGetButtonAction('', $langs->trans('Cancel'), 'danger', $_SERVER["PHP_SELF"].'?action=cancel&token='.newToken().'&id='.$object->id.'&mode=init#formmailbeforetitle', '');
3093 }
3094 }
3095
3096 // Delete
3097 if ($user->hasRight('expedition', 'supprimer')) {
3098 print dolGetButtonAction('', $langs->trans('Delete'), 'delete', $_SERVER["PHP_SELF"].'?action=delete&token='.newToken().'&id='.$object->id, '');
3099 }
3100 }
3101
3102 print '</div>';
3103 }
3104
3105
3106 /*
3107 * Documents generated
3108 */
3109
3110 if ($action != 'presend' && $action != 'editline') {
3111 print '<div class="fichecenter"><div class="fichehalfleft">';
3112
3113 $objectref = dol_sanitizeFileName($object->ref);
3114 $filedir = $conf->expedition->dir_output."/sending/".$objectref;
3115
3116 $urlsource = $_SERVER["PHP_SELF"]."?id=".$object->id;
3117
3118 $genallowed = $user->hasRight('expedition', 'lire');
3119 $delallowed = $user->hasRight('expedition', 'creer');
3120
3121 print $formfile->showdocuments('expedition', $objectref, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', '', '', $soc->default_lang);
3122
3123
3124 // Show links to link elements
3125 $tmparray = $form->showLinkToObjectBlock($object, array(), array('shipping'), 1);
3126 $linktoelem = $tmparray['linktoelem'];
3127 $htmltoenteralink = $tmparray['htmltoenteralink'];
3128 print $htmltoenteralink;
3129
3130 $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem);
3131
3132 // Show online signature link
3133 $useonlinesignature = getDolGlobalInt('EXPEDITION_ALLOW_ONLINESIGN');
3134
3135 if ($object->status != Expedition::STATUS_DRAFT && $useonlinesignature) {
3136 print '<br><!-- Link to sign -->';
3137 require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
3138 print showOnlineSignatureUrl('expedition', $object->ref, $object).'<br>';
3139 }
3140
3141 print '</div><div class="fichehalfright">';
3142
3143 // List of actions on element
3144 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php';
3145 $formactions = new FormActions($db);
3146 $somethingshown = $formactions->showactions($object, 'shipping', $socid, 1);
3147
3148 print '</div></div>';
3149 }
3150
3151
3152 /*
3153 * Action presend
3154 */
3155
3156 //Select mail models is same action as presend
3157 if (GETPOST('modelselected')) {
3158 $action = 'presend';
3159 }
3160
3161 // Presend form
3162 $modelmail = 'shipping_send';
3163 $defaulttopic = 'SendShippingRef';
3164 $diroutput = $conf->expedition->dir_output.'/sending';
3165 $trackid = 'shi'.$object->id;
3166
3167 include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php';
3168}
3169
3170// End of page
3171llxFooter();
3172$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.