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-2023 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 
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("bills", "orders", "sendings", "companies", "deliveries", "products", "stocks", "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 if ($user->socid) {
64  $socid = $user->socid;
65 }
66 
67 $hookmanager->initHooks(array('ordersupplierdispatch'));
68 
69 // Recuperation de l'id de projet
70 $projectid = 0;
71 if (GETPOSTISSET("projectid")) {
72  $projectid = GETPOST("projectid", 'int');
73 }
74 
75 $object = new CommandeFournisseur($db);
76 
77 if ($id > 0 || !empty($ref)) {
78  $result = $object->fetch($id, $ref);
79  if ($result < 0) {
80  setEventMessages($object->error, $object->errors, 'errors');
81  }
82  $result = $object->fetch_thirdparty();
83  if ($result < 0) {
84  setEventMessages($object->error, $object->errors, 'errors');
85  }
86 }
87 
88 if (empty($conf->reception->enabled)) {
89  $permissiontoreceive = $user->hasRight("fournisseur", "commande", "receptionner");
90  $permissiontocontrol = ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && $user->hasRight("fournisseur", "commande", "receptionner")) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && $user->hasRight("fournisseur", "commande_advance", "check")));
91 } else {
92  $permissiontoreceive = $user->hasRight("reception", "creer");
93  $permissiontocontrol = ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && $user->hasRight("reception", "creer")) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && $user->hasRight("reception", "reception_advance", "validate")));
94 }
95 
96 // $id is id of a purchase order.
97 $result = restrictedArea($user, 'fournisseur', $object, 'commande_fournisseur', 'commande');
98 
99 if (!isModEnabled('stock')) {
100  accessforbidden();
101 }
102 
103 $usercancreate = ($user->hasRight("fournisseur", "commande", "creer") || $user->hasRight("supplier_order", "creer"));
104 $permissiontoadd = $usercancreate; // Used by the include of actions_addupdatedelete.inc.php
105 
106 
107 /*
108  * Actions
109  */
110 
111 $parameters = array();
112 $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
113 if ($reshook < 0) {
114  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
115 }
116 
117 if ($action == 'checkdispatchline' && $permissiontocontrol) {
118  $error = 0;
119  $supplierorderdispatch = new CommandeFournisseurDispatch($db);
120 
121  $db->begin();
122 
123  $result = $supplierorderdispatch->fetch($lineid);
124  if (!$result) {
125  $error++;
126  setEventMessages($supplierorderdispatch->error, $supplierorderdispatch->errors, 'errors');
127  $action = '';
128  }
129 
130  if (!$error) {
131  $result = $supplierorderdispatch->setStatut(1);
132  if ($result < 0) {
133  setEventMessages($supplierorderdispatch->error, $supplierorderdispatch->errors, 'errors');
134  $error++;
135  $action = '';
136  }
137  }
138 
139  if (!$error) {
140  $result = $object->calcAndSetStatusDispatch($user);
141  if ($result < 0) {
142  setEventMessages($object->error, $object->errors, 'errors');
143  $error++;
144  $action = '';
145  }
146  }
147  if (!$error) {
148  $db->commit();
149  } else {
150  $db->rollback();
151  }
152 }
153 
154 if ($action == 'uncheckdispatchline' && $permissiontocontrol) {
155  $error = 0;
156  $supplierorderdispatch = new CommandeFournisseurDispatch($db);
157 
158  $db->begin();
159 
160  $result = $supplierorderdispatch->fetch($lineid);
161  if (!$result) {
162  $error++;
163  setEventMessages($supplierorderdispatch->error, $supplierorderdispatch->errors, 'errors');
164  $action = '';
165  }
166 
167  if (!$error) {
168  $result = $supplierorderdispatch->setStatut(0);
169  if ($result < 0) {
170  setEventMessages($supplierorderdispatch->error, $supplierorderdispatch->errors, 'errors');
171  $error++;
172  $action = '';
173  }
174  }
175  if (!$error) {
176  $result = $object->calcAndSetStatusDispatch($user);
177  if ($result < 0) {
178  setEventMessages($object->error, $object->errors, 'errors');
179  $error++;
180  $action = '';
181  }
182  }
183  if (!$error) {
184  $db->commit();
185  } else {
186  $db->rollback();
187  }
188 }
189 
190 if ($action == 'denydispatchline' && $permissiontocontrol) {
191  $error = 0;
192  $supplierorderdispatch = new CommandeFournisseurDispatch($db);
193 
194  $db->begin();
195 
196  $result = $supplierorderdispatch->fetch($lineid);
197  if (!$result) {
198  $error++;
199  setEventMessages($supplierorderdispatch->error, $supplierorderdispatch->errors, 'errors');
200  $action = '';
201  }
202 
203  if (!$error) {
204  $result = $supplierorderdispatch->setStatut(2);
205  if ($result < 0) {
206  setEventMessages($supplierorderdispatch->error, $supplierorderdispatch->errors, 'errors');
207  $error++;
208  $action = '';
209  }
210  }
211  if (!$error) {
212  $result = $object->calcAndSetStatusDispatch($user);
213  if ($result < 0) {
214  setEventMessages($object->error, $object->errors, 'errors');
215  $error++;
216  $action = '';
217  }
218  }
219  if (!$error) {
220  $db->commit();
221  } else {
222  $db->rollback();
223  }
224 }
225 
226 if ($action == 'dispatch' && $permissiontoreceive) {
227  $error = 0;
228  $notrigger = 0;
229 
230  $db->begin();
231 
232  $pos = 0;
233  foreach ($_POST as $key => $value) {
234  // without batch module enabled
235  $reg = array();
236  if (preg_match('/^product_([0-9]+)_([0-9]+)$/i', $key, $reg)) {
237  $pos++;
238 
239  // $numline=$reg[2] + 1; // line of product
240  $numline = $pos;
241  $prod = "product_".$reg[1].'_'.$reg[2];
242  $qty = "qty_".$reg[1].'_'.$reg[2];
243  $ent = "entrepot_".$reg[1].'_'.$reg[2];
244  if (empty(GETPOST($ent))) {
245  $ent = $fk_default_warehouse;
246  }
247  $pu = "pu_".$reg[1].'_'.$reg[2]; // This is unit price including discount
248  $fk_commandefourndet = "fk_commandefourndet_".$reg[1].'_'.$reg[2];
249 
250  if (!empty($conf->global->SUPPLIER_ORDER_CAN_UPDATE_BUYINGPRICE_DURING_RECEIPT)) {
251  if (!isModEnabled("multicurrency") && empty($conf->dynamicprices->enabled)) {
252  $dto = GETPOST("dto_".$reg[1].'_'.$reg[2], 'int');
253  if (!empty($dto)) {
254  $unit_price = price2num(GETPOST("pu_".$reg[1]) * (100 - $dto) / 100, 'MU');
255  }
256  $saveprice = "saveprice_".$reg[1].'_'.$reg[2];
257  }
258  }
259 
260  // We ask to move a qty
261  if (GETPOST($qty) != 0) {
262  if (!(GETPOST($ent, 'int') > 0)) {
263  dol_syslog('No dispatch for line '.$key.' as no warehouse was chosen.');
264  $text = $langs->transnoentities('Warehouse').', '.$langs->transnoentities('Line').' '.($numline);
265  setEventMessages($langs->trans('ErrorFieldRequired', $text), null, 'errors');
266  $error++;
267  }
268 
269  if (!$error) {
270  $result = $object->dispatchProduct($user, GETPOST($prod, 'int'), GETPOST($qty), GETPOST($ent, 'int'), GETPOST($pu), GETPOST('comment'), '', '', '', GETPOST($fk_commandefourndet, 'int'), $notrigger);
271  if ($result < 0) {
272  setEventMessages($object->error, $object->errors, 'errors');
273  $error++;
274  }
275 
276  if (!$error && !empty($conf->global->SUPPLIER_ORDER_CAN_UPDATE_BUYINGPRICE_DURING_RECEIPT)) {
277  if (!isModEnabled("multicurrency") && empty($conf->dynamicprices->enabled)) {
278  $dto = price2num(GETPOST("dto_".$reg[1].'_'.$reg[2], 'int'), '');
279  if (empty($dto)) {
280  $dto = 0;
281  }
282 
283  //update supplier price
284  if (GETPOSTISSET($saveprice)) {
285  // TODO Use class
286  $sql = "UPDATE ".MAIN_DB_PREFIX."product_fournisseur_price";
287  $sql .= " SET unitprice='".price2num(GETPOST($pu), 'MU')."'";
288  $sql .= ", price=".price2num(GETPOST($pu), 'MU')."*quantity";
289  $sql .= ", remise_percent = ".((float) $dto);
290  $sql .= " WHERE fk_soc=".((int) $object->socid);
291  $sql .= " AND fk_product=".((int) GETPOST($prod, 'int'));
292 
293  $resql = $db->query($sql);
294  }
295  }
296  }
297  }
298  }
299  }
300  // with batch module enabled
301  if (preg_match('/^product_batch_([0-9]+)_([0-9]+)$/i', $key, $reg)) {
302  $pos++;
303 
304  // eat-by date dispatch
305  // $numline=$reg[2] + 1; // line of product
306  $numline = $pos;
307  $prod = 'product_batch_'.$reg[1].'_'.$reg[2];
308  $qty = 'qty_'.$reg[1].'_'.$reg[2];
309  $ent = 'entrepot_'.$reg[1].'_'.$reg[2];
310  $pu = 'pu_'.$reg[1].'_'.$reg[2];
311  $fk_commandefourndet = 'fk_commandefourndet_'.$reg[1].'_'.$reg[2];
312  $lot = 'lot_number_'.$reg[1].'_'.$reg[2];
313  $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'));
314  $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'));
315 
316  $fk_commandefourndet = 'fk_commandefourndet_'.$reg[1].'_'.$reg[2];
317 
318  if (!empty($conf->global->SUPPLIER_ORDER_CAN_UPDATE_BUYINGPRICE_DURING_RECEIPT)) {
319  if (!isModEnabled("multicurrency") && empty($conf->dynamicprices->enabled)) {
320  $dto = GETPOST("dto_".$reg[1].'_'.$reg[2], 'int');
321  if (!empty($dto)) {
322  $unit_price = price2num(GETPOST("pu_".$reg[1]) * (100 - $dto) / 100, 'MU');
323  }
324  $saveprice = "saveprice_".$reg[1].'_'.$reg[2];
325  }
326  }
327 
328  // We ask to move a qty
329  if (GETPOST($qty) > 0) {
330  if (!(GETPOST($ent, 'int') > 0)) {
331  dol_syslog('No dispatch for line '.$key.' as no warehouse was chosen.');
332  $text = $langs->transnoentities('Warehouse').', '.$langs->transnoentities('Line').' '.($numline).'-'.($reg[1] + 1);
333  setEventMessages($langs->trans('ErrorFieldRequired', $text), null, 'errors');
334  $error++;
335  }
336 
337  if (!(GETPOST($lot, 'alpha') || $dDLUO || $dDLC)) {
338  dol_syslog('No dispatch for line '.$key.' as serial/eat-by/sellby date are not set');
339  $text = $langs->transnoentities('atleast1batchfield').', '.$langs->transnoentities('Line').' '.($numline).'-'.($reg[1] + 1);
340  setEventMessages($langs->trans('ErrorFieldRequired', $text), null, 'errors');
341  $error++;
342  }
343 
344  if (!$error) {
345  $result = $object->dispatchProduct($user, GETPOST($prod, 'int'), GETPOST($qty), GETPOST($ent, 'int'), GETPOST($pu), GETPOST('comment'), $dDLUO, $dDLC, GETPOST($lot, 'alpha'), GETPOST($fk_commandefourndet, 'int'), $notrigger);
346  if ($result < 0) {
347  setEventMessages($object->error, $object->errors, 'errors');
348  $error++;
349  }
350 
351  if (!$error && !empty($conf->global->SUPPLIER_ORDER_CAN_UPDATE_BUYINGPRICE_DURING_RECEIPT)) {
352  if (!isModEnabled("multicurrency") && empty($conf->dynamicprices->enabled)) {
353  $dto = GETPOST("dto_".$reg[1].'_'.$reg[2], 'int');
354  //update supplier price
355  if (GETPOSTISSET($saveprice)) {
356  // TODO Use class
357  $sql = "UPDATE ".MAIN_DB_PREFIX."product_fournisseur_price";
358  $sql .= " SET unitprice = ".price2num(GETPOST($pu), 'MU', 2);
359  $sql .= ", price = ".price2num(GETPOST($pu), 'MU', 2)." * quantity";
360  $sql .= ", remise_percent = ".price2num((empty($dto) ? 0 : $dto), 3, 2)."'";
361  $sql .= " WHERE fk_soc = ".((int) $object->socid);
362  $sql .= " AND fk_product=".((int) GETPOST($prod, 'int'));
363 
364  $resql = $db->query($sql);
365  }
366  }
367  }
368  }
369  }
370  }
371  }
372 
373  if (!$error) {
374  $result = $object->calcAndSetStatusDispatch($user, GETPOST('closeopenorder') ? 1 : 0, GETPOST('comment'));
375  if ($result < 0) {
376  setEventMessages($object->error, $object->errors, 'errors');
377  $error++;
378  }
379  }
380 
381  if ($result >= 0 && !$error) {
382  $db->commit();
383 
384  setEventMessages($langs->trans("ReceptionsRecorded"), null, 'mesgs');
385 
386  header("Location: dispatch.php?id=".$id);
387  exit();
388  } else {
389  $db->rollback();
390  }
391 }
392 
393 // Remove a dispatched line
394 if ($action == 'confirm_deleteline' && $confirm == 'yes' && $permissiontoreceive) {
395  $db->begin();
396 
397  $supplierorderdispatch = new CommandeFournisseurDispatch($db);
398  $result = $supplierorderdispatch->fetch($lineid);
399  if ($result > 0) {
400  $qty = $supplierorderdispatch->qty;
401  $entrepot = $supplierorderdispatch->fk_entrepot;
402  $product = $supplierorderdispatch->fk_product;
403  $price = price2num(GETPOST('price', 'alpha'), 'MU');
404  $comment = $supplierorderdispatch->comment;
405  $eatby = $supplierorderdispatch->eatby;
406  $sellby = $supplierorderdispatch->sellby;
407  $batch = $supplierorderdispatch->batch;
408 
409  $result = $supplierorderdispatch->delete($user);
410  }
411  if ($result < 0) {
412  $errors = $object->errors;
413  $error++;
414  } else {
415  // If module stock is enabled and the stock increase is done on purchase order dispatching
416  if ($entrepot > 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER) && empty($supplierorderdispatch->fk_reception)) {
417  $mouv = new MouvementStock($db);
418  if ($product > 0) {
419  $mouv->origin = &$object;
420  $mouv->setOrigin($object->element, $object->id);
421  $result = $mouv->livraison($user, $product, $entrepot, $qty, $price, $comment, '', $eatby, $sellby, $batch);
422  if ($result < 0) {
423  $errors = $mouv->errors;
424  $error++;
425  }
426  }
427  }
428  }
429  if ($error > 0) {
430  $db->rollback();
431  setEventMessages($error, $errors, 'errors');
432  } else {
433  $db->commit();
434  }
435 }
436 
437 // Update a dispatched line
438 if ($action == 'updateline' && $permissiontoreceive) {
439  $db->begin();
440  $error = 0;
441 
442  $supplierorderdispatch = new CommandeFournisseurDispatch($db);
443  $result = $supplierorderdispatch->fetch($lineid);
444  if ($result > 0) {
445  $qty = $supplierorderdispatch->qty;
446  $entrepot = $supplierorderdispatch->fk_entrepot;
447  $product = $supplierorderdispatch->fk_product;
448  $price = price2num(GETPOST('price'), '', 2);
449  $comment = $supplierorderdispatch->comment;
450  $eatby = $supplierorderdispatch->eatby;
451  $sellby = $supplierorderdispatch->sellby;
452  $batch = $supplierorderdispatch->batch;
453 
454  $supplierorderdispatch->qty = price2num(GETPOST('qty', 'alpha'), 'MS', 2);
455  $supplierorderdispatch->fk_entrepot = GETPOST('fk_entrepot');
456  $result = $supplierorderdispatch->update($user);
457  }
458  if ($result < 0) {
459  $error++;
460  $errors = $supplierorderdispatch->errors;
461  } else {
462  // If module stock is enabled and the stock increase is done on purchase order dispatching
463  if ($entrepot > 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) {
464  $mouv = new MouvementStock($db);
465  if ($product > 0) {
466  $mouv->origin = &$object;
467  $mouv->setOrigin($object->element, $object->id);
468  $result = $mouv->livraison($user, $product, $entrepot, $qty, $price, $comment, '', $eatby, $sellby, $batch);
469  if ($result < 0) {
470  $errors = $mouv->errors;
471  $error++;
472  } else {
473  $mouv->origin = &$object;
474  $result = $mouv->reception($user, $product, $supplierorderdispatch->fk_entrepot, $supplierorderdispatch->qty, $price, $comment, $eatby, $sellby, $batch);
475  if ($result < 0) {
476  $errors = $mouv->errors;
477  $error++;
478  }
479  }
480  }
481  }
482  }
483  if ($error > 0) {
484  $db->rollback();
485  setEventMessages($error, $errors, 'errors');
486  } else {
487  $db->commit();
488  }
489 }
490 
491 
492 /*
493  * View
494  */
495 
496 $now = dol_now();
497 
498 $form = new Form($db);
499 $formproduct = new FormProduct($db);
500 $warehouse_static = new Entrepot($db);
501 $supplierorderdispatch = new CommandeFournisseurDispatch($db);
502 
503 $title = $object->ref." - ".$langs->trans('OrderDispatch');
504 $help_url = 'EN:Module_Suppliers_Orders|FR:CommandeFournisseur|ES:Módulo_Pedidos_a_proveedores';
505 $morejs = array('/fourn/js/lib_dispatch.js.php');
506 
507 llxHeader('', $title, $help_url, '', 0, 0, $morejs);
508 
509 if ($id > 0 || !empty($ref)) {
510  $soc = new Societe($db);
511  $soc->fetch($object->socid);
512 
513  $author = new User($db);
514  $author->fetch($object->user_author_id);
515 
516  $head = ordersupplier_prepare_head($object);
517 
518  $title = $langs->trans("SupplierOrder");
519  print dol_get_fiche_head($head, 'dispatch', $title, -1, 'order');
520 
521  $formconfirm = '';
522 
523  // Confirmation to delete line
524  if ($action == 'ask_deleteline') {
525  $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_deleteline', '', 0, 1);
526  }
527 
528  // Call Hook formConfirm
529  $parameters = array('lineid' => $lineid);
530  // Note that $action and $object may be modified by hook
531  $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action);
532  if (empty($reshook)) {
533  $formconfirm .= $hookmanager->resPrint;
534  } elseif ($reshook > 0) {
535  $formconfirm = $hookmanager->resPrint;
536  }
537 
538  // Print form confirm
539  print $formconfirm;
540 
541  // Supplier order card
542 
543  $linkback = '<a href="'.DOL_URL_ROOT.'/fourn/commande/list.php'.(!empty($socid) ? '?socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
544 
545  $morehtmlref = '<div class="refidno">';
546  // Ref supplier
547  $morehtmlref .= $form->editfieldkey("RefSupplier", 'ref_supplier', $object->ref_supplier, $object, 0, 'string', '', 0, 1);
548  $morehtmlref .= $form->editfieldval("RefSupplier", 'ref_supplier', $object->ref_supplier, $object, 0, 'string', '', null, null, '', 1);
549  // Thirdparty
550  $morehtmlref .= '<br>'.$object->thirdparty->getNomUrl(1);
551  // Project
552  if (isModEnabled('project')) {
553  $langs->load("projects");
554  $morehtmlref .= '<br>';
555  if (0) {
556  $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
557  if ($action != 'classify' && $caneditproject) {
558  $morehtmlref .= '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=classify&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
559  }
560  $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');
561  } else {
562  if (!empty($object->fk_project)) {
563  $proj = new Project($db);
564  $proj->fetch($object->fk_project);
565  $morehtmlref .= $proj->getNomUrl(1);
566  if ($proj->title) {
567  $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
568  }
569  }
570  }
571  }
572  $morehtmlref .= '</div>';
573 
574 
575  dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
576 
577 
578  print '<div class="fichecenter">';
579  print '<div class="underbanner clearboth"></div>';
580 
581  print '<table class="border tableforfield" width="100%">';
582 
583  // Date
584  if ($object->methode_commande_id > 0) {
585  print '<tr><td class="titlefield">'.$langs->trans("Date").'</td><td>';
586  if ($object->date_commande) {
587  print dol_print_date($object->date_commande, "dayhour")."\n";
588  }
589  print "</td></tr>";
590 
591  if ($object->methode_commande) {
592  print '<tr><td>'.$langs->trans("Method").'</td><td>'.$object->getInputMethod().'</td></tr>';
593  }
594  }
595 
596  // Author
597  print '<tr><td class="titlefield">'.$langs->trans("AuthorRequest").'</td>';
598  print '<td>'.$author->getNomUrl(1, '', 0, 0, 0).'</td>';
599  print '</tr>';
600 
601  $parameters = array();
602  $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
603 
604  print "</table>";
605 
606  print '</div>';
607 
608  // if ($mesg) print $mesg;
609  print '<br>';
610 
611  /*$disabled = 1;
612  if (!empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) {
613  $disabled = 0;
614  }*/
615  $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.
616 
617  // Line of orders
618  if ($object->statut <= CommandeFournisseur::STATUS_ACCEPTED || $object->statut >= CommandeFournisseur::STATUS_CANCELED) {
619  print '<br><span class="opacitymedium">'.$langs->trans("OrderStatusNotReadyToDispatch").'</span>';
620  }
621 
622 
623  print '<br>';
624 
625 
626  if ($object->statut == CommandeFournisseur::STATUS_ORDERSENT
628  || $object->statut == CommandeFournisseur::STATUS_RECEIVED_COMPLETELY) {
629  require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
630  $formproduct = new FormProduct($db);
631  $formproduct->loadWarehouses();
632  $entrepot = new Entrepot($db);
633  $listwarehouses = $entrepot->list_array(1);
634 
635 
636  if (empty($conf->reception->enabled)) {
637  print '<form method="POST" action="dispatch.php?id='.$object->id.'">';
638  } else {
639  print '<form method="post" action="'.dol_buildpath('/reception/card.php', 1).'?originid='.$object->id.'&origin=supplierorder">';
640  }
641 
642  print '<input type="hidden" name="token" value="'.newToken().'">';
643  if (empty($conf->reception->enabled)) {
644  print '<input type="hidden" name="action" value="dispatch">';
645  } else {
646  print '<input type="hidden" name="action" value="create">';
647  }
648 
649  print '<div class="div-table-responsive-no-min">';
650  print '<table class="noborder centpercent">';
651 
652  // Set $products_dispatched with qty dispatched for each product id
653  $products_dispatched = array();
654  $sql = "SELECT l.rowid, cfd.fk_product, sum(cfd.qty) as qty";
655  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as cfd";
656  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as l on l.rowid = cfd.fk_commandefourndet";
657  $sql .= " WHERE cfd.fk_commande = ".((int) $object->id);
658  $sql .= " GROUP BY l.rowid, cfd.fk_product";
659 
660  $resql = $db->query($sql);
661  if ($resql) {
662  $num = $db->num_rows($resql);
663  $i = 0;
664 
665  if ($num) {
666  while ($i < $num) {
667  $objd = $db->fetch_object($resql);
668  $products_dispatched[$objd->rowid] = price2num($objd->qty, 'MS');
669  $i++;
670  }
671  }
672  $db->free($resql);
673  }
674 
675  //$sql = "SELECT l.rowid, l.fk_product, l.subprice, l.remise_percent, l.ref AS sref, SUM(l.qty) as qty,";
676  $sql = "SELECT l.rowid, l.fk_product, l.subprice, l.remise_percent, l.ref AS sref, l.qty as qty,";
677  $sql .= " p.ref, p.label, p.tobatch, p.fk_default_warehouse";
678 
679  // Enable hooks to alter the SQL query (SELECT)
680  $parameters = array();
681  $reshook = $hookmanager->executeHooks(
682  'printFieldListSelect',
683  $parameters,
684  $object,
685  $action
686  );
687  if ($reshook < 0) {
688  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
689  }
690  $sql .= $hookmanager->resPrint;
691 
692  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as l";
693  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON l.fk_product=p.rowid";
694  $sql .= " WHERE l.fk_commande = ".((int) $object->id);
695  if (empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
696  $sql .= " AND l.product_type = 0";
697  }
698 
699  // Enable hooks to alter the SQL query (WHERE)
700  $parameters = array();
701  $reshook = $hookmanager->executeHooks(
702  'printFieldListWhere',
703  $parameters,
704  $object,
705  $action
706  );
707  if ($reshook < 0) {
708  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
709  }
710  $sql .= $hookmanager->resPrint;
711 
712  //$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
713  $sql .= " ORDER BY l.rang, p.ref, p.label";
714 
715  $resql = $db->query($sql);
716  if ($resql) {
717  $num = $db->num_rows($resql);
718  $i = 0;
719 
720  if ($num) {
721  print '<tr class="liste_titre">';
722 
723  print '<td>'.$langs->trans("Description").'</td>';
724  if (isModEnabled('productbatch')) {
725  print '<td class="dispatch_batch_number_title">'.$langs->trans("batch_number").'</td>';
726  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
727  print '<td class="dispatch_dlc_title">'.$langs->trans("SellByDate").'</td>';
728  }
729  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
730  print '<td class="dispatch_dluo_title">'.$langs->trans("EatByDate").'</td>';
731  }
732  } else {
733  print '<td></td>';
734  print '<td></td>';
735  print '<td></td>';
736  }
737  print '<td class="right">'.$langs->trans("SupplierRef").'</td>';
738  print '<td class="right">'.$langs->trans("QtyOrdered").'</td>';
739  print '<td class="right">'.$langs->trans("QtyDispatchedShort").'</td>';
740  print ' <td class="right">'.$langs->trans("QtyToDispatchShort");
741  print '<br><a href="#" id="autoreset">'.img_picto($langs->trans("Reset"), 'eraser', 'class="pictofixedwidth opacitymedium"').$langs->trans("Reset").'</a></td>';
742  print '<td width="32"></td>';
743 
744  if (!empty($conf->global->SUPPLIER_ORDER_CAN_UPDATE_BUYINGPRICE_DURING_RECEIPT)) {
745  if (!isModEnabled("multicurrency") && empty($conf->dynamicprices->enabled)) {
746  print '<td class="right">'.$langs->trans("Price").'</td>';
747  print '<td class="right">'.$langs->trans("ReductionShort").' (%)</td>';
748  print '<td class="right">'.$langs->trans("UpdatePrice").'</td>';
749  }
750  }
751 
752  print '<td align="right">'.$langs->trans("Warehouse");
753 
754  // Select warehouse to force it everywhere
755  if (count($listwarehouses) > 1) {
756  print '<br>'.$form->selectarray('fk_default_warehouse', $listwarehouses, $fk_default_warehouse, $langs->trans("ForceTo"), 0, 0, '', 0, 0, $disabled, '', 'minwidth100 maxwidth300', 1);
757  } elseif (count($listwarehouses) == 1) {
758  print '<br>'.$form->selectarray('fk_default_warehouse', $listwarehouses, $fk_default_warehouse, 0, 0, 0, '', 0, 0, $disabled, '', 'minwidth100 maxwidth300', 1);
759  }
760 
761  print '</td>';
762 
763  // Enable hooks to append additional columns
764  $parameters = array();
765  $reshook = $hookmanager->executeHooks(
766  'printFieldListTitle',
767  $parameters,
768  $object,
769  $action
770  );
771  if ($reshook < 0) {
772  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
773  }
774  print $hookmanager->resPrint;
775 
776  print "</tr>\n";
777  }
778 
779  $nbfreeproduct = 0; // Nb of lins of free products/services
780  $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)
781  // or nb of line that remain to dispatch if SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED is on.
782 
783  $conf->cache['product'] = array();
784 
785  // Loop on each source order line (may be more or less than current number of lines in llx_commande_fournisseurdet)
786  while ($i < $num) {
787  $objp = $db->fetch_object($resql);
788 
789  // On n'affiche pas les produits libres
790  if (!$objp->fk_product > 0) {
791  $nbfreeproduct++;
792  } else {
793  $alreadydispatched = isset($products_dispatched[$objp->rowid])?$products_dispatched[$objp->rowid]:0;
794  $remaintodispatch = price2num($objp->qty - ((float) $alreadydispatched), 5); // Calculation of dispatched
795  if ($remaintodispatch < 0 && empty($conf->global->SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN)) {
796  $remaintodispatch = 0;
797  }
798 
799  if ($remaintodispatch || empty($conf->global->SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED)) {
800  $nbproduct++;
801 
802  // To show detail cref and description value, we must make calculation by cref
803  // print ($objp->cref?' ('.$objp->cref.')':'');
804  // if ($objp->description) print '<br>'.nl2br($objp->description);
805  $suffix = '_0_'.$i;
806 
807  print "\n";
808  print '<!-- Line to dispatch '.$suffix.' -->'."\n";
809  // hidden fields for js function
810  print '<input id="qty_ordered'.$suffix.'" type="hidden" value="'.$objp->qty.'">';
811  print '<input id="qty_dispatched'.$suffix.'" type="hidden" value="'.(float) $alreadydispatched.'">';
812  print '<tr class="oddeven">';
813 
814  if (empty($conf->cache['product'][$objp->fk_product])) {
815  $tmpproduct = new Product($db);
816  $tmpproduct->fetch($objp->fk_product);
817  $conf->cache['product'][$objp->fk_product] = $tmpproduct;
818  } else {
819  $tmpproduct = $conf->cache['product'][$objp->fk_product];
820  }
821 
822  $linktoprod = $tmpproduct->getNomUrl(1);
823  $linktoprod .= ' - '.$objp->label."\n";
824 
825  if (isModEnabled('productbatch')) {
826  if ($objp->tobatch) {
827  // Product
828  print '<td>';
829  print $linktoprod;
830  print "</td>";
831  print '<td class="dispatch_batch_number"></td>';
832  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
833  print '<td class="dispatch_dlc"></td>';
834  }
835  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
836  print '<td class="dispatch_dluo"></td>';
837  }
838  } else {
839  // Product
840  print '<td>';
841  print $linktoprod;
842  print "</td>";
843  print '<td class="dispatch_batch_number">';
844  print '<span class="opacitymedium small">'.$langs->trans("ProductDoesNotUseBatchSerial").'</small>';
845  print '</td>';
846  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
847  print '<td class="dispatch_dlc"></td>';
848  }
849  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
850  print '<td class="dispatch_dluo"></td>';
851  }
852  }
853  } else {
854  print '<td colspan="4">';
855  print $linktoprod;
856  print "</td>";
857  }
858 
859  // Define unit price for PMP calculation
860  $up_ht_disc = $objp->subprice;
861  if (!empty($objp->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
862  $up_ht_disc = price2num($up_ht_disc * (100 - $objp->remise_percent) / 100, 'MU');
863  }
864 
865  // Supplier ref
866  print '<td class="right">'.$objp->sref.'</td>';
867 
868  // Qty ordered
869  print '<td class="right">'.$objp->qty.'</td>';
870 
871  // Already dispatched
872  print '<td class="right">'.$alreadydispatched.'</td>';
873 
874  if (isModEnabled('productbatch') && $objp->tobatch > 0) {
875  $type = 'batch';
876  print '<td class="right">';
877  print '</td>'; // Qty to dispatch
878  print '<td>';
879  //print img_picto($langs->trans('AddDispatchBatchLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine(' . $i . ',\'' . $type . '\')"');
880  print '</td>'; // Dispatch column
881  print '<td></td>'; // Warehouse column
882 
883  // Enable hooks to append additional columns
884  $parameters = array(
885  // allows hook to distinguish between the rows with information and the rows with dispatch form input
886  'is_information_row' => true,
887  'i' => $i,
888  'suffix' => $suffix,
889  'objp' => $objp,
890  );
891  $reshook = $hookmanager->executeHooks(
892  'printFieldListValue',
893  $parameters,
894  $object,
895  $action
896  );
897  if ($reshook < 0) {
898  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
899  }
900  print $hookmanager->resPrint;
901 
902  print '</tr>';
903 
904  print '<tr class="oddeven" name="'.$type.$suffix.'">';
905  print '<td>';
906  print '<input name="fk_commandefourndet'.$suffix.'" type="hidden" value="'.$objp->rowid.'">';
907  print '<input name="product_batch'.$suffix.'" type="hidden" value="'.$objp->fk_product.'">';
908 
909  print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
910  if (!empty($conf->global->SUPPLIER_ORDER_EDIT_BUYINGPRICE_DURING_RECEIPT)) { // Not tested !
911  print $langs->trans("BuyingPrice").': <input class="maxwidth75" name="pu'.$suffix.'" type="text" value="'.price2num($up_ht_disc, 'MU').'">';
912  } else {
913  print '<input class="maxwidth75" name="pu'.$suffix.'" type="hidden" value="'.price2num($up_ht_disc, 'MU').'">';
914  }
915 
916  print '</td>';
917 
918  print '<td>';
919  print '<input type="text" class="inputlotnumber quatrevingtquinzepercent" id="lot_number'.$suffix.'" name="lot_number'.$suffix.'" value="'.GETPOST('lot_number'.$suffix).'">';
920  print '</td>';
921  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
922  print '<td class="nowraponall">';
923  $dlcdatesuffix = dol_mktime(0, 0, 0, GETPOST('dlc'.$suffix.'month'), GETPOST('dlc'.$suffix.'day'), GETPOST('dlc'.$suffix.'year'));
924  print $form->selectDate($dlcdatesuffix, 'dlc'.$suffix, '', '', 1, '');
925  print '</td>';
926  }
927  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
928  print '<td class="nowraponall">';
929  $dluodatesuffix = dol_mktime(0, 0, 0, GETPOST('dluo'.$suffix.'month'), GETPOST('dluo'.$suffix.'day'), GETPOST('dluo'.$suffix.'year'));
930  print $form->selectDate($dluodatesuffix, 'dluo'.$suffix, '', '', 1, '');
931  print '</td>';
932  }
933  print '<td colspan="3">&nbsp;</td>'; // Supplier ref + Qty ordered + qty already dispatched
934  } else {
935  $type = 'dispatch';
936  $colspan = 7;
937  $colspan = (!empty($conf->global->PRODUCT_DISABLE_SELLBY)) ? --$colspan : $colspan;
938  $colspan = (!empty($conf->global->PRODUCT_DISABLE_EATBY)) ? --$colspan : $colspan;
939  print '<td class="right">';
940  print '</td>'; // Qty to dispatch
941  print '<td>';
942  //print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine(' . $i . ',\'' . $type . '\')"');
943  print '</td>'; // Dispatch column
944  print '<td></td>'; // Warehouse column
945 
946  // Enable hooks to append additional columns
947  $parameters = array(
948  // allows hook to distinguish between the rows with information and the rows with dispatch form input
949  'is_information_row' => true,
950  'i' => $i,
951  'suffix' => $suffix,
952  'objp' => $objp,
953  );
954  $reshook = $hookmanager->executeHooks(
955  'printFieldListValue',
956  $parameters,
957  $object,
958  $action
959  );
960  if ($reshook < 0) {
961  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
962  }
963  print $hookmanager->resPrint;
964 
965  print '</tr>';
966 
967  print '<tr class="oddeven" name="'.$type.$suffix.'">';
968  print '<td colspan="'.$colspan.'">';
969  print '<input name="fk_commandefourndet'.$suffix.'" type="hidden" value="'.$objp->rowid.'">';
970  print '<input name="product'.$suffix.'" type="hidden" value="'.$objp->fk_product.'">';
971 
972  print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
973  if (!empty($conf->global->SUPPLIER_ORDER_EDIT_BUYINGPRICE_DURING_RECEIPT)) { // Not tested !
974  print $langs->trans("BuyingPrice").': <input class="maxwidth75" name="pu'.$suffix.'" type="text" value="'.price2num($up_ht_disc, 'MU').'">';
975  } else {
976  print '<input class="maxwidth75" name="pu'.$suffix.'" type="hidden" value="'.price2num($up_ht_disc, 'MU').'">';
977  }
978 
979  print '</td>';
980  }
981 
982  // Qty to dispatch
983  print '<td class="right">';
984  print '<a href="#" id="reset'.$suffix.'" class="resetline">'.img_picto($langs->trans("Reset"), 'eraser', 'class="pictofixedwidth opacitymedium"').'</a>';
985  print '<input id="qty'.$suffix.'" name="qty'.$suffix.'" type="text" class="width50 right qtydispatchinput" value="'.(GETPOSTISSET('qty'.$suffix) ? GETPOST('qty'.$suffix, 'int') : (empty($conf->global->SUPPLIER_ORDER_DISPATCH_FORCE_QTY_INPUT_TO_ZERO) ? $remaintodispatch : 0)).'">';
986  print '</td>';
987 
988  print '<td>';
989  if (isModEnabled('productbatch') && $objp->tobatch > 0) {
990  $type = 'batch';
991  print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine('.$i.', \''.$type.'\')"');
992  } else {
993  $type = 'dispatch';
994  print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine('.$i.', \''.$type.'\')"');
995  }
996  print '</td>';
997 
998  if (!empty($conf->global->SUPPLIER_ORDER_CAN_UPDATE_BUYINGPRICE_DURING_RECEIPT)) {
999  if (!isModEnabled("multicurrency") && empty($conf->dynamicprices->enabled)) {
1000  // Price
1001  print '<td class="right">';
1002  print '<input id="pu'.$suffix.'" name="pu'.$suffix.'" type="text" size="8" value="'.price((GETPOST('pu'.$suffix) != '' ? price2num(GETPOST('pu'.$suffix)) : $up_ht_disc)).'">';
1003  print '</td>';
1004 
1005  // Discount
1006  print '<td class="right">';
1007  print '<input id="dto'.$suffix.'" name="dto'.$suffix.'" type="text" size="8" value="'.(GETPOST('dto'.$suffix) != '' ? GETPOST('dto'.$suffix) : '').'">';
1008  print '</td>';
1009 
1010  // Save price
1011  print '<td class="center">';
1012  print '<input class="flat checkformerge" type="checkbox" name="saveprice'.$suffix.'" value="'.(GETPOST('saveprice'.$suffix) != '' ? GETPOST('saveprice'.$suffix) : '').'">';
1013  print '</td>';
1014  }
1015  }
1016 
1017  // Warehouse
1018  print '<td class="right">';
1019  if (count($listwarehouses) > 1) {
1020  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);
1021  } elseif (count($listwarehouses) == 1) {
1022  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);
1023  } else {
1024  $langs->load("errors");
1025  print $langs->trans("ErrorNoWarehouseDefined");
1026  }
1027  print "</td>\n";
1028 
1029  // Enable hooks to append additional columns
1030  $parameters = array(
1031  'is_information_row' => false, // this is a dispatch form row
1032  'i' => $i,
1033  'suffix' => $suffix,
1034  'objp' => $objp,
1035  );
1036  $reshook = $hookmanager->executeHooks(
1037  'printFieldListValue',
1038  $parameters,
1039  $object,
1040  $action
1041  );
1042  if ($reshook < 0) {
1043  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1044  }
1045  print $hookmanager->resPrint;
1046 
1047  print "</tr>\n";
1048  }
1049  }
1050  $i++;
1051  }
1052  $db->free($resql);
1053  } else {
1054  dol_print_error($db);
1055  }
1056 
1057  print "</table>\n";
1058  print '</div>';
1059 
1060  if ($nbproduct) {
1061  $checkboxlabel = $langs->trans("CloseReceivedSupplierOrdersAutomatically", $langs->transnoentitiesnoconv('StatusOrderReceivedAll'));
1062 
1063  print '<div class="center">';
1064  $parameters = array();
1065  $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
1066  // modified by hook
1067  if (empty($reshook)) {
1068  if (empty($conf->reception->enabled)) {
1069  print $langs->trans("Comment").' : ';
1070  print '<input type="text" class="minwidth400" maxlength="128" name="comment" value="';
1071  print GETPOSTISSET("comment") ? GETPOST("comment") : $langs->trans("DispatchSupplierOrder", $object->ref);
1072  // print ' / '.$object->ref_supplier; // Not yet available
1073  print '" class="flat"><br>';
1074 
1075  print '<input type="checkbox" checked="checked" name="closeopenorder"> '.$checkboxlabel;
1076  }
1077 
1078  $dispatchBt = empty($conf->reception->enabled) ? $langs->trans("Receive") : $langs->trans("CreateReception");
1079 
1080  print '<br>';
1081  print '<input type="hidden" name="backtopageforcancel" value="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'">';
1082  print '<input type="submit" class="button" name="dispatch" value="'.dol_escape_htmltag($dispatchBt).'"';
1083  $disabled = 0;
1084  if (!$permissiontoreceive) {
1085  $disabled = 1;
1086  }
1087  if (count($listwarehouses) <= 0) {
1088  $disabled = 1;
1089  }
1090  if ($disabled) {
1091  print ' disabled';
1092  }
1093 
1094  print '>';
1095  }
1096  print '</div>';
1097  }
1098 
1099  // Message if nothing to dispatch
1100  if (!$nbproduct) {
1101  print "<br>\n";
1102  if (empty($conf->global->SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED)) {
1103  print '<div class="opacitymedium">'.$langs->trans("NoPredefinedProductToDispatch").'</div>'; // No predefined line at all
1104  } else {
1105  print '<div class="opacitymedium">'.$langs->trans("NoMorePredefinedProductToDispatch").'</div>'; // No predefined line that remain to be dispatched.
1106  }
1107  }
1108 
1109  print '</form>';
1110  }
1111 
1112  print dol_get_fiche_end();
1113 
1114  // traitement entrepot par défaut
1115  print '<script type="text/javascript">
1116  $(document).ready(function () {
1117  $("select[name=fk_default_warehouse]").change(function() {
1118  var fk_default_warehouse = $("option:selected", this).val();
1119  $("select[name^=entrepot_]").val(fk_default_warehouse).change();
1120  });
1121 
1122  $("#autoreset").click(function() {
1123  $(".qtydispatchinput").each(function(){
1124  id = $(this).attr("id");
1125  idtab = id.split("_");
1126  if(idtab[1] == 0){
1127  console.log(idtab);
1128  $(this).val("");
1129  $("#qty_dispatched_0_"+idtab[2]).val("0");
1130  } else {
1131  obj = $(this).parent().parent();
1132  nameobj = obj.attr("name");
1133  nametab = nameobj.split("_");
1134  obj.remove();
1135  $("tr[name^=\'"+nametab[0]+"_\'][name$=\'_"+nametab[2]+"\']:last .splitbutton").show();
1136  }
1137  });
1138  });
1139 
1140  $(".resetline").click(function(){
1141  id = $(this).attr("id");
1142  id = id.split("reset_");
1143  console.log("Reset trigger for id = qty_"+id[1]);
1144  $("#qty_"+id[1]).val("");
1145  });
1146  });
1147  </script>';
1148 
1149  // List of lines already dispatched
1150  $sql = "SELECT p.rowid as pid, p.ref, p.label,";
1151  $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
1152  $sql .= " cfd.rowid as dispatchlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.datec";
1153  $sql .= " ,cd.rowid, cd.subprice";
1154  if ($conf->reception->enabled) {
1155  $sql .= " ,cfd.fk_reception, r.date_delivery";
1156  }
1157  $sql .= " FROM ".MAIN_DB_PREFIX."product as p,";
1158  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as cfd";
1159  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as cd ON cd.rowid = cfd.fk_commandefourndet";
1160  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e ON cfd.fk_entrepot = e.rowid";
1161  if ($conf->reception->enabled) {
1162  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."reception as r ON cfd.fk_reception = r.rowid";
1163  }
1164  $sql .= " WHERE cfd.fk_commande = ".((int) $object->id);
1165  $sql .= " AND cfd.fk_product = p.rowid";
1166  $sql .= " ORDER BY cfd.rowid ASC";
1167 
1168  $resql = $db->query($sql);
1169  if ($resql) {
1170  $num = $db->num_rows($resql);
1171  $i = 0;
1172 
1173  if ($num > 0) {
1174  print "<br>\n";
1175 
1176  print load_fiche_titre($langs->trans("ReceivingForSameOrder"));
1177 
1178  print '<div class="div-table-responsive">';
1179  print '<table id="dispatch_received_products" class="noborder centpercent">';
1180 
1181  print '<tr class="liste_titre">';
1182  // Reception ref
1183  if ($conf->reception->enabled) {
1184  print '<td>'.$langs->trans("Reception").'</td>';
1185  }
1186  // Product
1187  print '<td>'.$langs->trans("Product").'</td>';
1188  print '<td class="center">'.$langs->trans("DateCreation").'</td>';
1189  print '<td class="center">'.$langs->trans("DateDeliveryPlanned").'</td>';
1190  if (isModEnabled('productbatch')) {
1191  print '<td class="dispatch_batch_number_title">'.$langs->trans("batch_number").'</td>';
1192  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
1193  print '<td class="dispatch_dlc_title">'.$langs->trans("SellByDate").'</td>';
1194  }
1195  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
1196  print '<td class="dispatch_dluo_title">'.$langs->trans("EatByDate").'</td>';
1197  }
1198  }
1199  print '<td class="right">'.$langs->trans("QtyDispatched").'</td>';
1200  print '<td>'.$langs->trans("Warehouse").'</td>';
1201  print '<td>'.$langs->trans("Comment").'</td>';
1202 
1203  // Status
1204  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS) && empty($reception->rowid)) {
1205  print '<td class="center" colspan="2">'.$langs->trans("Status").'</td>';
1206  } elseif (isModEnabled("reception")) {
1207  print '<td class="center"></td>';
1208  }
1209 
1210  print '<td class="center" colspan="2"></td>';
1211 
1212  print "</tr>\n";
1213 
1214 
1215  while ($i < $num) {
1216  $objp = $db->fetch_object($resql);
1217 
1218  if ($action == 'editline' && $lineid == $objp->dispatchlineid) {
1219  print '<form name="editdispatchedlines" id="editdispatchedlines" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'#line_'.GETPOST('lineid', 'int').'" method="POST">
1220  <input type="hidden" name="token" value="'.newToken().'">
1221  <input type="hidden" name="action" value="updateline">
1222  <input type="hidden" name="mode" value="">
1223  <input type="hidden" name="lineid" value="'.$objp->dispatchlineid.'">';
1224  }
1225 
1226  print '<tr class="oddeven" id="line_'.$objp->dispatchlineid.'" >';
1227 
1228  // Reception ref
1229  if (isModEnabled("reception")) {
1230  print '<td class="nowraponall">';
1231  if (!empty($objp->fk_reception)) {
1232  $reception = new Reception($db);
1233  $reception->fetch($objp->fk_reception);
1234  print $reception->getNomUrl(1);
1235  }
1236 
1237  print "</td>";
1238  }
1239 
1240  // Product
1241  print '<td class="tdoverflowmax150">';
1242  if (empty($conf->cache['product'][$objp->fk_product])) {
1243  $tmpproduct = new Product($db);
1244  $tmpproduct->fetch($objp->fk_product);
1245  $conf->cache['product'][$objp->fk_product] = $tmpproduct;
1246  } else {
1247  $tmpproduct = $conf->cache['product'][$objp->fk_product];
1248  }
1249  print $tmpproduct->getNomUrl(1);
1250  print ' - '.$objp->label;
1251  print "</td>\n";
1252 
1253  // Date creation
1254  print '<td class="center">'.dol_print_date($db->jdate($objp->datec), 'day').'</td>';
1255 
1256  // Date delivery
1257  print '<td class="center">'.dol_print_date($db->jdate($objp->date_delivery), 'day').'</td>';
1258 
1259  // Batch / Eat by / Sell by
1260  if (isModEnabled('productbatch')) {
1261  if ($objp->batch) {
1262  include_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
1263  $lot = new Productlot($db);
1264  $lot->fetch(0, $objp->pid, $objp->batch);
1265  print '<td class="dispatch_batch_number">'.$lot->getNomUrl(1).'</td>';
1266  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
1267  print '<td class="dispatch_dlc">'.dol_print_date($lot->sellby, 'day').'</td>';
1268  }
1269  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
1270  print '<td class="dispatch_dluo">'.dol_print_date($lot->eatby, 'day').'</td>';
1271  }
1272  } else {
1273  print '<td class="dispatch_batch_number"></td>';
1274  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
1275  print '<td class="dispatch_dlc"></td>';
1276  }
1277  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
1278  print '<td class="dispatch_dluo"></td>';
1279  }
1280  }
1281  }
1282 
1283  // Qty
1284  print '<td class="right">';
1285  if ($action == 'editline' && $lineid == $objp->dispatchlineid) {
1286  print '<input style="width: 50px;" type="text" min="1" name="qty" value="'.$objp->qty.'" />';
1287  } else {
1288  print $objp->qty;
1289  }
1290  print '<input type="hidden" name="price" value="'.$objp->subprice.'" />';
1291  print '</td>';
1292 
1293  // Warehouse
1294  print '<td class="tdoverflowmax150">';
1295  if ($action == 'editline' && $lineid == $objp->dispatchlineid) {
1296  if (count($listwarehouses) > 1) {
1297  print $formproduct->selectWarehouses(GETPOST("fk_entrepot") ?GETPOST("fk_entrepot") : ($objp->warehouse_id ? $objp->warehouse_id : ''), "fk_entrepot", '', 1, 0, $objp->fk_product, '', 1, 1, null, 'csswarehouse');
1298  } elseif (count($listwarehouses) == 1) {
1299  print $formproduct->selectWarehouses(GETPOST("fk_entrepot") ?GETPOST("fk_entrepot") : ($objp->warehouse_id ? $objp->warehouse_id : ''), "fk_entrepot", '', 0, 0, $objp->fk_product, '', 1, 1, null, 'csswarehouse');
1300  } else {
1301  $langs->load("errors");
1302  print $langs->trans("ErrorNoWarehouseDefined");
1303  }
1304  } else {
1305  $warehouse_static->id = $objp->warehouse_id;
1306  $warehouse_static->label = $objp->entrepot;
1307  print $warehouse_static->getNomUrl(1);
1308  }
1309  print '</td>';
1310 
1311  // Comment
1312  print '<td class="tdoverflowmax300" style="white-space: pre;">'.$objp->comment.'</td>';
1313 
1314  // Status
1315  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS) && empty($reception->rowid)) {
1316  print '<td class="right">';
1317  $supplierorderdispatch->status = (empty($objp->status) ? 0 : $objp->status);
1318  // print $supplierorderdispatch->status;
1319  print $supplierorderdispatch->getLibStatut(5);
1320  print '</td>';
1321 
1322  // Add button to check/uncheck disaptching
1323  print '<td class="center">';
1324  if (!$permissiontocontrol) {
1325  if (empty($objp->status)) {
1326  print '<a class="button buttonRefused" href="#">'.$langs->trans("Approve").'</a>';
1327  print '<a class="button buttonRefused" href="#">'.$langs->trans("Deny").'</a>';
1328  } else {
1329  print '<a class="button buttonRefused" href="#">'.$langs->trans("Disapprove").'</a>';
1330  print '<a class="button buttonRefused" href="#">'.$langs->trans("Deny").'</a>';
1331  }
1332  } else {
1333  $disabled = '';
1334  if ($object->statut == 5) {
1335  $disabled = 1;
1336  }
1337  if (empty($objp->status)) {
1338  print '<a class="button'.($disabled ? ' buttonRefused' : '').'" href="'.$_SERVER["PHP_SELF"]."?id=".$id."&action=checkdispatchline&lineid=".$objp->dispatchlineid.'">'.$langs->trans("Approve").'</a>';
1339  print '<a class="button'.($disabled ? ' buttonRefused' : '').'" href="'.$_SERVER["PHP_SELF"]."?id=".$id."&action=denydispatchline&lineid=".$objp->dispatchlineid.'">'.$langs->trans("Deny").'</a>';
1340  }
1341  if ($objp->status == 1) {
1342  print '<a class="button'.($disabled ? ' buttonRefused' : '').'" href="'.$_SERVER["PHP_SELF"]."?id=".$id."&action=uncheckdispatchline&lineid=".$objp->dispatchlineid.'">'.$langs->trans("Reinit").'</a>';
1343  print '<a class="button'.($disabled ? ' buttonRefused' : '').'" href="'.$_SERVER["PHP_SELF"]."?id=".$id."&action=denydispatchline&lineid=".$objp->dispatchlineid.'">'.$langs->trans("Deny").'</a>';
1344  }
1345  if ($objp->status == 2) {
1346  print '<a class="button'.($disabled ? ' buttonRefused' : '').'" href="'.$_SERVER["PHP_SELF"]."?id=".$id."&action=uncheckdispatchline&lineid=".$objp->dispatchlineid.'">'.$langs->trans("Reinit").'</a>';
1347  print '<a class="button'.($disabled ? ' buttonRefused' : '').'" href="'.$_SERVER["PHP_SELF"]."?id=".$id."&action=checkdispatchline&lineid=".$objp->dispatchlineid.'">'.$langs->trans("Approve").'</a>';
1348  }
1349  }
1350  print '</td>';
1351  } elseif (isModEnabled("reception")) {
1352  print '<td class="right">';
1353  if (!empty($reception->id)) {
1354  print $reception->getLibStatut(5);
1355  }
1356  print '</td>';
1357  }
1358 
1359  // Action
1360  if ($action != 'editline' || $lineid != $objp->dispatchlineid) {
1361  if (empty($reception->id) || ($reception->statut == Reception::STATUS_DRAFT)) { // only allow edit on draft reception
1362  print '<td class="linecoledit center">';
1363  print '<a class="reposition" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=editline&token='.newToken().'&lineid='.$objp->dispatchlineid.'#line_'.$objp->dispatchlineid.'">';
1364  print img_edit();
1365  print '</a>';
1366  print '</td>';
1367 
1368  print '<td class="linecoldelete center">';
1369  print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=ask_deleteline&token='.newToken().'&lineid='.$objp->dispatchlineid.'#dispatch_received_products">';
1370  print img_delete();
1371  print '</a>';
1372  print '</td>';
1373  } else {
1374  print '<td></td><td></td>';
1375  }
1376  } else {
1377  print '<td class="center valignmiddle">';
1378  print '<input type="submit" class="button button-save" id="savelinebutton" name="save" value="'.$langs->trans("Save").'" />';
1379  print '</td>';
1380  print '<td class="center valignmiddle">';
1381  print '<input type="submit" class="button button-cancel" id="cancellinebutton" name="cancel" value="'.$langs->trans("Cancel").'" />';
1382  print '</td>';
1383  }
1384 
1385 
1386  print "</tr>\n";
1387  if ($action == 'editline' && $lineid == $objp->dispatchlineid) {
1388  print '</form>';
1389  }
1390 
1391  $i++;
1392  }
1393  $db->free($resql);
1394 
1395  print "</table>\n";
1396  print '</div>';
1397  }
1398  } else {
1399  dol_print_error($db);
1400  }
1401 }
1402 
1403 // End of page
1404 llxFooter();
1405 $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 table commandefournisseurdispatch.
Class to manage predefined suppliers products.
const STATUS_RECEIVED_PARTIALLY
Received partially.
const STATUS_CANCELED
Order canceled.
const STATUS_RECEIVED_COMPLETELY
Received completely.
const STATUS_ORDERSENT
Order sent, shipment on process.
Class to manage comment.
Class to manage warehouses.
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 stock movements.
Class to manage products or services.
Class with list of lots and properties.
Class to manage projects.
Class to manage receptions.
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
ordersupplier_prepare_head(CommandeFournisseur $object)
Prepare array with list of tabs.
Definition: fourn.lib.php:139
dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='rowid', $fieldref='ref', $morehtmlref='', $moreparam='', $nodbprefix=0, $morehtmlleft='', $morehtmlstatus='', $onlybanner=0, $morehtmlright='')
Show tab footer of a card.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
load_fiche_titre($titre, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
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.