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