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