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