dolibarr  x.y.z
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 
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 ($_GET["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->rights->fournisseur->commande->receptionner;
90  $permissiontocontrol = ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->fournisseur->commande->receptionner)) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->fournisseur->commande_advance->check)));
91 } else {
92  $permissiontoreceive = $user->rights->reception->creer;
93  $permissiontocontrol = ((empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->reception->creer)) || (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && !empty($user->rights->reception->reception_advance->validate)));
94 }
95 
96 // $id is id of a purchase order.
97 $result = restrictedArea($user, 'fournisseur', $id, 'commande_fournisseur', 'commande');
98 
99 if (!isModEnabled('stock')) {
100  accessforbidden();
101 }
102 
103 $usercancreate = ($user->rights->fournisseur->commande->creer || $user->rights->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  header("Location: dispatch.php?id=".$id);
385  exit();
386  } else {
387  $db->rollback();
388  }
389 }
390 
391 // Remove a dispatched line
392 if ($action == 'confirm_deleteline' && $confirm == 'yes' && $permissiontoreceive) {
393  $db->begin();
394 
395  $supplierorderdispatch = new CommandeFournisseurDispatch($db);
396  $result = $supplierorderdispatch->fetch($lineid);
397  if ($result > 0) {
398  $qty = $supplierorderdispatch->qty;
399  $entrepot = $supplierorderdispatch->fk_entrepot;
400  $product = $supplierorderdispatch->fk_product;
401  $price = price2num(GETPOST('price', 'alpha'), 'MU');
402  $comment = $supplierorderdispatch->comment;
403  $eatby = $supplierorderdispatch->eatby;
404  $sellby = $supplierorderdispatch->sellby;
405  $batch = $supplierorderdispatch->batch;
406 
407  $result = $supplierorderdispatch->delete($user);
408  }
409  if ($result < 0) {
410  $errors = $object->errors;
411  $error++;
412  } else {
413  // If module stock is enabled and the stock increase is done on purchase order dispatching
414  if ($entrepot > 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER) && empty($supplierorderdispatch->fk_reception)) {
415  $mouv = new MouvementStock($db);
416  if ($product > 0) {
417  $mouv->origin = &$object;
418  $mouv->setOrigin($object->element, $object->id);
419  $result = $mouv->livraison($user, $product, $entrepot, $qty, $price, $comment, '', $eatby, $sellby, $batch);
420  if ($result < 0) {
421  $errors = $mouv->errors;
422  $error++;
423  }
424  }
425  }
426  }
427  if ($error > 0) {
428  $db->rollback();
429  setEventMessages($error, $errors, 'errors');
430  } else {
431  $db->commit();
432  }
433 }
434 
435 // Update a dispatched line
436 if ($action == 'updateline' && $permissiontoreceive) {
437  $db->begin();
438  $error = 0;
439 
440  $supplierorderdispatch = new CommandeFournisseurDispatch($db);
441  $result = $supplierorderdispatch->fetch($lineid);
442  if ($result > 0) {
443  $qty = $supplierorderdispatch->qty;
444  $entrepot = $supplierorderdispatch->fk_entrepot;
445  $product = $supplierorderdispatch->fk_product;
446  $price = price2num(GETPOST('price'), '', 2);
447  $comment = $supplierorderdispatch->comment;
448  $eatby = $supplierorderdispatch->eatby;
449  $sellby = $supplierorderdispatch->sellby;
450  $batch = $supplierorderdispatch->batch;
451 
452  $supplierorderdispatch->qty = price2num(GETPOST('qty', 'alpha'), 'MS', 2);
453  $supplierorderdispatch->fk_entrepot = GETPOST('fk_entrepot');
454  $result = $supplierorderdispatch->update($user);
455  }
456  if ($result < 0) {
457  $error++;
458  $errors = $supplierorderdispatch->errors;
459  } else {
460  // If module stock is enabled and the stock increase is done on purchase order dispatching
461  if ($entrepot > 0 && isModEnabled('stock') && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) {
462  $mouv = new MouvementStock($db);
463  if ($product > 0) {
464  $mouv->origin = &$object;
465  $mouv->setOrigin($object->element, $object->id);
466  $result = $mouv->livraison($user, $product, $entrepot, $qty, $price, $comment, '', $eatby, $sellby, $batch);
467  if ($result < 0) {
468  $errors = $mouv->errors;
469  $error++;
470  } else {
471  $mouv->origin = &$object;
472  $result = $mouv->reception($user, $product, $supplierorderdispatch->fk_entrepot, $supplierorderdispatch->qty, $price, $comment, $eatby, $sellby, $batch);
473  if ($result < 0) {
474  $errors = $mouv->errors;
475  $error++;
476  }
477  }
478  }
479  }
480  }
481  if ($error > 0) {
482  $db->rollback();
483  setEventMessages($error, $errors, 'errors');
484  } else {
485  $db->commit();
486  }
487 }
488 
489 /*
490  * View
491  */
492 
493 $now = dol_now();
494 
495 $form = new Form($db);
496 $formproduct = new FormProduct($db);
497 $warehouse_static = new Entrepot($db);
498 $supplierorderdispatch = new CommandeFournisseurDispatch($db);
499 
500 $title = $object->ref." - ".$langs->trans('OrderDispatch');
501 $help_url = 'EN:Module_Suppliers_Orders|FR:CommandeFournisseur|ES:Módulo_Pedidos_a_proveedores';
502 $morejs = array('/fourn/js/lib_dispatch.js.php');
503 
504 llxHeader('', $title, $help_url, '', 0, 0, $morejs);
505 
506 if ($id > 0 || !empty($ref)) {
507  $soc = new Societe($db);
508  $soc->fetch($object->socid);
509 
510  $author = new User($db);
511  $author->fetch($object->user_author_id);
512 
513  $head = ordersupplier_prepare_head($object);
514 
515  $title = $langs->trans("SupplierOrder");
516  print dol_get_fiche_head($head, 'dispatch', $title, -1, 'order');
517 
518  $formconfirm = '';
519 
520  // Confirmation to delete line
521  if ($action == 'ask_deleteline') {
522  $formconfirm = $form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_deleteline', '', 0, 1);
523  }
524 
525  // Call Hook formConfirm
526  $parameters = array('lineid' => $lineid);
527  // Note that $action and $object may be modified by hook
528  $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action);
529  if (empty($reshook)) {
530  $formconfirm .= $hookmanager->resPrint;
531  } elseif ($reshook > 0) {
532  $formconfirm = $hookmanager->resPrint;
533  }
534 
535  // Print form confirm
536  print $formconfirm;
537 
538  // Supplier order card
539 
540  $linkback = '<a href="'.DOL_URL_ROOT.'/fourn/commande/list.php'.(!empty($socid) ? '?socid='.$socid : '').'">'.$langs->trans("BackToList").'</a>';
541 
542  $morehtmlref = '<div class="refidno">';
543  // Ref supplier
544  $morehtmlref .= $form->editfieldkey("RefSupplier", 'ref_supplier', $object->ref_supplier, $object, 0, 'string', '', 0, 1);
545  $morehtmlref .= $form->editfieldval("RefSupplier", 'ref_supplier', $object->ref_supplier, $object, 0, 'string', '', null, null, '', 1);
546  // Thirdparty
547  $morehtmlref .= '<br>'.$object->thirdparty->getNomUrl(1);
548  // Project
549  if (isModEnabled('project')) {
550  $langs->load("projects");
551  $morehtmlref .= '<br>';
552  if (0) {
553  $morehtmlref .= img_picto($langs->trans("Project"), 'project', 'class="pictofixedwidth"');
554  if ($action != 'classify' && $caneditproject) {
555  $morehtmlref .= '<a class="editfielda" href="'.$_SERVER['PHP_SELF'].'?action=classify&token='.newToken().'&id='.$object->id.'">'.img_edit($langs->transnoentitiesnoconv('SetProject')).'</a> ';
556  }
557  $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, ($action == 'classify' ? 1 : 0), 0, 1, '');
558  } else {
559  if (!empty($object->fk_project)) {
560  $proj = new Project($db);
561  $proj->fetch($object->fk_project);
562  $morehtmlref .= $proj->getNomUrl(1);
563  if ($proj->title) {
564  $morehtmlref .= '<span class="opacitymedium"> - '.dol_escape_htmltag($proj->title).'</span>';
565  }
566  }
567  }
568  }
569  $morehtmlref .= '</div>';
570 
571 
572  dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref);
573 
574 
575  print '<div class="fichecenter">';
576  print '<div class="underbanner clearboth"></div>';
577 
578  print '<table class="border tableforfield" width="100%">';
579 
580  // Date
581  if ($object->methode_commande_id > 0) {
582  print '<tr><td class="titlefield">'.$langs->trans("Date").'</td><td>';
583  if ($object->date_commande) {
584  print dol_print_date($object->date_commande, "dayhour")."\n";
585  }
586  print "</td></tr>";
587 
588  if ($object->methode_commande) {
589  print '<tr><td>'.$langs->trans("Method").'</td><td>'.$object->getInputMethod().'</td></tr>';
590  }
591  }
592 
593  // Author
594  print '<tr><td class="titlefield">'.$langs->trans("AuthorRequest").'</td>';
595  print '<td>'.$author->getNomUrl(1, '', 0, 0, 0).'</td>';
596  print '</tr>';
597 
598  $parameters = array();
599  $reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
600 
601  print "</table>";
602 
603  print '</div>';
604 
605  // if ($mesg) print $mesg;
606  print '<br>';
607 
608  /*$disabled = 1;
609  if (!empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) {
610  $disabled = 0;
611  }*/
612  $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.
613 
614  // Line of orders
615  if ($object->statut <= CommandeFournisseur::STATUS_ACCEPTED || $object->statut >= CommandeFournisseur::STATUS_CANCELED) {
616  print '<br><span class="opacitymedium">'.$langs->trans("OrderStatusNotReadyToDispatch").'</span>';
617  }
618 
619  if ($object->statut == CommandeFournisseur::STATUS_ORDERSENT
621  || $object->statut == CommandeFournisseur::STATUS_RECEIVED_COMPLETELY) {
622  require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
623  $formproduct = new FormProduct($db);
624  $formproduct->loadWarehouses();
625  $entrepot = new Entrepot($db);
626  $listwarehouses = $entrepot->list_array(1);
627 
628 
629  if (empty($conf->reception->enabled)) {
630  print '<form method="POST" action="dispatch.php?id='.$object->id.'">';
631  } else {
632  print '<form method="post" action="'.dol_buildpath('/reception/card.php', 1).'?originid='.$object->id.'&origin=supplierorder">';
633  }
634 
635  print '<input type="hidden" name="token" value="'.newToken().'">';
636  if (empty($conf->reception->enabled)) {
637  print '<input type="hidden" name="action" value="dispatch">';
638  } else {
639  print '<input type="hidden" name="action" value="create">';
640  }
641 
642  print '<div class="div-table-responsive-no-min">';
643  print '<table class="noborder centpercent">';
644 
645  // Set $products_dispatched with qty dispatched for each product id
646  $products_dispatched = array();
647  $sql = "SELECT l.rowid, cfd.fk_product, sum(cfd.qty) as qty";
648  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as cfd";
649  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as l on l.rowid = cfd.fk_commandefourndet";
650  $sql .= " WHERE cfd.fk_commande = ".((int) $object->id);
651  $sql .= " GROUP BY l.rowid, cfd.fk_product";
652 
653  $resql = $db->query($sql);
654  if ($resql) {
655  $num = $db->num_rows($resql);
656  $i = 0;
657 
658  if ($num) {
659  while ($i < $num) {
660  $objd = $db->fetch_object($resql);
661  $products_dispatched[$objd->rowid] = price2num($objd->qty, 5);
662  $i++;
663  }
664  }
665  $db->free($resql);
666  }
667 
668  //$sql = "SELECT l.rowid, l.fk_product, l.subprice, l.remise_percent, l.ref AS sref, SUM(l.qty) as qty,";
669  $sql = "SELECT l.rowid, l.fk_product, l.subprice, l.remise_percent, l.ref AS sref, l.qty as qty,";
670  $sql .= " p.ref, p.label, p.tobatch, p.fk_default_warehouse";
671 
672  // Enable hooks to alter the SQL query (SELECT)
673  $parameters = array();
674  $reshook = $hookmanager->executeHooks(
675  'printFieldListSelect',
676  $parameters,
677  $object,
678  $action
679  );
680  if ($reshook < 0) {
681  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
682  }
683  $sql .= $hookmanager->resPrint;
684 
685  $sql .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as l";
686  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON l.fk_product=p.rowid";
687  $sql .= " WHERE l.fk_commande = ".((int) $object->id);
688  if (empty($conf->global->STOCK_SUPPORTS_SERVICES)) {
689  $sql .= " AND l.product_type = 0";
690  }
691 
692  // Enable hooks to alter the SQL query (WHERE)
693  $parameters = array();
694  $reshook = $hookmanager->executeHooks(
695  'printFieldListWhere',
696  $parameters,
697  $object,
698  $action
699  );
700  if ($reshook < 0) {
701  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
702  }
703  $sql .= $hookmanager->resPrint;
704 
705  //$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
706  $sql .= " ORDER BY l.rang, p.ref, p.label";
707 
708  $resql = $db->query($sql);
709  if ($resql) {
710  $num = $db->num_rows($resql);
711  $i = 0;
712 
713  if ($num) {
714  print '<tr class="liste_titre">';
715 
716  print '<td>'.$langs->trans("Description").'</td>';
717  if (isModEnabled('productbatch')) {
718  print '<td class="dispatch_batch_number_title">'.$langs->trans("batch_number").'</td>';
719  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
720  print '<td class="dispatch_dlc_title">'.$langs->trans("SellByDate").'</td>';
721  }
722  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
723  print '<td class="dispatch_dluo_title">'.$langs->trans("EatByDate").'</td>';
724  }
725  } else {
726  print '<td></td>';
727  print '<td></td>';
728  print '<td></td>';
729  }
730  print '<td class="right">'.$langs->trans("SupplierRef").'</td>';
731  print '<td class="right">'.$langs->trans("QtyOrdered").'</td>';
732  print '<td class="right">'.$langs->trans("QtyDispatchedShort").'</td>';
733  print ' <td class="right">'.$langs->trans("QtyToDispatchShort");
734  print '<br><a href="#" id="autoreset">'.$langs->trans("Reset").'</a></td>';
735  print '<td width="32"></td>';
736 
737  if (!empty($conf->global->SUPPLIER_ORDER_CAN_UPDATE_BUYINGPRICE_DURING_RECEIPT)) {
738  if (!isModEnabled("multicurrency") && empty($conf->dynamicprices->enabled)) {
739  print '<td class="right">'.$langs->trans("Price").'</td>';
740  print '<td class="right">'.$langs->trans("ReductionShort").' (%)</td>';
741  print '<td class="right">'.$langs->trans("UpdatePrice").'</td>';
742  }
743  }
744 
745  print '<td align="right">'.$langs->trans("Warehouse");
746 
747  // Select warehouse to force it everywhere
748  if (count($listwarehouses) > 1) {
749  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);
750  } elseif (count($listwarehouses) == 1) {
751  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);
752  }
753 
754  print '</td>';
755 
756  // Enable hooks to append additional columns
757  $parameters = array();
758  $reshook = $hookmanager->executeHooks(
759  'printFieldListTitle',
760  $parameters,
761  $object,
762  $action
763  );
764  if ($reshook < 0) {
765  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
766  }
767  print $hookmanager->resPrint;
768 
769  print "</tr>\n";
770  }
771 
772  $nbfreeproduct = 0; // Nb of lins of free products/services
773  $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)
774  // or nb of line that remain to dispatch if SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED is on.
775 
776  $conf->cache['product'] = array();
777 
778  while ($i < $num) {
779  $objp = $db->fetch_object($resql);
780 
781  // On n'affiche pas les produits libres
782  if (!$objp->fk_product > 0) {
783  $nbfreeproduct++;
784  } else {
785  $remaintodispatch = price2num($objp->qty - ((float) $products_dispatched[$objp->rowid]), 5); // Calculation of dispatched
786  if ($remaintodispatch < 0 && empty($conf->global->SUPPLIER_ORDER_ALLOW_NEGATIVE_QTY_FOR_SUPPLIER_ORDER_RETURN)) {
787  $remaintodispatch = 0;
788  }
789 
790  if ($remaintodispatch || empty($conf->global->SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED)) {
791  $nbproduct++;
792 
793  // To show detail cref and description value, we must make calculation by cref
794  // print ($objp->cref?' ('.$objp->cref.')':'');
795  // if ($objp->description) print '<br>'.nl2br($objp->description);
796  $suffix = '_0_'.$i;
797 
798  print "\n";
799  print '<!-- Line to dispatch '.$suffix.' -->'."\n";
800  // hidden fields for js function
801  print '<input id="qty_ordered'.$suffix.'" type="hidden" value="'.$objp->qty.'">';
802  print '<input id="qty_dispatched'.$suffix.'" type="hidden" value="'.(float) $products_dispatched[$objp->rowid].'">';
803  print '<tr class="oddeven">';
804 
805  if (empty($conf->cache['product'][$objp->fk_product])) {
806  $tmpproduct = new Product($db);
807  $tmpproduct->fetch($objp->fk_product);
808  $conf->cache['product'][$objp->fk_product] = $tmpproduct;
809  } else {
810  $tmpproduct = $conf->cache['product'][$objp->fk_product];
811  }
812 
813  $linktoprod = $tmpproduct->getNomUrl(1);
814  $linktoprod .= ' - '.$objp->label."\n";
815 
816  if (isModEnabled('productbatch')) {
817  if ($objp->tobatch) {
818  // Product
819  print '<td>';
820  print $linktoprod;
821  print "</td>";
822  print '<td class="dispatch_batch_number"></td>';
823  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
824  print '<td class="dispatch_dlc"></td>';
825  }
826  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
827  print '<td class="dispatch_dluo"></td>';
828  }
829  } else {
830  // Product
831  print '<td>';
832  print $linktoprod;
833  print "</td>";
834  print '<td class="dispatch_batch_number">';
835  print $langs->trans("ProductDoesNotUseBatchSerial");
836  print '</td>';
837  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
838  print '<td class="dispatch_dlc"></td>';
839  }
840  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
841  print '<td class="dispatch_dluo"></td>';
842  }
843  }
844  } else {
845  print '<td colspan="4">';
846  print $linktoprod;
847  print "</td>";
848  }
849 
850  // Define unit price for PMP calculation
851  $up_ht_disc = $objp->subprice;
852  if (!empty($objp->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) {
853  $up_ht_disc = price2num($up_ht_disc * (100 - $objp->remise_percent) / 100, 'MU');
854  }
855 
856  // Supplier ref
857  print '<td class="right">'.$objp->sref.'</td>';
858 
859  // Qty ordered
860  print '<td class="right">'.$objp->qty.'</td>';
861 
862  // Already dispatched
863  print '<td class="right">'.$products_dispatched[$objp->rowid].'</td>';
864 
865  if (isModEnabled('productbatch') && $objp->tobatch > 0) {
866  $type = 'batch';
867  print '<td class="right">';
868  print '</td>'; // Qty to dispatch
869  print '<td>';
870  //print img_picto($langs->trans('AddDispatchBatchLine'), 'split.png', 'onClick="addDispatchLine(' . $i . ',\'' . $type . '\')"');
871  print '</td>'; // Dispatch column
872  print '<td></td>'; // Warehouse column
873 
874  // Enable hooks to append additional columns
875  $parameters = array(
876  // allows hook to distinguish between the rows with information and the rows with dispatch form input
877  'is_information_row' => true,
878  'i' => $i,
879  'suffix' => $suffix,
880  'objp' => $objp,
881  );
882  $reshook = $hookmanager->executeHooks(
883  'printFieldListValue',
884  $parameters,
885  $object,
886  $action
887  );
888  if ($reshook < 0) {
889  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
890  }
891  print $hookmanager->resPrint;
892 
893  print '</tr>';
894 
895  print '<tr class="oddeven" name="'.$type.$suffix.'">';
896  print '<td>';
897  print '<input name="fk_commandefourndet'.$suffix.'" type="hidden" value="'.$objp->rowid.'">';
898  print '<input name="product_batch'.$suffix.'" type="hidden" value="'.$objp->fk_product.'">';
899 
900  print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
901  if (!empty($conf->global->SUPPLIER_ORDER_EDIT_BUYINGPRICE_DURING_RECEIPT)) { // Not tested !
902  print $langs->trans("BuyingPrice").': <input class="maxwidth75" name="pu'.$suffix.'" type="text" value="'.price2num($up_ht_disc, 'MU').'">';
903  } else {
904  print '<input class="maxwidth75" name="pu'.$suffix.'" type="hidden" value="'.price2num($up_ht_disc, 'MU').'">';
905  }
906 
907  print '</td>';
908 
909  print '<td>';
910  print '<input type="text" class="inputlotnumber quatrevingtquinzepercent" id="lot_number'.$suffix.'" name="lot_number'.$suffix.'" value="'.GETPOST('lot_number'.$suffix).'">';
911  print '</td>';
912  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
913  print '<td class="nowraponall">';
914  $dlcdatesuffix = dol_mktime(0, 0, 0, GETPOST('dlc'.$suffix.'month'), GETPOST('dlc'.$suffix.'day'), GETPOST('dlc'.$suffix.'year'));
915  print $form->selectDate($dlcdatesuffix, 'dlc'.$suffix, '', '', 1, '');
916  print '</td>';
917  }
918  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
919  print '<td class="nowraponall">';
920  $dluodatesuffix = dol_mktime(0, 0, 0, GETPOST('dluo'.$suffix.'month'), GETPOST('dluo'.$suffix.'day'), GETPOST('dluo'.$suffix.'year'));
921  print $form->selectDate($dluodatesuffix, 'dluo'.$suffix, '', '', 1, '');
922  print '</td>';
923  }
924  print '<td colspan="3">&nbsp;</td>'; // Supplier ref + Qty ordered + qty already dispatched
925  } else {
926  $type = 'dispatch';
927  $colspan = 7;
928  $colspan = (!empty($conf->global->PRODUCT_DISABLE_SELLBY)) ? --$colspan : $colspan;
929  $colspan = (!empty($conf->global->PRODUCT_DISABLE_EATBY)) ? --$colspan : $colspan;
930  print '<td class="right">';
931  print '</td>'; // Qty to dispatch
932  print '<td>';
933  //print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'onClick="addDispatchLine(' . $i . ',\'' . $type . '\')"');
934  print '</td>'; // Dispatch column
935  print '<td></td>'; // Warehouse column
936 
937  // Enable hooks to append additional columns
938  $parameters = array(
939  // allows hook to distinguish between the rows with information and the rows with dispatch form input
940  'is_information_row' => true,
941  'i' => $i,
942  'suffix' => $suffix,
943  'objp' => $objp,
944  );
945  $reshook = $hookmanager->executeHooks(
946  'printFieldListValue',
947  $parameters,
948  $object,
949  $action
950  );
951  if ($reshook < 0) {
952  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
953  }
954  print $hookmanager->resPrint;
955 
956  print '</tr>';
957 
958  print '<tr class="oddeven" name="'.$type.$suffix.'">';
959  print '<td colspan="'.$colspan.'">';
960  print '<input name="fk_commandefourndet'.$suffix.'" type="hidden" value="'.$objp->rowid.'">';
961  print '<input name="product'.$suffix.'" type="hidden" value="'.$objp->fk_product.'">';
962 
963  print '<!-- This is a up (may include discount or not depending on STOCK_EXCLUDE_DISCOUNT_FOR_PMP. will be used for PMP calculation) -->';
964  if (!empty($conf->global->SUPPLIER_ORDER_EDIT_BUYINGPRICE_DURING_RECEIPT)) { // Not tested !
965  print $langs->trans("BuyingPrice").': <input class="maxwidth75" name="pu'.$suffix.'" type="text" value="'.price2num($up_ht_disc, 'MU').'">';
966  } else {
967  print '<input class="maxwidth75" name="pu'.$suffix.'" type="hidden" value="'.price2num($up_ht_disc, 'MU').'">';
968  }
969 
970  print '</td>';
971  }
972 
973  // Qty to dispatch
974  print '<td class="right">';
975  print '<input id="qty'.$suffix.'" name="qty'.$suffix.'" type="text" class="width50 right" value="'.(GETPOSTISSET('qty'.$suffix) ? GETPOST('qty'.$suffix, 'int') : (empty($conf->global->SUPPLIER_ORDER_DISPATCH_FORCE_QTY_INPUT_TO_ZERO) ? $remaintodispatch : 0)).'">';
976  print '</td>';
977 
978  print '<td>';
979  if (isModEnabled('productbatch') && $objp->tobatch > 0) {
980  $type = 'batch';
981  print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine('.$i.', \''.$type.'\')"');
982  } else {
983  $type = 'dispatch';
984  print img_picto($langs->trans('AddStockLocationLine'), 'split.png', 'class="splitbutton" onClick="addDispatchLine('.$i.', \''.$type.'\')"');
985  }
986  print '</td>';
987 
988  if (!empty($conf->global->SUPPLIER_ORDER_CAN_UPDATE_BUYINGPRICE_DURING_RECEIPT)) {
989  if (!isModEnabled("multicurrency") && empty($conf->dynamicprices->enabled)) {
990  // Price
991  print '<td class="right">';
992  print '<input id="pu'.$suffix.'" name="pu'.$suffix.'" type="text" size="8" value="'.price((GETPOST('pu'.$suffix) != '' ? price2num(GETPOST('pu'.$suffix)) : $up_ht_disc)).'">';
993  print '</td>';
994 
995  // Discount
996  print '<td class="right">';
997  print '<input id="dto'.$suffix.'" name="dto'.$suffix.'" type="text" size="8" value="'.(GETPOST('dto'.$suffix) != '' ? GETPOST('dto'.$suffix) : '').'">';
998  print '</td>';
999 
1000  // Save price
1001  print '<td class="center">';
1002  print '<input class="flat checkformerge" type="checkbox" name="saveprice'.$suffix.'" value="'.(GETPOST('saveprice'.$suffix) != '' ? GETPOST('saveprice'.$suffix) : '').'">';
1003  print '</td>';
1004  }
1005  }
1006 
1007  // Warehouse
1008  print '<td class="right">';
1009  if (count($listwarehouses) > 1) {
1010  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);
1011  } elseif (count($listwarehouses) == 1) {
1012  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);
1013  } else {
1014  $langs->load("errors");
1015  print $langs->trans("ErrorNoWarehouseDefined");
1016  }
1017  print "</td>\n";
1018 
1019  // Enable hooks to append additional columns
1020  $parameters = array(
1021  'is_information_row' => false, // this is a dispatch form row
1022  'i' => $i,
1023  'suffix' => $suffix,
1024  'objp' => $objp,
1025  );
1026  $reshook = $hookmanager->executeHooks(
1027  'printFieldListValue',
1028  $parameters,
1029  $object,
1030  $action
1031  );
1032  if ($reshook < 0) {
1033  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
1034  }
1035  print $hookmanager->resPrint;
1036 
1037  print "</tr>\n";
1038  }
1039  }
1040  $i++;
1041  }
1042  $db->free($resql);
1043  } else {
1044  dol_print_error($db);
1045  }
1046 
1047  print "</table>\n";
1048  print '</div>';
1049 
1050  if ($nbproduct) {
1051  $checkboxlabel = $langs->trans("CloseReceivedSupplierOrdersAutomatically", $langs->transnoentitiesnoconv('StatusOrderReceivedAll'));
1052 
1053  print '<div class="center">';
1054  $parameters = array();
1055  $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been
1056  // modified by hook
1057  if (empty($reshook)) {
1058  if (empty($conf->reception->enabled)) {
1059  print $langs->trans("Comment").' : ';
1060  print '<input type="text" class="minwidth400" maxlength="128" name="comment" value="';
1061  print GETPOSTISSET("comment") ? GETPOST("comment") : $langs->trans("DispatchSupplierOrder", $object->ref);
1062  // print ' / '.$object->ref_supplier; // Not yet available
1063  print '" class="flat"><br>';
1064 
1065  print '<input type="checkbox" checked="checked" name="closeopenorder"> '.$checkboxlabel;
1066  }
1067 
1068  $dispatchBt = empty($conf->reception->enabled) ? $langs->trans("Receive") : $langs->trans("CreateReception");
1069 
1070  print '<br>';
1071  print '<input type="submit" class="button" name="dispatch" value="'.dol_escape_htmltag($dispatchBt).'"';
1072  $disabled = 0;
1073  if (!$permissiontoreceive) {
1074  $disabled = 1;
1075  }
1076  if (count($listwarehouses) <= 0) {
1077  $disabled = 1;
1078  }
1079  if ($disabled) {
1080  print ' disabled';
1081  }
1082 
1083  print '>';
1084  }
1085  print '</div>';
1086  }
1087 
1088  // Message if nothing to dispatch
1089  if (!$nbproduct) {
1090  print "<br>\n";
1091  if (empty($conf->global->SUPPLIER_ORDER_DISABLE_STOCK_DISPATCH_WHEN_TOTAL_REACHED)) {
1092  print '<div class="opacitymedium">'.$langs->trans("NoPredefinedProductToDispatch").'</div>'; // No predefined line at all
1093  } else {
1094  print '<div class="opacitymedium">'.$langs->trans("NoMorePredefinedProductToDispatch").'</div>'; // No predefined line that remain to be dispatched.
1095  }
1096  }
1097 
1098  print '</form>';
1099  }
1100 
1101  print dol_get_fiche_end();
1102 
1103  // traitement entrepot par défaut
1104  print '<script type="text/javascript">
1105  $(document).ready(function () {
1106  $("select[name=fk_default_warehouse]").change(function() {
1107  var fk_default_warehouse = $("option:selected", this).val();
1108  $("select[name^=entrepot_]").val(fk_default_warehouse).change();
1109  });
1110 
1111  jQuery("#autoreset").click(function() {';
1112  $i = 0;
1113  while ($i < $nbproduct) {
1114  print ' jQuery("#qty_0_'.$i.'").val("");';
1115  $i++;
1116  }
1117  print '
1118  });
1119  });
1120  </script>';
1121 
1122  // List of lines already dispatched
1123  $sql = "SELECT p.rowid as pid, p.ref, p.label,";
1124  $sql .= " e.rowid as warehouse_id, e.ref as entrepot,";
1125  $sql .= " cfd.rowid as dispatchlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.datec";
1126  $sql .= " ,cd.rowid, cd.subprice";
1127  if ($conf->reception->enabled) {
1128  $sql .= " ,cfd.fk_reception, r.date_delivery";
1129  }
1130  $sql .= " FROM ".MAIN_DB_PREFIX."product as p,";
1131  $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as cfd";
1132  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."commande_fournisseurdet as cd ON cd.rowid = cfd.fk_commandefourndet";
1133  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e ON cfd.fk_entrepot = e.rowid";
1134  if ($conf->reception->enabled) {
1135  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."reception as r ON cfd.fk_reception = r.rowid";
1136  }
1137  $sql .= " WHERE cfd.fk_commande = ".((int) $object->id);
1138  $sql .= " AND cfd.fk_product = p.rowid";
1139  $sql .= " ORDER BY cfd.rowid ASC";
1140 
1141  $resql = $db->query($sql);
1142  if ($resql) {
1143  $num = $db->num_rows($resql);
1144  $i = 0;
1145 
1146  if ($num > 0) {
1147  print "<br>\n";
1148 
1149  print load_fiche_titre($langs->trans("ReceivingForSameOrder"));
1150 
1151  print '<div class="div-table-responsive">';
1152  print '<table id="dispatch_received_products" class="noborder centpercent">';
1153 
1154  print '<tr class="liste_titre">';
1155  // Reception ref
1156  if ($conf->reception->enabled) {
1157  print '<td>'.$langs->trans("Reception").'</td>';
1158  }
1159  // Product
1160  print '<td>'.$langs->trans("Product").'</td>';
1161  print '<td class="center">'.$langs->trans("DateCreation").'</td>';
1162  print '<td class="center">'.$langs->trans("DateDeliveryPlanned").'</td>';
1163  if (isModEnabled('productbatch')) {
1164  print '<td class="dispatch_batch_number_title">'.$langs->trans("batch_number").'</td>';
1165  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
1166  print '<td class="dispatch_dlc_title">'.$langs->trans("SellByDate").'</td>';
1167  }
1168  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
1169  print '<td class="dispatch_dluo_title">'.$langs->trans("EatByDate").'</td>';
1170  }
1171  }
1172  print '<td class="right">'.$langs->trans("QtyDispatched").'</td>';
1173  print '<td>'.$langs->trans("Warehouse").'</td>';
1174  print '<td>'.$langs->trans("Comment").'</td>';
1175 
1176  // Status
1177  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS) && empty($reception->rowid)) {
1178  print '<td class="center" colspan="2">'.$langs->trans("Status").'</td>';
1179  } elseif (isModEnabled("reception")) {
1180  print '<td class="center"></td>';
1181  }
1182 
1183  print '<td class="center" colspan="2"></td>';
1184 
1185  print "</tr>\n";
1186 
1187 
1188  while ($i < $num) {
1189  $objp = $db->fetch_object($resql);
1190 
1191  if ($action == 'editline' && $lineid == $objp->dispatchlineid) {
1192  print '<form name="editdispatchedlines" id="editdispatchedlines" action="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'#line_'.GETPOST('lineid', 'int').'" method="POST">
1193  <input type="hidden" name="token" value="'.newToken().'">
1194  <input type="hidden" name="action" value="updateline">
1195  <input type="hidden" name="mode" value="">
1196  <input type="hidden" name="lineid" value="'.$objp->dispatchlineid.'">';
1197  }
1198 
1199  print '<tr class="oddeven" id="line_'.$objp->dispatchlineid.'" >';
1200 
1201  // Reception ref
1202  if (isModEnabled("reception")) {
1203  print '<td class="nowraponall">';
1204  if (!empty($objp->fk_reception)) {
1205  $reception = new Reception($db);
1206  $reception->fetch($objp->fk_reception);
1207  print $reception->getNomUrl(1);
1208  }
1209 
1210  print "</td>";
1211  }
1212 
1213  // Product
1214  print '<td class="tdoverflowmax150">';
1215  if (empty($conf->cache['product'][$objp->fk_product])) {
1216  $tmpproduct = new Product($db);
1217  $tmpproduct->fetch($objp->fk_product);
1218  $conf->cache['product'][$objp->fk_product] = $tmpproduct;
1219  } else {
1220  $tmpproduct = $conf->cache['product'][$objp->fk_product];
1221  }
1222  print $tmpproduct->getNomUrl(1);
1223  print ' - '.$objp->label;
1224  print "</td>\n";
1225 
1226  // Date creation
1227  print '<td class="center">'.dol_print_date($db->jdate($objp->datec), 'day').'</td>';
1228 
1229  // Date delivery
1230  print '<td class="center">'.dol_print_date($db->jdate($objp->date_delivery), 'day').'</td>';
1231 
1232  // Batch / Eat by / Sell by
1233  if (isModEnabled('productbatch')) {
1234  if ($objp->batch) {
1235  include_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
1236  $lot = new Productlot($db);
1237  $lot->fetch(0, $objp->pid, $objp->batch);
1238  print '<td class="dispatch_batch_number">'.$lot->getNomUrl(1).'</td>';
1239  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
1240  print '<td class="dispatch_dlc">'.dol_print_date($lot->sellby, 'day').'</td>';
1241  }
1242  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
1243  print '<td class="dispatch_dluo">'.dol_print_date($lot->eatby, 'day').'</td>';
1244  }
1245  } else {
1246  print '<td class="dispatch_batch_number"></td>';
1247  if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
1248  print '<td class="dispatch_dlc"></td>';
1249  }
1250  if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
1251  print '<td class="dispatch_dluo"></td>';
1252  }
1253  }
1254  }
1255 
1256  // Qty
1257  print '<td class="right">';
1258  if ($action == 'editline' && $lineid == $objp->dispatchlineid) {
1259  print '<input style="width: 50px;" type="text" min="1" name="qty" value="'.$objp->qty.'" />';
1260  } else {
1261  print $objp->qty;
1262  }
1263  print '<input type="hidden" name="price" value="'.$objp->subprice.'" />';
1264  print '</td>';
1265 
1266  // Warehouse
1267  print '<td class="tdoverflowmax150">';
1268  if ($action == 'editline' && $lineid == $objp->dispatchlineid) {
1269  if (count($listwarehouses) > 1) {
1270  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');
1271  } elseif (count($listwarehouses) == 1) {
1272  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');
1273  } else {
1274  $langs->load("errors");
1275  print $langs->trans("ErrorNoWarehouseDefined");
1276  }
1277  } else {
1278  $warehouse_static->id = $objp->warehouse_id;
1279  $warehouse_static->label = $objp->entrepot;
1280  print $warehouse_static->getNomUrl(1);
1281  }
1282  print '</td>';
1283 
1284  // Comment
1285  print '<td class="tdoverflowmax300" style="white-space: pre;">'.$objp->comment.'</td>';
1286 
1287  // Status
1288  if (!empty($conf->global->SUPPLIER_ORDER_USE_DISPATCH_STATUS) && empty($reception->rowid)) {
1289  print '<td class="right">';
1290  $supplierorderdispatch->status = (empty($objp->status) ? 0 : $objp->status);
1291  // print $supplierorderdispatch->status;
1292  print $supplierorderdispatch->getLibStatut(5);
1293  print '</td>';
1294 
1295  // Add button to check/uncheck disaptching
1296  print '<td class="center">';
1297  if (!$permissiontocontrol) {
1298  if (empty($objp->status)) {
1299  print '<a class="button buttonRefused" href="#">'.$langs->trans("Approve").'</a>';
1300  print '<a class="button buttonRefused" href="#">'.$langs->trans("Deny").'</a>';
1301  } else {
1302  print '<a class="button buttonRefused" href="#">'.$langs->trans("Disapprove").'</a>';
1303  print '<a class="button buttonRefused" href="#">'.$langs->trans("Deny").'</a>';
1304  }
1305  } else {
1306  $disabled = '';
1307  if ($object->statut == 5) {
1308  $disabled = 1;
1309  }
1310  if (empty($objp->status)) {
1311  print '<a class="button'.($disabled ? ' buttonRefused' : '').'" href="'.$_SERVER["PHP_SELF"]."?id=".$id."&action=checkdispatchline&lineid=".$objp->dispatchlineid.'">'.$langs->trans("Approve").'</a>';
1312  print '<a class="button'.($disabled ? ' buttonRefused' : '').'" href="'.$_SERVER["PHP_SELF"]."?id=".$id."&action=denydispatchline&lineid=".$objp->dispatchlineid.'">'.$langs->trans("Deny").'</a>';
1313  }
1314  if ($objp->status == 1) {
1315  print '<a class="button'.($disabled ? ' buttonRefused' : '').'" href="'.$_SERVER["PHP_SELF"]."?id=".$id."&action=uncheckdispatchline&lineid=".$objp->dispatchlineid.'">'.$langs->trans("Reinit").'</a>';
1316  print '<a class="button'.($disabled ? ' buttonRefused' : '').'" href="'.$_SERVER["PHP_SELF"]."?id=".$id."&action=denydispatchline&lineid=".$objp->dispatchlineid.'">'.$langs->trans("Deny").'</a>';
1317  }
1318  if ($objp->status == 2) {
1319  print '<a class="button'.($disabled ? ' buttonRefused' : '').'" href="'.$_SERVER["PHP_SELF"]."?id=".$id."&action=uncheckdispatchline&lineid=".$objp->dispatchlineid.'">'.$langs->trans("Reinit").'</a>';
1320  print '<a class="button'.($disabled ? ' buttonRefused' : '').'" href="'.$_SERVER["PHP_SELF"]."?id=".$id."&action=checkdispatchline&lineid=".$objp->dispatchlineid.'">'.$langs->trans("Approve").'</a>';
1321  }
1322  }
1323  print '</td>';
1324  } elseif (isModEnabled("reception")) {
1325  print '<td class="right">';
1326  if (!empty($reception->id)) {
1327  print $reception->getLibStatut(5);
1328  }
1329  print '</td>';
1330  }
1331 
1332  // Action
1333  if ($action != 'editline' || $lineid != $objp->dispatchlineid) {
1334  if (empty($reception->id) || ($reception->statut == Reception::STATUS_DRAFT)) { // only allow edit on draft reception
1335  print '<td class="linecoledit center">';
1336  print '<a class="reposition" href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=editline&token='.newToken().'&lineid='.$objp->dispatchlineid.'#line_'.$objp->dispatchlineid.'">';
1337  print img_edit();
1338  print '</a>';
1339  print '</td>';
1340 
1341  print '<td class="linecoldelete center">';
1342  print '<a href="'.$_SERVER["PHP_SELF"].'?id='.$object->id.'&action=ask_deleteline&token='.newToken().'&lineid='.$objp->dispatchlineid.'#dispatch_received_products">';
1343  print img_delete();
1344  print '</a>';
1345  print '</td>';
1346  } else {
1347  print '<td></td><td></td>';
1348  }
1349  } else {
1350  print '<td class="center valignmiddle">';
1351  print '<input type="submit" class="button button-save" id="savelinebutton" name="save" value="'.$langs->trans("Save").'" />';
1352  print '</td>';
1353  print '<td class="center valignmiddle">';
1354  print '<input type="submit" class="button button-cancel" id="cancellinebutton" name="cancel" value="'.$langs->trans("Cancel").'" />';
1355  print '</td>';
1356  }
1357 
1358 
1359  print "</tr>\n";
1360  if ($action == 'editline' && $lineid == $objp->dispatchlineid) {
1361  print '</form>';
1362  }
1363 
1364  $i++;
1365  }
1366  $db->free($resql);
1367 
1368  print "</table>\n";
1369  print '</div>';
1370  }
1371  } else {
1372  dol_print_error($db);
1373  }
1374 }
1375 
1376 // End of page
1377 llxFooter();
1378 $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
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 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:45
if(isModEnabled('facture') &&!empty($user->rights->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') &&!empty($user->rights->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)) $resql
Social contributions to pay.
Definition: index.php:745
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:138
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='')
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='')
Set event messages in dol_events session object.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
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.
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:843
div float
Buy price without taxes.
Definition: style.css.php:913
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:122
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.