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