dolibarr  18.0.6
dispatch.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2004-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004-2016 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2005 Eric Seigne <eric.seigne@ryxeo.com>
5  * Copyright (C) 2005-2009 Regis Houssin <regis.houssin@inodbox.com>
6  * Copyright (C) 2010-2021 Juanjo Menent <jmenent@2byte.es>
7  * Copyright (C) 2014 Cedric Gross <c.gross@kreiz-it.fr>
8  * Copyright (C) 2016 Florian Henry <florian.henry@atm-consulting.fr>
9  * Copyright (C) 2017-2022 Ferran Marcet <fmarcet@2byte.es>
10  * Copyright (C) 2018-2022 Frédéric France <frederic.france@netlogic.fr>
11  * Copyright (C) 2019-2020 Christophe Battarel <christophe@altairis.fr>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program. If not, see <https://www.gnu.org/licenses/>.
25  */
26 
33 // Load Dolibarr environment
34 require '../main.inc.php';
35 require_once DOL_DOCUMENT_ROOT.'/core/modules/supplier_order/modules_commandefournisseur.php';
36 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
37 require_once DOL_DOCUMENT_ROOT.'/core/lib/fourn.lib.php';
38 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
39 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php';
40 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
41 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
42 require_once DOL_DOCUMENT_ROOT.'/core/lib/sendings.lib.php';
43 if (isModEnabled('project')) {
44  require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
45 }
46 
47 // Load translation files required by the page
48 $langs->loadLangs(array("sendings", "companies", "bills", 'deliveries', 'orders', 'stocks', 'other', 'propal', 'receptions'));
49 
50 if (isModEnabled('productbatch')) {
51  $langs->load('productbatch');
52 }
53 
54  // Security check
55 $id = GETPOST("id", 'int');
56 $ref = GETPOST('ref');
57 $lineid = GETPOST('lineid', 'int');
58 $action = GETPOST('action', 'aZ09');
59 $fk_default_warehouse = GETPOST('fk_default_warehouse', 'int');
60 $cancel = GETPOST('cancel', 'alpha');
61 $confirm = GETPOST('confirm', 'alpha');
62 
63 $error = 0;
64 $errors = array();
65 
66 if ($user->socid) {
67  $socid = $user->socid;
68 }
69 
70 $hookmanager->initHooks(array('expeditiondispatch'));
71 
72 // Recuperation de l'id de projet
73 $projectid = 0;
74 if (GETPOSTISSET("projectid")) {
75  $projectid = GETPOST("projectid", 'int');
76 }
77 
78 $object = new Expedition($db);
79 $objectorder = new Commande($db);
80 
81 
82 if ($id > 0 || !empty($ref)) {
83  $result = $object->fetch($id, $ref);
84  if ($result <= 0) {
85  setEventMessages($object->error, $object->errors, 'errors');
86  }
87  $result = $object->fetch_thirdparty();
88  if ($result < 0) {
89  setEventMessages($object->error, $object->errors, 'errors');
90  }
91  if (!empty($object->origin)) {
92  $origin = $object->origin;
93 
94  $object->fetch_origin();
95  $typeobject = $object->origin;
96  }
97 }
98 
99 // $id is id of a purchase order.
100 $result = restrictedArea($user, 'expedition', $object, '');
101 
102 if (!isModEnabled('stock')) {
103  accessforbidden('Module stock disabled');
104 }
105 
106 $usercancreate = $user->hasRight('expedition', 'creer');
107 $permissiontoadd = $usercancreate; // Used by the include of actions_addupdatedelete.inc.php
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) {
117  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
118 }
119 
120 // Update a dispatched line
121 if ($action == 'updatelines' && $usercancreate) {
122  $db->begin();
123  $error = 0;
124 
125  $expeditiondispatch = new ExpeditionLigne($db);
126  $expeditionlinebatch = new ExpeditionLineBatch($db);
127 
128  $pos = 0;
129 
130  foreach ($_POST as $key => $value) {
131  // without batch module enabled
132  $reg = array();
133  if (preg_match('/^product_.*([0-9]+)_([0-9]+)$/i', $key, $reg)) {
134  $pos++;
135  if (preg_match('/^product_([0-9]+)_([0-9]+)$/i', $key, $reg)) {
136  $modebatch = "barcode";
137  } elseif (preg_match('/^product_batch_([0-9]+)_([0-9]+)$/i', $key, $reg)) { // With batchmode enabled
138  $modebatch = "batch";
139  }
140 
141  $numline = $pos;
142  if ($modebatch == "barcode") {
143  $prod = "product_".$reg[1].'_'.$reg[2];
144  } else {
145  $prod = 'product_batch_'.$reg[1].'_'.$reg[2];
146  }
147  $qty = "qty_".$reg[1].'_'.$reg[2];
148  $ent = "entrepot_".$reg[1].'_'.$reg[2];
149  $fk_commandedet = "fk_commandedet_".$reg[1].'_'.$reg[2];
150  $idline = GETPOST("idline_".$reg[1].'_'.$reg[2]);
151  $pu = "pu_".$reg[1].'_'.$reg[2]; // This is unit price including discount
152  $lot = '';
153  $dDLUO = '';
154  $dDLC = '';
155  if ($modebatch == "batch") { //TODO: Make impossible to input non existing batch code
156  $lot = GETPOST('lot_number_'.$reg[1].'_'.$reg[2]);
157  $dDLUO = dol_mktime(12, 0, 0, GETPOST('dluo_'.$reg[1].'_'.$reg[2].'month', 'int'), GETPOST('dluo_'.$reg[1].'_'.$reg[2].'day', 'int'), GETPOST('dluo_'.$reg[1].'_'.$reg[2].'year', 'int'));
158  $dDLC = dol_mktime(12, 0, 0, GETPOST('dlc_'.$reg[1].'_'.$reg[2].'month', 'int'), GETPOST('dlc_'.$reg[1].'_'.$reg[2].'day', 'int'), GETPOST('dlc_'.$reg[1].'_'.$reg[2].'year', 'int'));
159  }
160 
161  $newqty = price2num(GETPOST($qty, 'alpha'), 'MS');
162  //var_dump("modebatch=".$modebatch." newqty=".$newqty." ent=".$ent." idline=".$idline);
163 
164  // We ask to move a qty
165  if (($modebatch == "batch" && $newqty >= 0) || ($modebatch == "barcode" && $newqty != 0)) {
166  if ($newqty > 0) { // If we want a qty, we make test on input data
167  if (!(GETPOST($ent, 'int') > 0)) {
168  dol_syslog('No dispatch for line '.$key.' as no warehouse was chosen.');
169  $text = $langs->transnoentities('Warehouse').', '.$langs->transnoentities('Line').' '.($numline);
170  setEventMessages($langs->trans('ErrorFieldRequired', $text), null, 'errors');
171  $error++;
172  }
173  if (!$error && $modebatch == "batch") {
174  $sql = "SELECT pb.rowid ";
175  $sql .= " FROM ".MAIN_DB_PREFIX."product_batch as pb";
176  $sql .= " JOIN ".MAIN_DB_PREFIX."product_stock as ps";
177  $sql .= " ON ps.rowid = pb.fk_product_stock";
178  $sql .= " WHERE pb.batch = '".$db->escape($lot)."'";
179  $sql .= " AND ps.fk_product = ".((int) GETPOST($prod, 'int')) ;
180  $sql .= " AND ps.fk_entrepot = ".((int) GETPOST($ent, 'int')) ;
181 
182  $resql = $db->query($sql);
183  if ($resql) {
184  $num = $db->num_rows($resql);
185  if ($num > 1) {
186  dol_syslog('No dispatch for line '.$key.' as too many combination warehouse, product, batch code was found ('.$num.').');
187  setEventMessages($langs->trans('ErrorTooManyCombinationBatchcode', $numline, $num), null, 'errors');
188  $error++;
189  } elseif ($num < 1) {
190  dol_syslog('No dispatch for line '.$key.' as no combination warehouse, product, batch code was found.');
191  setEventMessages($langs->trans('ErrorNoCombinationBatchcode', $numline), null, 'errors');
192  $error++;
193  }
194  $db->free($resql);
195  }
196  }
197  }
198  //var_dump($key.' '.$newqty.' '.$idline.' '.$error);
199 
200  if (!$error) {
201  $qtystart = 0;
202 
203  if ($idline > 0) {
204  $result = $expeditiondispatch->fetch($idline); // get line from llx_expeditiondet
205  if ($result < 0) {
206  setEventMessages($expeditiondispatch->error, $expeditiondispatch->errors, 'errors');
207  $error++;
208  } else {
209  $qtystart = $expeditiondispatch->qty;
210  $expeditiondispatch->qty = $newqty;
211  $expeditiondispatch->entrepot_id = GETPOST($ent, 'int');
212 
213  if ($newqty > 0) {
214  $result = $expeditiondispatch->update($user);
215  } else {
216  $result = $expeditiondispatch->delete($user);
217  }
218  if ($result < 0) {
219  setEventMessages($expeditiondispatch->error, $expeditiondispatch->errors, 'errors');
220  $error++;
221  }
222 
223  if (!$error && $modebatch == "batch") {
224  if ($newqty > 0) {
225  $suffixkeyfordate = preg_replace('/^product_batch/', '', $key);
226  $sellby = dol_mktime(0, 0, 0, GETPOST('dlc'.$suffixkeyfordate.'month'), GETPOST('dlc'.$suffixkeyfordate.'day'), GETPOST('dlc'.$suffixkeyfordate.'year'), '');
227  $eatby = dol_mktime(0, 0, 0, GETPOST('dluo'.$suffixkeyfordate.'month'), GETPOST('dluo'.$suffixkeyfordate.'day'), GETPOST('dluo'.$suffixkeyfordate.'year'));
228 
229  $sqlsearchdet = "SELECT rowid FROM ".MAIN_DB_PREFIX.$expeditionlinebatch->table_element;
230  $sqlsearchdet .= " WHERE fk_expeditiondet = ".((int) $idline);
231  $sqlsearchdet .= " AND batch = '".$db->escape($lot)."'";
232  $resqlsearchdet = $db->query($sqlsearchdet);
233 
234  if ($resqlsearchdet) {
235  $objsearchdet = $db->fetch_object($resqlsearchdet);
236  } else {
237  dol_print_error($db);
238  }
239 
240  if ($objsearchdet) {
241  $sql = "UPDATE ".MAIN_DB_PREFIX.$expeditionlinebatch->table_element." SET";
242  $sql .= " eatby = ".($eatby ? "'".$db->idate($eatby)."'" : "null");
243  $sql .= " , sellby = ".($sellby ? "'".$db->idate($sellby)."'" : "null");
244  $sql .= " , qty = ".((float) $newqty);
245  // TODO Add a column fk_warehouse
246  $sql .= " WHERE rowid = ".((int) $objsearchdet->rowid);
247  } else {
248  $sql = "INSERT INTO ".MAIN_DB_PREFIX.$expeditionlinebatch->table_element." (";
249  $sql .= "fk_expeditiondet, eatby, sellby, batch, qty, fk_origin_stock)";
250  // TODO Add a column fk_warehouse
251  $sql .= " VALUES (".((int) $idline).", ".($eatby ? "'".$db->idate($eatby)."'" : "null").", ".($sellby ? "'".$db->idate($sellby)."'" : "null").", ";
252  $sql .= " '".$db->escape($lot)."', ".((float) $newqty).", 0)";
253  }
254  } else {
255  $sql = " DELETE FROM ".MAIN_DB_PREFIX.$expeditionlinebatch->table_element;
256  $sql .= " WHERE fk_expeditiondet = ".((int) $idline);
257  $sql .= " AND batch = '".$db->escape($lot)."'";
258  }
259 
260  $resql = $db->query($sql);
261  if (!$resql) {
262  dol_print_error($db);
263  $error++;
264  }
265  }
266  }
267  } else {
268  $expeditiondispatch->fk_expedition = $object->id;
269  $expeditiondispatch->entrepot_id = GETPOST($ent, 'int');
270  $expeditiondispatch->fk_origin_line = GETPOST($fk_commandedet, 'int');
271  $expeditiondispatch->qty = $newqty;
272 
273  if ($newqty > 0) {
274  $idline = $expeditiondispatch->insert($user);
275  if ($idline < 0) {
276  setEventMessages($expeditiondispatch->error, $expeditiondispatch->errors, 'errors');
277  $error++;
278  }
279 
280  if ($modebatch == "batch" && !$error) {
281  $expeditionlinebatch->sellby = $dDLUO;
282  $expeditionlinebatch->eatby = $dDLC;
283  $expeditionlinebatch->batch = $lot;
284  $expeditionlinebatch->qty = $newqty;
285  $expeditionlinebatch->fk_origin_stock = 0;
286  $expeditionlinebatch->fk_warehouse = GETPOST($ent, 'int');
287 
288  $result = $expeditionlinebatch->create($idline);
289  if ($result < 0) {
290  setEventMessages($expeditionlinebatch->error, $expeditionlinebatch->errors, 'errors');
291  $error++;
292  }
293  }
294  }
295  }
296 
297  // If module stock is enabled and the stock decrease is done on edtion of this page
298  /*
299  if (!$error && GETPOST($ent, 'int') > 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_DISPATCH_ORDER)) {
300  $mouv = new MouvementStock($db);
301  $product = GETPOST($prod, 'int');
302  $entrepot = GETPOST($ent, 'int');
303  $qtymouv = price2num(GETPOST($qty, 'alpha'), 'MS') - $qtystart;
304  $price = price2num(GETPOST($pu), 'MU');
305  $comment = GETPOST('comment');
306  $inventorycode = dol_print_date(dol_now(), 'dayhourlog');
307  $now = dol_now();
308  $eatby = '';
309  $sellby = '';
310  $batch = '';
311  if ($modebatch == "batch") {
312  $eatby = $dDLUO;
313  $sellby = $dDLC;
314  $batch = $lot ;
315  }
316  if ($product > 0 && $qtymouv != 0) {
317  // $price should take into account discount (except if option STOCK_EXCLUDE_DISCOUNT_FOR_PMP is on)
318  $mouv->origin = $objectorder;
319  $mouv->setOrigin($objectorder->element, $objectorder->id);
320 
321  // Method change if qty < 0
322  if (!empty($conf->global->SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN) && $qtymouv < 0) {
323  $result = $mouv->reception($user, $product, $entrepot, $qtymouv*(-1), $price, $comment, $eatby, $sellby, $batch, '', 0, $inventorycode);
324  } else {
325  $result = $mouv->livraison($user, $product, $entrepot, $qtymouv, $price, $comment, $now, $eatby, $sellby, $batch, 0, $inventorycode);
326  }
327 
328  if ($result < 0) {
329  setEventMessages($mouv->error, $mouv->errors, 'errors');
330  $error++;
331  }
332  }
333  }
334  */
335  }
336  }
337  }
338  }
339 
340  if ($error > 0) {
341  $db->rollback();
342  setEventMessages($error, $errors, 'errors');
343  } else {
344  $db->commit();
345  setEventMessages($langs->trans("ReceptionUpdated"), null);
346 
347  header("Location: ".DOL_URL_ROOT.'/expedition/dispatch.php?id='.$object->id);
348  exit;
349  }
350 } elseif ($action == 'setdate_livraison' && $usercancreate) {
351  $datedelivery = dol_mktime(GETPOST('liv_hour', 'int'), GETPOST('liv_min', 'int'), 0, GETPOST('liv_month', 'int'), GETPOST('liv_day', 'int'), GETPOST('liv_year', 'int'));
352 
353  $object->fetch($id);
354  $result = $object->setDeliveryDate($user, $datedelivery);
355  if ($result < 0) {
356  setEventMessages($object->error, $object->errors, 'errors');
357  }
358 }
359 
360 
361 /*
362  * View
363  */
364 
365 $now = dol_now();
366 
367 $form = new Form($db);
368 $formproduct = new FormProduct($db);
369 $warehouse_static = new Entrepot($db);
370 
371 $title = $object->ref." - ".$langs->trans('ShipmentDistribution');
372 $help_url = 'EN:Module_Shipments|FR:Module_Expéditions|ES:M&oacute;dulo_Expediciones|DE:Modul_Lieferungen';
373 $morejs = array('/expedition/js/lib_dispatch.js.php');
374 
375 llxHeader('', $title, $help_url, '', 0, 0, $morejs);
376 
377 if ($object->id > 0 || !empty($object->ref)) {
378  $lines = $object->lines; // This is an array of detail of line, on line per source order line found intolines[]->fk_origin_line, then each line may have sub data
379  //var_dump($lines[0]->fk_origin_line); exit;
380 
381  $num_prod = count($lines);
382 
383  if (!empty($object->origin) && $object->origin_id > 0) {
384  $object->origin = 'commande';
385  $typeobject = $object->origin;
386  $origin = $object->origin;
387  $origin_id = $object->origin_id;
388  $object->fetch_origin(); // Load property $object->commande, $object->propal, ...
389  }
390  $soc = new Societe($db);
391  $soc->fetch($object->socid);
392 
393  $author = new User($db);
394  $author->fetch($object->user_author_id);
395 
396  $head = shipping_prepare_head($object);
397 
398  print dol_get_fiche_head($head, 'dispatch', $langs->trans("Shipment"), -1, $object->picto);
399 
400 
401  $formconfirm = '';
402 
403  // Confirmation to delete line
404  if ($action == 'ask_deleteline') {
405  $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_deleteline', '', 0, 1);
406  }
407 
408  // Call Hook formConfirm
409  $parameters = array('lineid' => $lineid);
410  // Note that $action and $object may be modified by hook
411  $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action);
412  if (empty($reshook)) {
413  $formconfirm .= $hookmanager->resPrint;
414  } elseif ($reshook > 0) {
415  $formconfirm = $hookmanager->resPrint;
416  }
417 
418  // Print form confirm
419  print $formconfirm;
420 
421  if ($typeobject == 'commande' && $object->$typeobject->id && isModEnabled('commande')) {
422  $objectsrc = new Commande($db);
423  $objectsrc->fetch($object->$typeobject->id);
424  }
425  if ($typeobject == 'propal' && $object->$typeobject->id && isModEnabled("propal")) {
426  $objectsrc = new Propal($db);
427  $objectsrc->fetch($object->$typeobject->id);
428  }
429 
430  // Shipment card
431  $linkback = '<a href="'.DOL_URL_ROOT.'/expedition/list.php?restore_lastsearch_values=1'.(!empty($socid) ? '&socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
432  $morehtmlref = '<div class="refidno">';
433 
434  // Ref customer shipment
435  $morehtmlref .= $form->editfieldkey("RefCustomer", 'ref_customer', $object->ref_customer, $object, $user->rights->expedition->creer, 'string', '', 0, 1);
436  $morehtmlref .= $form->editfieldval("RefCustomer", 'ref_customer', $object->ref_customer, $object, $user->rights->expedition->creer, 'string'.(isset($conf->global->THIRDPARTY_REF_INPUT_SIZE) ? ':'.$conf->global->THIRDPARTY_REF_INPUT_SIZE : ''), '', null, null, '', 1);
437 
438  // Thirdparty
439  $morehtmlref .= '<br>'.$object->thirdparty->getNomUrl(1);
440  // Project
441  if (isModEnabled('project')) {
442  $langs->load("projects");
443  $morehtmlref .= '<br>';
444  if (0) { // Do not change on reception
445  $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
446  if ($action != 'classify' && $permissiontoadd) {
447  $morehtmlref .= '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=classify&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
448  }
449  $morehtmlref .= $form->form_project($_SERVER['PHP_SELF'].'?id='.$object->id, (empty($conf->global->PROJECT_CAN_ALWAYS_LINK_TO_ALL_SUPPLIERS) ? $object->socid : -1), $object->fk_project, ($action == 'classify' ? 'projectid' : 'none'), 0, 0, 0, 1, '', 'maxwidth300');
450  } else {
451  if (!empty($objectsrc) && !empty($objectsrc->fk_project)) {
452  $proj = new Project($db);
453  $proj->fetch($objectsrc->fk_project);
454  $morehtmlref .= $proj->getNomUrl(1);
455  if ($proj->title) {
456  $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
457  }
458  }
459  }
460  }
461  $morehtmlref .= '</div>';
462 
463  dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
464 
465 
466  print '<div class="fichecenter">';
467  print '<div class="underbanner clearboth"></div>';
468 
469  print '<table class="border tableforfield centpercent">';
470 
471  // Linked documents
472  if ($typeobject == 'commande' && $object->$typeobject->id && isModEnabled('commande')) {
473  print '<tr><td>';
474  print $langs->trans("RefOrder").'</td>';
475  print '<td colspan="3">';
476  print $objectsrc->getNomUrl(1, 'commande');
477  print "</td>\n";
478  print '</tr>';
479  }
480  if ($typeobject == 'propal' && $object->$typeobject->id && isModEnabled("propal")) {
481  print '<tr><td>';
482  print $langs->trans("RefProposal").'</td>';
483  print '<td colspan="3">';
484  print $objectsrc->getNomUrl(1, 'expedition');
485  print "</td>\n";
486  print '</tr>';
487  }
488 
489  // Date creation
490  print '<tr><td class="titlefield">'.$langs->trans("DateCreation").'</td>';
491  print '<td colspan="3">'.dol_print_date($object->date_creation, "dayhour", "tzuserrel")."</td>\n";
492  print '</tr>';
493 
494  // Delivery date planned
495  print '<tr><td height="10">';
496  print '<table class="nobordernopadding" width="100%"><tr><td>';
497  print $langs->trans('DateDeliveryPlanned');
498  print '</td>';
499  if ($action != 'editdate_livraison') {
500  print '<td class="right"><a class="editfielda" href="'.$_SERVER["PHP_SELF"].'?action=editdate_livraison&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->trans('SetDeliveryDate'), 1).'</a></td>';
501  }
502  print '</tr></table>';
503  print '</td><td colspan="2">';
504  if ($action == 'editdate_livraison') {
505  print '<form name="setdate_livraison" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'" method="post">';
506  print '<input type="hidden" name="token" value="'.newToken().'">';
507  print '<input type="hidden" name="action" value="setdate_livraison">';
508  print $form->selectDate($object->date_delivery ? $object->date_delivery : -1, 'liv_', 1, 1, '', "setdate_livraison", 1, 0);
509  print '<input type="submit" class="button button-edit smallpaddingimp" value="'.$langs->trans('Modify').'">';
510  print '</form>';
511  } else {
512  print $object->date_delivery ? dol_print_date($object->date_delivery, 'dayhour') : '&nbsp;';
513  }
514  print '</td>';
515  print '</tr></table>';
516 
517  print '<br><center>';
518  print '<a href="#" id="resetalltoexpected" class="marginrightonly paddingright marginleftonly paddingleft">'.img_picto("", 'autofill', 'class="pictofixedwidth"').$langs->trans("RestoreWithCurrentQtySaved").'</a></td>';
519  // Link to clear qty
520  print '<a href="#" id="autoreset" class="marginrightonly paddingright marginleftonly paddingleft">'.img_picto("", 'eraser', 'class="pictofixedwidth"').$langs->trans("ClearQtys").'</a></td>';
521  print '<center>';
522 
523  print '<br>';
524  $disabled = 0; // This is used to disable or not the bulk selection of target warehouse. No reason to have it disabled so forced to 0.
525 
526  if ($object->statut == Expedition::STATUS_DRAFT) {
527  require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
528  $formproduct = new FormProduct($db);
529  $formproduct->loadWarehouses();
530  $entrepot = new Entrepot($db);
531  $listwarehouses = $entrepot->list_array(1);
532 
533 
534  print '<form method="post" action="'.$_SERVER["PHP_SELF"].'">';
535 
536  print '<input type="hidden" name="token" value="'.newToken().'">';
537  print '<input type="hidden" name="action" value="updatelines">';
538  print '<input type="hidden" name="id" value="'.$object->id.'">';
539 
540  print '<div class="div-table-responsive-no-min">';
541  print '<table class="noborder centpercent">';
542 
543  // Get list of lines of the shipment $products_dispatched, with qty dispatched for each product id
544  $products_dispatched = array();
545  $sql = "SELECT ed.fk_origin_line as rowid, sum(ed.qty) as qty";
546  $sql .= " FROM ".MAIN_DB_PREFIX."expeditiondet as ed";
547  $sql .= " WHERE ed.fk_expedition = ".((int) $object->id);
548  $sql .= " GROUP BY ed.fk_origin_line";
549 
550  $resql = $db->query($sql);
551  if ($resql) {
552  $num = $db->num_rows($resql);
553  $i = 0;
554 
555  if ($num) {
556  while ($i < $num) {
557  $objd = $db->fetch_object($resql);
558  $products_dispatched[$objd->rowid] = price2num($objd->qty, 'MS');
559  $i++;
560  }
561  }
562  $db->free($resql);
563  }
564 
565  //$sql = "SELECT l.rowid, l.fk_product, l.subprice, l.remise_percent, l.ref AS sref, SUM(l.qty) as qty,";
566  $sql = "SELECT l.rowid, l.fk_product, l.subprice, l.remise_percent, '' AS sref, l.qty as qty,";
567  $sql .= " p.ref, p.label, p.tobatch, p.fk_default_warehouse";
568  // Enable hooks to alter the SQL query (SELECT)
569  $parameters = array();
570  $reshook = $hookmanager->executeHooks(
571  'printFieldListSelect',
572  $parameters,
573  $object,
574  $action
575  );
576  if ($reshook < 0) {
577  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
578  }
579  $sql .= $hookmanager->resPrint;
580 
581  $sql .= " FROM ".MAIN_DB_PREFIX."commandedet as l";
582  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON l.fk_product=p.rowid";
583  $sql .= " WHERE l.fk_commande = ".((int) $objectsrc->id);
584  if (empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
585  $sql .= " AND l.product_type = 0";
586  }
587  // Enable hooks to alter the SQL query (WHERE)
588  $parameters = array();
589  $reshook = $hookmanager->executeHooks(
590  'printFieldListWhere',
591  $parameters,
592  $object,
593  $action
594  );
595  if ($reshook < 0) {
596  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
597  }
598  $sql .= $hookmanager->resPrint;
599 
600  //$sql .= " GROUP BY p.ref, p.label, p.tobatch, p.fk_default_warehouse, l.rowid, l.fk_product, l.subprice, l.remise_percent, l.ref"; // Calculation of amount dispatched is done per fk_product so we must group by fk_product
601  $sql .= " ORDER BY l.rang, p.ref, p.label";
602 
603  $resql = $db->query($sql);
604  if ($resql) {
605  $num = $db->num_rows($resql);
606  $i = 0;
607  $numline = 1;
608 
609  if ($num) {
610  print '<tr class="liste_titre">';
611 
612  print '<td>'.$langs->trans("Description").'</td>';
613  if (isModEnabled('productbatch')) {
614  print '<td class="dispatch_batch_number_title">'.$langs->trans("batch_number").'</td>';
615  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
616  print '<td class="dispatch_dlc_title">'.$langs->trans("SellByDate").'</td>';
617  }
618  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
619  print '<td class="dispatch_dluo_title">'.$langs->trans("EatByDate").'</td>';
620  }
621  } else {
622  print '<td></td>';
623  print '<td></td>';
624  print '<td></td>';
625  }
626  print '<td class="right">'.$langs->trans("QtyOrdered").'</td>';
627  if ($object->status == Expedition::STATUS_DRAFT) {
628  print '<td class="right">'.$langs->trans("QtyToShip"); // Qty to dispatch (sum for all lines of batch detail if there is)
629  } else {
630  print '<td class="right">'.$langs->trans("QtyDispatchedShort").'</td>';
631  }
632  print '<td class="right">'.$langs->trans("Details");
633  print '<td width="32"></td>';
634 
635  if (!empty($conf->global->SUPPLIER_ORDER_CAN_UPDATE_BUYINGPRICE_DURING_RECEIPT)) {
636  if (!isModEnabled("multicurrency") && empty($conf->dynamicprices->enabled)) {
637  print '<td class="right">'.$langs->trans("Price").'</td>';
638  print '<td class="right">'.$langs->trans("ReductionShort").' (%)</td>';
639  print '<td class="right">'.$langs->trans("UpdatePrice").'</td>';
640  }
641  }
642 
643  print '<td align="right">'.$langs->trans("Warehouse");
644 
645  // Select warehouse to force it everywhere
646  if (count($listwarehouses) > 1) {
647  print '<br><span class="opacitymedium">'.$langs->trans("ForceTo").'</span> '.$form->selectarray('fk_default_warehouse', $listwarehouses, $fk_default_warehouse, 1, 0, 0, '', 0, 0, $disabled, '', 'minwidth100 maxwidth300', 1);
648  } elseif (count($listwarehouses) == 1) {
649  print '<br><span class="opacitymedium">'.$langs->trans("ForceTo").'</span> '.$form->selectarray('fk_default_warehouse', $listwarehouses, $fk_default_warehouse, 0, 0, 0, '', 0, 0, $disabled, '', 'minwidth100 maxwidth300', 1);
650  }
651 
652  print '</td>';
653 
654  // Enable hooks to append additional columns
655  $parameters = array();
656  $reshook = $hookmanager->executeHooks(
657  'printFieldListTitle',
658  $parameters,
659  $object,
660  $action
661  );
662  if ($reshook < 0) {
663  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
664  }
665  print $hookmanager->resPrint;
666 
667  print "</tr>\n";
668  }
669 
670  $nbfreeproduct = 0; // Nb of lins of free products/services
671  $nbproduct = 0; // Nb of predefined product lines to dispatch (already done or not) if SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED is off (default)
672  // or nb of line that remain to dispatch if SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED is on.
673 
674  $conf->cache['product'] = array();
675 
676  // Loop on each line of origin order
677  while ($i < $num) {
678  $objp = $db->fetch_object($resql);
679 
680  // On n'affiche pas les produits libres
681  if (!$objp->fk_product > 0) {
682  $nbfreeproduct++;
683  } else {
684  $alreadydispatched = isset($products_dispatched[$objp->rowid])?$products_dispatched[$objp->rowid]:0;
685  $remaintodispatch = price2num($objp->qty, 5); // Calculation of dispatched
686  if ($remaintodispatch < 0 && empty($conf->global->SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN)) {
687  $remaintodispatch = 0;
688  }
689 
690  if ($remaintodispatch || empty($conf->global->SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED)) {
691  $nbproduct++;
692 
693  // To show detail cref and description value, we must make calculation by cref
694  // print ($objp->cref?' ('.$objp->cref.')':'');
695  // if ($objp->description) print '<br>'.nl2br($objp->description);
696  $suffix = '_0_'.$i;
697 
698  print "\n";
699  print '<!-- Line to dispatch '.$suffix.' -->'."\n";
700  // hidden fields for js function
701  print '<input id="qty_ordered'.$suffix.'" type="hidden" value="'.$objp->qty.'">';
702  print '<input id="qty_dispatched'.$suffix.'" type="hidden" data-dispatched="'.((float) $alreadydispatched).'" value="'.(float) $alreadydispatched.'">';
703  print '<tr class="oddeven">';
704 
705  if (empty($conf->cache['product'][$objp->fk_product])) {
706  $tmpproduct = new Product($db);
707  $tmpproduct->fetch($objp->fk_product);
708  $conf->cache['product'][$objp->fk_product] = $tmpproduct;
709  } else {
710  $tmpproduct = $conf->cache['product'][$objp->fk_product];
711  }
712 
713  $linktoprod = $tmpproduct->getNomUrl(1);
714  $linktoprod .= ' - '.$objp->label."\n";
715 
716  if (isModEnabled('productbatch')) {
717  if ($objp->tobatch) {
718  // Product
719  print '<td>';
720  print $linktoprod;
721  print "</td>";
722  print '<td class="dispatch_batch_number"></td>';
723  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
724  print '<td class="dispatch_dlc"></td>';
725  }
726  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
727  print '<td class="dispatch_dluo"></td>';
728  }
729  } else {
730  // Product
731  print '<td>';
732  print $linktoprod;
733  print "</td>";
734  print '<td class="dispatch_batch_number">';
735  print '<span class="opacitymedium small">'.$langs->trans("ProductDoesNotUseBatchSerial").'</span>';
736  print '</td>';
737  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
738  print '<td class="dispatch_dlc"></td>';
739  }
740  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
741  print '<td class="dispatch_dluo"></td>';
742  }
743  }
744  } else {
745  print '<td colspan="4">';
746  print $linktoprod;
747  print "</td>";
748  }
749 
750  // Define unit price for PMP calculation
751  $up_ht_disc = $objp->subprice;
752  if (!empty($objp->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
753  $up_ht_disc = price2num($up_ht_disc * (100 - $objp->remise_percent) / 100, 'MU');
754  }
755 
756  // Qty ordered
757  print '<td class="right">'.$objp->qty.'</td>';
758 
759  // Already dispatched
760  print '<td class="right">'.$alreadydispatched.'</td>';
761 
762  print '<td class="right">';
763  print '</td>'; // Qty to dispatch
764  print '<td>';
765  print '</td>'; // Dispatch column
766  print '<td></td>'; // Warehouse column
767 
768  /*$sql = "SELECT cfd.rowid, cfd.qty, cfd.fk_entrepot, cfd.batch, cfd.eatby, cfd.sellby, cfd.fk_product";
769  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as cfd";
770  $sql .= " WHERE cfd.fk_commandefourndet = ".(int) $objp->rowid;*/
771 
772  $sql = "SELECT ed.rowid, ed.qty, ed.fk_entrepot, eb.batch, eb.eatby, eb.sellby, cd.fk_product";
773  $sql .= " FROM ".MAIN_DB_PREFIX."expeditiondet as ed";
774  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."expeditiondet_batch as eb on ed.rowid = eb.fk_expeditiondet";
775  $sql .= " JOIN ".MAIN_DB_PREFIX."commandedet as cd on ed.fk_origin_line = cd.rowid";
776  $sql .= " WHERE ed.fk_origin_line =".(int) $objp->rowid;
777  $sql .= " AND ed.fk_expedition =".(int) $object->id;
778  $sql .= " ORDER BY ed.rowid, ed.fk_origin_line";
779 
780  $resultsql = $db->query($sql);
781  $j = 0;
782  if ($resultsql) {
783  $numd = $db->num_rows($resultsql);
784 
785  while ($j < $numd) {
786  $suffix = "_".$j."_".$i;
787  $objd = $db->fetch_object($resultsql);
788 
789  if (isModEnabled('productbatch') && (!empty($objd->batch) || (is_null($objd->batch) && $tmpproduct->status_batch > 0))) {
790  $type = 'batch';
791 
792  // Enable hooks to append additional columns
793  $parameters = array(
794  // allows hook to distinguish between the rows with information and the rows with dispatch form input
795  'is_information_row' => true,
796  'j' => $j,
797  'suffix' => $suffix,
798  'objd' => $objd,
799  );
800  $reshook = $hookmanager->executeHooks(
801  'printFieldListValue',
802  $parameters,
803  $object,
804  $action
805  );
806  if ($reshook < 0) {
807  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
808  }
809  print $hookmanager->resPrint;
810 
811  print '</tr>';
812 
813  print '<!-- line for batch '.$numline.' -->';
814  print '<tr class="oddeven autoresettr" name="'.$type.$suffix.'" data-remove="clear">';
815  print '<td>';
816  print '<input id="fk_commandedet'.$suffix.'" name="fk_commandedet'.$suffix.'" type="hidden" value="'.$objp->rowid.'">';
817  print '<input id="idline'.$suffix.'" name="idline'.$suffix.'" type="hidden" value="'.$objd->rowid.'">';
818  print '<input name="product_batch'.$suffix.'" type="hidden" value="'.$objd->fk_product.'">';
819 
820  print '<!-- This is a U.P. (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
821  print '<input class="maxwidth75" name="pu'.$suffix.'" type="hidden" value="'.price2num($up_ht_disc, 'MU').'">';
822 
823  print '</td>';
824 
825  print '<td>';
826  print '<input type="text" class="inputlotnumber quatrevingtquinzepercent" id="lot_number'.$suffix.'" name="lot_number'.$suffix.'" value="'.(GETPOSTISSET('lot_number'.$suffix) ? GETPOST('lot_number'.$suffix) : $objd->batch).'">';
827  //print '<input type="hidden" id="lot_number'.$suffix.'" name="lot_number'.$suffix.'" value="'.$objd->batch.'">';
828  print '</td>';
829  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
830  print '<td class="nowraponall">';
831  $dlcdatesuffix = !empty($objd->sellby) ? dol_stringtotime($objd->sellby) : dol_mktime(0, 0, 0, GETPOST('dlc'.$suffix.'month'), GETPOST('dlc'.$suffix.'day'), GETPOST('dlc'.$suffix.'year'));
832  print $form->selectDate($dlcdatesuffix, 'dlc'.$suffix, '', '', 1, '');
833  print '</td>';
834  }
835  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
836  print '<td class="nowraponall">';
837  $dluodatesuffix = !empty($objd->eatby) ? dol_stringtotime($objd->eatby) : dol_mktime(0, 0, 0, GETPOST('dluo'.$suffix.'month'), GETPOST('dluo'.$suffix.'day'), GETPOST('dluo'.$suffix.'year'));
838  print $form->selectDate($dluodatesuffix, 'dluo'.$suffix, '', '', 1, '');
839  print '</td>';
840  }
841  print '<td colspan="2">&nbsp;</td>'; // Supplier ref + Qty ordered + qty already dispatched
842  } else {
843  $type = 'dispatch';
844  $colspan = 6;
845  $colspan = (!empty($conf->global->PRODUCT_DISABLE_SELLBY)) ? --$colspan : $colspan;
846  $colspan = (!empty($conf->global->PRODUCT_DISABLE_EATBY)) ? --$colspan : $colspan;
847 
848  // Enable hooks to append additional columns
849  $parameters = array(
850  // allows hook to distinguish between the rows with information and the rows with dispatch form input
851  'is_information_row' => true,
852  'j' => $j,
853  'suffix' => $suffix,
854  'objd' => $objd,
855  );
856  $reshook = $hookmanager->executeHooks(
857  'printFieldListValue',
858  $parameters,
859  $object,
860  $action
861  );
862  if ($reshook < 0) {
863  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
864  }
865  print $hookmanager->resPrint;
866 
867  print '</tr>';
868 
869  print '<!-- line no batch '.$numline.' -->';
870  print '<tr class="oddeven autoresettr" name="'.$type.$suffix.'" data-remove="clear">';
871  print '<td colspan="'.$colspan.'">';
872  print '<input id="fk_commandedet'.$suffix.'" name="fk_commandedet'.$suffix.'" type="hidden" value="'.$objp->rowid.'">';
873  print '<input id="idline'.$suffix.'" name="idline'.$suffix.'" type="hidden" value="'.$objd->rowid.'">';
874  print '<input name="product'.$suffix.'" type="hidden" value="'.$objd->fk_product.'">';
875  print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
876  print '<input class="maxwidth75" name="pu'.$suffix.'" type="hidden" value="'.price2num($up_ht_disc, 'MU').'">';
877  print '</td>';
878  }
879  // Qty to dispatch
880  print '<td class="right">';
881  print '<a href="" id="reset'.$suffix.'" class="resetline">'.img_picto($langs->trans("Reset"), 'eraser', 'class="pictofixedwidth opacitymedium"').'</a>';
882  $suggestedvalue = (GETPOSTISSET('qty'.$suffix) ? GETPOST('qty'.$suffix, 'int') : $objd->qty);
883  //var_dump($suggestedvalue);exit;
884  print '<input id="qty'.$suffix.'" onchange="onChangeDispatchLineQty($(this))" name="qty'.$suffix.'" data-type="'.$type.'" data-index="'.$i.'" class="width50 right qtydispatchinput" value="'.$suggestedvalue.'" data-expected="'.$objd->qty.'">';
885  print '</td>';
886  print '<td>';
887  if (isModEnabled('productbatch') && $objp->tobatch > 0) {
888  $type = 'batch';
889  print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" '.($numd != $j+1 ? 'style="display:none"' : '').' onClick="addDispatchLine('.$i.', \''.$type.'\')"');
890  } else {
891  $type = 'dispatch';
892  print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" '.($numd != $j+1 ? 'style="display:none"' : '').' onClick="addDispatchLine('.$i.', \''.$type.'\')"');
893  }
894 
895  print '</td>';
896 
897  // Warehouse
898  print '<td class="right">';
899  if (count($listwarehouses) > 1) {
900  print $formproduct->selectWarehouses(GETPOST("entrepot".$suffix) ?GETPOST("entrepot".$suffix) : $objd->fk_entrepot, "entrepot".$suffix, '', 1, 0, $objp->fk_product, '', 1, 0, null, 'csswarehouse'.$suffix);
901  } elseif (count($listwarehouses) == 1) {
902  print $formproduct->selectWarehouses(GETPOST("entrepot".$suffix) ?GETPOST("entrepot".$suffix) : $objd->fk_entrepot, "entrepot".$suffix, '', 0, 0, $objp->fk_product, '', 1, 0, null, 'csswarehouse'.$suffix);
903  } else {
904  $langs->load("errors");
905  print $langs->trans("ErrorNoWarehouseDefined");
906  }
907  print "</td>\n";
908 
909  // Enable hooks to append additional columns
910  $parameters = array(
911  'is_information_row' => false, // this is a dispatch form row
912  'i' => $i,
913  'suffix' => $suffix,
914  'objp' => $objp,
915  );
916  $reshook = $hookmanager->executeHooks(
917  'printFieldListValue',
918  $parameters,
919  $object,
920  $action
921  );
922  if ($reshook < 0) {
923  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
924  }
925  print $hookmanager->resPrint;
926 
927  print "</tr>\n";
928  $j++;
929 
930  $numline++;
931  }
932  $suffix = "_".$j."_".$i;
933  }
934 
935  if ($j == 0) {
936  if (isModEnabled('productbatch') && !empty($objp->tobatch)) {
937  $type = 'batch';
938 
939  // Enable hooks to append additional columns
940  $parameters = array(
941  // allows hook to distinguish between the rows with information and the rows with dispatch form input
942  'is_information_row' => true,
943  'j' => $j,
944  'suffix' => $suffix,
945  'objp' => $objp,
946  );
947  $reshook = $hookmanager->executeHooks(
948  'printFieldListValue',
949  $parameters,
950  $object,
951  $action
952  );
953  if ($reshook < 0) {
954  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
955  }
956  print $hookmanager->resPrint;
957 
958  print '</tr>';
959 
960  print '<!-- line for batch '.$numline.' (not dispatched line yet for this order line) -->';
961  print '<tr class="oddeven autoresettr" name="'.$type.$suffix.'">';
962  print '<td>';
963  print '<input id="fk_commandedet'.$suffix.'" name="fk_commandedet'.$suffix.'" type="hidden" value="'.$objp->rowid.'">';
964  print '<input id="idline'.$suffix.'" name="idline'.$suffix.'" type="hidden" value="-1">';
965  print '<input name="product_batch'.$suffix.'" type="hidden" value="'.$objp->fk_product.'">';
966 
967  print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
968  print '<input class="maxwidth75" name="pu'.$suffix.'" type="hidden" value="'.price2num($up_ht_disc, 'MU').'">';
969  print '</td>';
970 
971  print '<td>';
972  print '<input type="text" class="inputlotnumber quatrevingtquinzepercent" id="lot_number'.$suffix.'" name="lot_number'.$suffix.'" value="'.GETPOST('lot_number'.$suffix).'">';
973  print '</td>';
974  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
975  print '<td class="nowraponall">';
976  $dlcdatesuffix = dol_mktime(0, 0, 0, GETPOST('dlc'.$suffix.'month'), GETPOST('dlc'.$suffix.'day'), GETPOST('dlc'.$suffix.'year'));
977  print $form->selectDate($dlcdatesuffix, 'dlc'.$suffix, '', '', 1, '');
978  print '</td>';
979  }
980  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
981  print '<td class="nowraponall">';
982  $dluodatesuffix = dol_mktime(0, 0, 0, GETPOST('dluo'.$suffix.'month'), GETPOST('dluo'.$suffix.'day'), GETPOST('dluo'.$suffix.'year'));
983  print $form->selectDate($dluodatesuffix, 'dluo'.$suffix, '', '', 1, '');
984  print '</td>';
985  }
986  print '<td colspan="2">&nbsp;</td>'; // Supplier ref + Qty ordered + qty already dispatched
987  } else {
988  $type = 'dispatch';
989  $colspan = 6;
990  $colspan = (!empty($conf->global->PRODUCT_DISABLE_SELLBY)) ? --$colspan : $colspan;
991  $colspan = (!empty($conf->global->PRODUCT_DISABLE_EATBY)) ? --$colspan : $colspan;
992 
993  // Enable hooks to append additional columns
994  $parameters = array(
995  // allows hook to distinguish between the rows with information and the rows with dispatch form input
996  'is_information_row' => true,
997  'j' => $j,
998  'suffix' => $suffix,
999  'objp' => $objp,
1000  );
1001  $reshook = $hookmanager->executeHooks(
1002  'printFieldListValue',
1003  $parameters,
1004  $object,
1005  $action
1006  );
1007  if ($reshook < 0) {
1008  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1009  }
1010  print $hookmanager->resPrint;
1011 
1012  print '</tr>';
1013 
1014  print '<!-- line no batch '.$numline.' (not dispatched line yet for this order line) -->';
1015  print '<tr class="oddeven autoresettr" name="'.$type.$suffix.'" data-remove="clear">';
1016  print '<td colspan="'.$colspan.'">';
1017  print '<input id="fk_commandedet'.$suffix.'" name="fk_commandedet'.$suffix.'" type="hidden" value="'.$objp->rowid.'">';
1018  print '<input id="idline'.$suffix.'" name="idline'.$suffix.'" type="hidden" value="-1">';
1019  print '<input name="product'.$suffix.'" type="hidden" value="'.$objp->fk_product.'">';
1020 
1021  print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
1022  print '<input class="maxwidth75" name="pu'.$suffix.'" type="hidden" value="'.price2num($up_ht_disc, 'MU').'">';
1023  print '</td>';
1024  }
1025  // Qty to dispatch
1026  print '<td class="right">';
1027  print '<a href="" id="reset'.$suffix.'" class="resetline">'.img_picto($langs->trans("Reset"), 'eraser', 'class="pictofixedwidth opacitymedium"').'</a>';
1028  $amounttosuggest = (GETPOSTISSET('qty'.$suffix) ? GETPOST('qty'.$suffix, 'int') : (empty($conf->global->SUPPLIER_ORDER_DISPATCH_FORCE_QTY_INPUT_TO_ZERO) ? $remaintodispatch : 0));
1029  if (count($products_dispatched)) {
1030  // There is already existing lines into llx_expeditiondet, this means a plan for the shipment has already been started.
1031  // In such a case, we do not suggest new values, we suggest the value known.
1032  $amounttosuggest = (GETPOSTISSET('qty'.$suffix) ? GETPOST('qty'.$suffix, 'int') : (isset($products_dispatched[$objp->rowid]) ? $products_dispatched[$objp->rowid] : ''));
1033  }
1034  print '<input id="qty'.$suffix.'" onchange="onChangeDispatchLineQty($(this))" name="qty'.$suffix.'" data-index="'.$i.'" data-type="text" class="width50 right qtydispatchinput" value="'.$amounttosuggest.'" data-expected="'.$amounttosuggest.'">';
1035  print '</td>';
1036  print '<td>';
1037  if (isModEnabled('productbatch') && $objp->tobatch > 0) {
1038  $type = 'batch';
1039  print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine('.$i.', \''.$type.'\')"');
1040  } else {
1041  $type = 'dispatch';
1042  print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine('.$i.', \''.$type.'\')"');
1043  }
1044 
1045  print '</td>';
1046 
1047  // Warehouse
1048  print '<td class="right">';
1049  if (count($listwarehouses) > 1) {
1050  print $formproduct->selectWarehouses(GETPOST("entrepot".$suffix) ?GETPOST("entrepot".$suffix) : ($objp->fk_default_warehouse ? $objp->fk_default_warehouse : ''), "entrepot".$suffix, '', 1, 0, $objp->fk_product, '', 1, 0, null, 'csswarehouse'.$suffix);
1051  } elseif (count($listwarehouses) == 1) {
1052  print $formproduct->selectWarehouses(GETPOST("entrepot".$suffix) ?GETPOST("entrepot".$suffix) : ($objp->fk_default_warehouse ? $objp->fk_default_warehouse : ''), "entrepot".$suffix, '', 0, 0, $objp->fk_product, '', 1, 0, null, 'csswarehouse'.$suffix);
1053  } else {
1054  $langs->load("errors");
1055  print $langs->trans("ErrorNoWarehouseDefined");
1056  }
1057  print "</td>\n";
1058 
1059  // Enable hooks to append additional columns
1060  $parameters = array(
1061  'is_information_row' => false, // this is a dispatch form row
1062  'i' => $i,
1063  'suffix' => $suffix,
1064  'objp' => $objp,
1065  );
1066  $reshook = $hookmanager->executeHooks(
1067  'printFieldListValue',
1068  $parameters,
1069  $object,
1070  $action
1071  );
1072  if ($reshook < 0) {
1073  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1074  }
1075  print $hookmanager->resPrint;
1076  print "</tr>\n";
1077  }
1078  }
1079  }
1080  $i++;
1081  }
1082  $db->free($resql);
1083  } else {
1084  dol_print_error($db);
1085  }
1086 
1087  print "</table>\n";
1088  print '</div>';
1089 
1090  if ($nbproduct) {
1091  //$checkboxlabel = $langs->trans("CloseReceivedSupplierOrdersAutomatically", $langs->transnoentitiesnoconv('StatusOrderReceivedAll'));
1092 
1093  print '<div class="center">';
1094  $parameters = array();
1095  $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
1096  // modified by hook
1097  if (empty($reshook)) {
1098  /*if (empty($conf->reception->enabled)) {
1099  print $langs->trans("Comment").' : ';
1100  print '<input type="text" class="minwidth400" maxlength="128" name="comment" value="';
1101  print GETPOSTISSET("comment") ? GETPOST("comment") : $langs->trans("DispatchSupplierOrder", $object->ref);
1102  // print ' / '.$object->ref_supplier; // Not yet available
1103  print '" class="flat"><br>';
1104 
1105  print '<input type="checkbox" checked="checked" name="closeopenorder"> '.$checkboxlabel;
1106  }
1107 
1108  $dispatchBt = empty($conf->reception->enabled) ? $langs->trans("Receive") : $langs->trans("CreateReception");
1109 
1110  print '<br>';
1111  */
1112 
1113  print '<input type="submit" id="submitform" class="button" name="dispatch" value="'.$langs->trans("Save").'"';
1114  $disabled = 0;
1115  if (!$usercancreate) {
1116  $disabled = 1;
1117  }
1118  if (count($listwarehouses) <= 0) {
1119  $disabled = 1;
1120  }
1121  if ($disabled) {
1122  print ' disabled';
1123  }
1124 
1125  print '>';
1126  }
1127  print '</div>';
1128  }
1129 
1130  // Message if nothing to dispatch
1131  if (!$nbproduct) {
1132  print "<br>\n";
1133  if (empty($conf->global->SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED)) {
1134  print '<div class="opacitymedium">'.$langs->trans("NoPredefinedProductToDispatch").'</div>'; // No predefined line at all
1135  } else {
1136  print '<div class="opacitymedium">'.$langs->trans("NoMorePredefinedProductToDispatch").'</div>'; // No predefined line that remain to be dispatched.
1137  }
1138  }
1139 
1140  print '</form>';
1141  }
1142 
1143  print dol_get_fiche_end();
1144 
1145  // traitement entrepot par défaut
1146  print '<script type="text/javascript">
1147  $(document).ready(function () {
1148  $("select[name=fk_default_warehouse]").change(function() {
1149  console.log("warehouse is modified");
1150  var fk_default_warehouse = $("option:selected", this).val();
1151  $("select[name^=entrepot_]").val(fk_default_warehouse).change();
1152  });
1153 
1154  $("#autoreset").click(function() {
1155  console.log("we click on autoreset");
1156  $(".autoresettr").each(function(){
1157  id = $(this).attr("name");
1158  idtab = id.split("_");
1159  if ($(this).data("remove") == "clear"){
1160  console.log("We clear the object to expected value")
1161  $("#qty_"+idtab[1]+"_"+idtab[2]).val("");
1162  /*
1163  qtyexpected = $("#qty_"+idtab[1]+"_"+idtab[2]).data("expected")
1164  console.log(qtyexpected);
1165  $("#qty_"+idtab[1]+"_"+idtab[2]).val(qtyexpected);
1166  qtydispatched = $("#qty_dispatched_0_"+idtab[2]).data("dispatched")
1167  $("#qty_dispatched_0_"+idtab[2]).val(qtydispatched);
1168  */
1169  } else {
1170  console.log("We remove the object")
1171  $(this).remove();
1172  $("tr[name^=\'"+idtab[0]+"_\'][name$=\'_"+idtab[2]+"\']:last .splitbutton").show();
1173  }
1174  });
1175  return false;
1176  });
1177 
1178  $("#resetalltoexpected").click(function(){
1179  $(".qtydispatchinput").each(function(){
1180  console.log("We reset to expected "+$(this).attr("id")+" qty to dispatch");
1181  $(this).val($(this).data("expected"));
1182  });
1183  return false;
1184  });
1185 
1186  $(".resetline").on("click", function(event) {
1187  event.preventDefault();
1188  id = $(this).attr("id");
1189  id = id.split("reset_");
1190  console.log("Reset trigger for id = qty_"+id[1]);
1191  $("#qty_"+id[1]).val("");
1192  });
1193  });
1194  </script>';
1195 }
1196 
1197 // End of page
1198 llxFooter();
1199 $db->close();
if(GETPOST('button_removefilter_x', 'alpha')||GETPOST('button_removefilter.x', 'alpha')||GETPOST('button_removefilter', 'alpha')) if(GETPOST('button_search_x', 'alpha')||GETPOST('button_search.x', 'alpha')||GETPOST('button_search', 'alpha')) if($action=="save" &&empty($cancel)) $help_url
View.
Definition: agenda.php:118
if(!defined('NOREQUIRESOC')) if(!defined('NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) llxHeader()
Empty header.
Definition: wrapper.php:56
llxFooter()
Empty footer.
Definition: wrapper.php:70
Class to manage customers orders.
Class to manage comment.
Class to manage warehouses.
Class to manage shipments.
const STATUS_DRAFT
Draft status.
Classe to manage lines of shipment.
CRUD class for batch number management within shipment.
Class to manage generation of HTML components Only common components must be here.
Class with static methods for building HTML components related to products Only components common to ...
Class to manage products or services.
Class to manage projects.
Class to manage proposals.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage Dolibarr users.
Definition: user.class.php:48
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
Definition: card.php:143
dol_stringtotime($string, $gm=1)
Convert a string date into a GM Timestamps date Warning: YYYY-MM-DDTHH:MM:SS+02:00 (RFC3339) is not s...
Definition: date.lib.php:409
dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='rowid', $fieldref='ref', $morehtmlref='', $moreparam='', $nodbprefix=0, $morehtmlleft='', $morehtmlstatus='', $onlybanner=0, $morehtmlright='')
Show tab footer of a card.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_now($mode='auto')
Return date for now.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
isModEnabled($module)
Is Dolibarr module enabled.
img_edit($titlealt='default', $float=0, $other='')
Show logo editer/modifier fiche.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
$formconfirm
if ($action == 'delbookkeepingyear') {
table tableforfield button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
Definition: style.css.php:853
div float
Buy price without taxes.
Definition: style.css.php:926
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:123
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.
shipping_prepare_head($object)
Prepare array with list of tabs.