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