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