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