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