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