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