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