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