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