dolibarr 25.0.0-alpha
replenish.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
3 * Copyright (C) 2013-2018 Laurent Destaileur <ely@users.sourceforge.net>
4 * Copyright (C) 2014 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2016 Juanjo Menent <jmenent@2byte.es>
6 * Copyright (C) 2016 ATM Consulting <support@atm-consulting.fr>
7 * Copyright (C) 2019-2025 Frédéric France <frederic.france@free.fr>
8 * Copyright (C) 2021 Ferran Marcet <fmarcet@2byte.es>
9 * Copyright (C) 2021 Antonin MARCHAL <antonin@letempledujeu.fr>
10 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
11 * Copyright (C) 2025 Josep Lluís Amador <joseplluis@lliuretic.cat>
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
34require '../../main.inc.php';
42require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
43require_once DOL_DOCUMENT_ROOT . '/core/class/html.formother.class.php';
44require_once DOL_DOCUMENT_ROOT . '/core/class/html.form.class.php';
45require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.commande.class.php';
46require_once DOL_DOCUMENT_ROOT . '/product/class/html.formproduct.class.php';
47require_once './lib/replenishment.lib.php';
48
49// Load translation files required by the page
50$langs->loadLangs(array('products', 'stocks', 'orders'));
51
52// Security check
53if ($user->socid) {
54 $socid = $user->socid;
55}
56
57// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
58$hookmanager->initHooks(array('stockreplenishlist'));
59
60//checks if a product has been ordered
61
62$action = GETPOST('action', 'aZ09');
63$search_ref = GETPOST('search_ref', 'alpha');
64$search_label = GETPOST('search_label', 'alpha');
65$sall = trim(GETPOST('search_all', 'alphanohtml'));
66$type = GETPOSTINT('type');
67$tobuy = GETPOSTFLOAT('tobuy');
68$salert = GETPOST('salert', 'alpha');
69$includeproductswithoutdesiredqty = GETPOST('includeproductswithoutdesiredqty', 'alpha');
70$mode = GETPOST('mode', 'alpha');
71$draftorder = GETPOST('draftorder', 'alpha');
72
73
74$fourn_id = GETPOSTINT('fourn_id');
75$fk_supplier = GETPOSTINT('fk_supplier');
76$fk_entrepot = GETPOSTINT('fk_entrepot');
77
78// List all visible warehouses
79$resWar = $db->query("SELECT rowid FROM " . MAIN_DB_PREFIX . "entrepot WHERE entity IN (" . $db->sanitize(getEntity('stock')) . ")");
80$listofqualifiedwarehousesid = "";
81$lastWarehouseID = 0;
82$count = 0;
83while ($tmpobj = $db->fetch_object($resWar)) {
84 if (!empty($listofqualifiedwarehousesid)) {
85 $listofqualifiedwarehousesid .= ",";
86 }
87 $listofqualifiedwarehousesid .= $tmpobj->rowid;
88 $lastWarehouseID = (int) $tmpobj->rowid;
89 $count++;
90}
91
92//MultiCompany : If only 1 Warehouse is visible, filter will automatically be set to it.
93if ($count == 1 && (empty($fk_entrepot) || $fk_entrepot <= 0) && getDolGlobalString('MULTICOMPANY_PRODUCT_SHARING_ENABLED')) {
94 $fk_entrepot = $lastWarehouseID;
95}
96//If the warehouse is set to the default selected user
97if (!GETPOSTISSET('fk_warehouse') && (empty($fk_entrepot) || $fk_entrepot <= 0) && getDolGlobalString('MAIN_DEFAULT_WAREHOUSE_USER')) {
98 $fk_entrepot = $user->fk_warehouse;
99}
100
101$texte = '';
102
103$sortfield = GETPOST('sortfield', 'aZ09comma');
104$sortorder = GETPOST('sortorder', 'aZ09comma');
105$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
106if (empty($page) || $page == -1) {
107 $page = 0;
108} // If $page is not defined, or '' or -1
109$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
110$offset = $limit * $page;
111
112if (!$sortfield) {
113 $sortfield = 'p.ref';
114}
115
116if (!$sortorder) {
117 $sortorder = 'ASC';
118}
119
120// Define virtualdiffersfromphysical
121$virtualdiffersfromphysical = 0;
122if (getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT')
123 || getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER')
124 || getDolGlobalString('STOCK_CALCULATE_ON_SHIPMENT_CLOSE')
125 || getDolGlobalString('STOCK_CALCULATE_ON_RECEPTION')
126 || getDolGlobalString('STOCK_CALCULATE_ON_RECEPTION_CLOSE')
127 || isModEnabled('mrp')) {
128 $virtualdiffersfromphysical = 1; // According to increase/decrease stock options, virtual and physical stock may differs.
129}
130
131if ($virtualdiffersfromphysical) {
132 $usevirtualstock = getDolGlobalString('STOCK_USE_REAL_STOCK_BY_DEFAULT_FOR_REPLENISHMENT') ? 0 : 1;
133} else {
134 $usevirtualstock = 0;
135}
136if ($mode == 'physical') {
137 $usevirtualstock = 0;
138}
139if ($mode == 'virtual') {
140 $usevirtualstock = 1;
141}
142
144
145if (!isModEnabled('stock')) {
146 accessforbidden("Module stock must be enabled to use this feature");
147}
148if (!$user->hasRight('stock', 'read')) {
149 accessforbidden("You need permission to read stock to access this feature");
150}
151
152restrictedArea($user, 'produit|service');
153
154
155/*
156 * Actions
157 */
158
159$parameters = array();
160$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
161if ($reshook < 0) {
162 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
163}
164
165if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // Both test are required to be compatible with all browsers
166 $search_ref = '';
167 $search_label = '';
168 $sall = '';
169 $salert = '';
170 $includeproductswithoutdesiredqty = '';
171 $draftorder = '';
172}
173$draftchecked = "";
174if ($draftorder == 'on') {
175 $draftchecked = "checked";
176}
177
178// Create purchase orders
179if ($action == 'order' && GETPOST('valid') && $user->hasRight('fournisseur', 'commande', 'creer')) {
180 $linecount = GETPOSTINT('linecount');
181 $box = 0;
182 $errorQty = 0;
183 unset($_POST['linecount']);
184 if ($linecount > 0) {
185 $db->begin();
186
187 $suppliers = array();
188 require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
189 $productsupplier = new ProductFournisseur($db);
190 for ($i = 0; $i < $linecount; $i++) {
191 if (GETPOST('choose'.$i) === 'on' && GETPOSTINT('fourn'.$i) > 0) {
192 //one line
193 $box = $i;
194 $supplierpriceid = GETPOSTINT('fourn'.$i);
195 //get all the parameters needed to create a line
196
197 $qty = GETPOSTFLOAT('tobuy'.$i);
198
199 // Resolve the product and the supplier of the selected price line first. Without them,
200 // get_buyprice() falls back to a search with no product and no supplier filter, and can
201 // return a price row of another supplier for another product (see #40182).
202 $tmpprodfourn = new ProductFournisseur($db);
203 if ($tmpprodfourn->fetch_product_fournisseur_price($supplierpriceid) > 0) {
204 $idprod = $productsupplier->get_buyprice($supplierpriceid, $qty, $tmpprodfourn->product_id, 'none', $tmpprodfourn->fourn_id);
205 } else {
206 $idprod = $productsupplier->get_buyprice($supplierpriceid, $qty);
207 }
208
209 $res = $productsupplier->fetch($idprod);
210 if ($res && $idprod > 0 && $fk_supplier > 0 && (int) $productsupplier->fourn_socid !== (int) $fk_supplier) {
211 // Safety net: never let a line be attached to a supplier different from the one filtered on.
212 dol_syslog("replenish.php: get_buyprice returned fourn_socid=".$productsupplier->fourn_socid." for fk_product=".$idprod." instead of expected fk_supplier=".$fk_supplier." (line $i, product_fournisseur_price id $supplierpriceid, qty $qty)", LOG_WARNING);
213 $errorQty++;
214 } elseif ($res && $idprod > 0) {
215 if ($qty) {
216 //might need some value checks
217 $line = new CommandeFournisseurLigne($db);
218 $line->qty = $qty;
219 $line->fk_product = $idprod;
220
221 //$product = new Product($db);
222 //$product->fetch($obj->fk_product);
223 if (getDolGlobalInt('MAIN_MULTILANGS')) {
224 $productsupplier->getMultiLangs();
225 }
226
227 // if we use supplier description of the products
228 if (!empty($productsupplier->desc_supplier) && getDolGlobalString('PRODUIT_FOURN_TEXTS')) {
229 $desc = $productsupplier->desc_supplier;
230 } else {
231 $desc = $productsupplier->description;
232 }
233 $line->desc = $desc;
234 if (getDolGlobalInt('MAIN_MULTILANGS')) {
235 // TODO Get desc in language of thirdparty
236 }
237
238 $line->tva_tx = $productsupplier->vatrate_supplier;
239 $tva = $line->tva_tx / 100;
240
241 // If we use multicurrency
242 if (isModEnabled('multicurrency') && !empty($productsupplier->fourn_multicurrency_code) && $productsupplier->fourn_multicurrency_code != $conf->currency) {
243 $line->multicurrency_code = $productsupplier->fourn_multicurrency_code;
244 $line->fk_multicurrency = (int) $productsupplier->fourn_multicurrency_id;
245 $line->multicurrency_subprice = $productsupplier->fourn_multicurrency_unitprice;
246 $line->multicurrency_total_ht = $line->multicurrency_subprice * $qty;
247 $line->multicurrency_total_tva = $line->multicurrency_total_ht * $tva;
248 $line->multicurrency_total_ttc = $line->multicurrency_total_ht + $line->multicurrency_total_tva;
249 }
250 $line->subprice = $productsupplier->fourn_pu;
251 $line->total_ht = $productsupplier->fourn_pu * $qty;
252 $line->total_tva = $line->total_ht * $tva;
253 $line->total_ttc = $line->total_ht + $line->total_tva;
254 $line->remise_percent = (float) $productsupplier->remise_percent;
255 $line->ref_fourn = $productsupplier->ref_supplier; // deprecated
256 $line->ref_supplier = $productsupplier->ref_supplier;
257 $line->type = $productsupplier->type;
258 $line->fk_unit = $productsupplier->fk_unit;
259
260 $suppliers[$productsupplier->fourn_socid]['lines'][] = $line;
261 }
262 } elseif ($idprod == -1) {
263 $errorQty++;
264 } else {
265 $error = $db->lasterror();
266 dol_print_error($db);
267 }
268
269 unset($_POST['fourn' . $i]);
270 }
271 unset($_POST[$i]);
272 }
273
274 //we now know how many orders we need and what lines they have
275 $i = 0;
276 $fail = 0;
277 $id = 0;
278 $orders = array();
279 $suppliersid = array_keys($suppliers); // array of ids of suppliers
280 foreach ($suppliers as $supplier) {
281 $order = new CommandeFournisseur($db);
282
283 // Check if an order for the supplier exists
284 $sql = "SELECT rowid FROM " . MAIN_DB_PREFIX . "commande_fournisseur";
285 $sql .= " WHERE fk_soc = " . ((int) $suppliersid[$i]);
286 $sql .= " AND source = " . ((int) $order::SOURCE_ID_REPLENISHMENT) . " AND fk_statut = " . ((int) $order::STATUS_DRAFT);
287 $sql .= " AND entity IN (" . getEntity('commande_fournisseur') . ")";
288 $sql .= " ORDER BY date_creation DESC";
289 $resql = $db->query($sql);
290 if ($resql && $db->num_rows($resql) > 0) {
291 $obj = $db->fetch_object($resql);
292
293 $order->fetch($obj->rowid);
294 $order->fetch_thirdparty();
295
296 $result = 0; // Stays 0 when the supplier has no line, so the test below is not done on an undefined value
297 foreach ($supplier['lines'] as $line) {
298 if (empty($line->remise_percent)) {
299 $line->remise_percent = (float) $order->thirdparty->remise_supplier_percent;
300 }
301 $result = $order->addline(
302 $line->desc,
303 $line->subprice,
304 $line->qty,
305 $line->tva_tx,
306 $line->localtax1_tx,
307 $line->localtax2_tx,
308 $line->fk_product,
309 0,
310 $line->ref_fourn,
311 $line->remise_percent,
312 'HT',
313 0,
314 $line->type,
315 0,
316 0,
317 null,
318 null,
319 array(),
320 $line->fk_unit,
321 $line->multicurrency_subprice
322 );
323 if ($result < 0) {
324 break;
325 }
326 }
327 if ($result < 0) {
328 $fail++;
329 $msg = $langs->trans('OrderFail') . "&nbsp;:&nbsp;";
330 $msg .= $order->error;
331 setEventMessages($msg, null, 'errors');
332 } else {
333 $id = $result;
334 }
335 $i++;
336 } else {
337 $order->socid = $suppliersid[$i];
338 $order->fetch_thirdparty();
339 $order->multicurrency_code = $order->thirdparty->multicurrency_code;
340
341 // Trick to know which orders have been generated using the replenishment feature
342 $order->source = $order::SOURCE_ID_REPLENISHMENT;
343
344 foreach ($supplier['lines'] as $line) {
345 if (empty($line->remise_percent)) {
346 $line->remise_percent = (float) $order->thirdparty->remise_supplier_percent;
347 }
348 $order->lines[] = $line;
349 }
350 $order->cond_reglement_id = (int) $order->thirdparty->cond_reglement_supplier_id;
351 $order->mode_reglement_id = (int) $order->thirdparty->mode_reglement_supplier_id;
352
353 $id = $order->create($user);
354 if ($id < 0) {
355 $fail++;
356 $msg = $langs->trans('OrderFail') . "&nbsp;:&nbsp;";
357 $msg .= $order->error;
358 setEventMessages($msg, null, 'errors');
359 }
360 $i++;
361 }
362 }
363
364 if ($errorQty) {
365 setEventMessages($langs->trans('ErrorOrdersNotCreatedQtyTooLow'), null, 'warnings');
366 }
367
368 if (!$fail && $id) {
369 $db->commit();
370
371 setEventMessages($langs->trans('OrderCreated'), null, 'mesgs');
372 header('Location: replenishorders.php');
373 exit;
374 } else {
375 $db->rollback();
376 }
377 }
378 if ($box == 0) {
379 setEventMessages($langs->trans('SelectProductWithNotNullQty'), null, 'warnings');
380 }
381}
382
383
384/*
385 * View
386 */
387
388$form = new Form($db);
389$formproduct = new FormProduct($db);
390$prod = new Product($db);
391
392$title = $langs->trans('MissingStocks');
393
394if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
395 $sqldesiredstock = $db->ifsql("pse.desiredstock IS NULL", "p.desiredstock", "pse.desiredstock");
396 $sqlalertstock = $db->ifsql("pse.seuil_stock_alerte IS NULL", "p.seuil_stock_alerte", "pse.seuil_stock_alerte");
397} else {
398 $sqldesiredstock = 'p.desiredstock';
399 $sqlalertstock = 'p.seuil_stock_alerte';
400}
401
402$sql = 'SELECT p.rowid, p.ref, p.label, p.description, p.price,';
403$sql .= ' p.price_ttc, p.price_base_type, p.fk_product_type,';
404$sql .= ' p.tms as datem, p.duration, p.tobuy,';
405$sql .= ' p.desiredstock, p.seuil_stock_alerte,';
406if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
407 $sql .= ' pse.desiredstock as desiredstockpse, pse.seuil_stock_alerte as seuil_stock_alertepse,';
408}
409$sql .= " " . $sqldesiredstock . " as desiredstockcombined, " . $sqlalertstock . " as seuil_stock_alertecombined,";
410$sql .= ' s.fk_product,';
411$sql .= " SUM(".$db->ifsql("s.reel IS NULL", "0", "s.reel").') as stock_physique';
412if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
413 $sql .= ", SUM(".$db->ifsql("s.reel IS NULL OR s.fk_entrepot <> ".((int) $fk_entrepot), "0", "s.reel").') as stock_real_warehouse';
414}
415
416// Add fields from hooks
417$parameters = array();
418$reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters); // Note that $action and $object may have been modified by hook
419$sql .= $hookmanager->resPrint;
420
421$list_warehouse = (empty($listofqualifiedwarehousesid) ? '0' : $listofqualifiedwarehousesid);
422
423$sql .= ' FROM ' . MAIN_DB_PREFIX . 'product as p';
424$sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product_stock as s ON p.rowid = s.fk_product';
425$sql .= ' AND s.fk_entrepot IN (' . $db->sanitize($list_warehouse) . ')';
426
427$list_warehouse_selected = ($fk_entrepot < 0 || empty($fk_entrepot)) ? $list_warehouse : $fk_entrepot;
428$sql .= ' AND s.fk_entrepot IN (' . $db->sanitize($list_warehouse_selected) . ')';
429
430
431//$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'entrepot AS ent ON s.fk_entrepot = ent.rowid AND ent.entity IN('.getEntity('stock').')';
432if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
433 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_warehouse_properties AS pse ON (p.rowid = pse.fk_product AND pse.fk_entrepot = '.((int) $fk_entrepot).')';
434}
435// Add fields from hooks
436$parameters = array();
437$reshook = $hookmanager->executeHooks('printFieldListJoin', $parameters); // Note that $action and $object may have been modified by hook
438$sql .= $hookmanager->resPrint;
439
440$sql .= ' WHERE p.entity IN (' . getEntity('product') . ')';
441if ($sall) {
442 $sql .= natural_search(array('p.ref', 'p.label', 'p.description', 'p.note'), $sall);
443}
444// if the type is not 1, we show all products (type = 0,2,3)
445if (dol_strlen((string) $type)) {
446 if ($type == 1) {
447 $sql .= ' AND p.fk_product_type = 1';
448 } else {
449 $sql .= ' AND p.fk_product_type <> 1';
450 }
451}
452if ($search_ref) {
453 $sql .= natural_search('p.ref', $search_ref);
454}
455if ($search_label) {
456 $sql .= natural_search('p.label', $search_label);
457}
458$sql .= ' AND p.tobuy = 1';
459if (isModEnabled('variants') && !getDolGlobalString('VARIANT_ALLOW_STOCK_MOVEMENT_ON_VARIANT_PARENT')) { // Add test to exclude products that has variants
460 $sql .= ' AND p.rowid NOT IN (SELECT pac.fk_product_parent FROM '.MAIN_DB_PREFIX.'product_attribute_combination as pac WHERE pac.entity IN ('.getEntity('product').'))';
461}
462if ($fk_supplier > 0) {
463 $sql .= ' AND EXISTS (SELECT pfp.rowid FROM ' . MAIN_DB_PREFIX . 'product_fournisseur_price as pfp WHERE pfp.fk_product = p.rowid AND pfp.fk_soc = ' . ((int) $fk_supplier) . ' AND pfp.entity IN (' . getEntity('productsupplierprice') . '))';
464}
465// Add where from hooks
466$parameters = array();
467$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
468$sql .= $hookmanager->resPrint;
469
470$sql .= ' GROUP BY p.rowid, p.ref, p.label, p.description, p.price';
471$sql .= ', p.price_ttc, p.price_base_type,p.fk_product_type, p.tms';
472$sql .= ', p.duration, p.tobuy';
473$sql .= ', p.desiredstock';
474$sql .= ', p.seuil_stock_alerte';
475if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
476 $sql .= ', pse.desiredstock';
477 $sql .= ', pse.seuil_stock_alerte';
478}
479$sql .= ', s.fk_product';
480
481if ($usevirtualstock) {
482 if (isModEnabled('order')) {
483 $sqlCommandesCli = "(SELECT ".$db->ifsql("SUM(cd1.qty) IS NULL", "0", "SUM(cd1.qty)")." as qty"; // We need the ifsql because if result is 0 for product p.rowid, we must return 0 and not NULL
484 $sqlCommandesCli .= " FROM ".MAIN_DB_PREFIX."commandedet as cd1, ".MAIN_DB_PREFIX."commande as c1";
485 $sqlCommandesCli .= " WHERE c1.rowid = cd1.fk_commande AND c1.entity IN (".getEntity(getDolGlobalString('STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE') ? 'stock' : 'commande').")";
486 $sqlCommandesCli .= " AND cd1.fk_product = p.rowid";
487 $sqlCommandesCli .= " AND c1.fk_statut IN (1,2))";
488 } else {
489 $sqlCommandesCli = '0';
490 }
491
492 if (isModEnabled("shipping")) {
493 $sqlExpeditionsCli = "(SELECT ".$db->ifsql("SUM(ed2.qty) IS NULL", "0", "SUM(ed2.qty)")." as qty"; // We need the ifsql because if result is 0 for product p.rowid, we must return 0 and not NULL
494 $sqlExpeditionsCli .= " FROM ".MAIN_DB_PREFIX."expedition as e2,";
495 $sqlExpeditionsCli .= " ".MAIN_DB_PREFIX."expeditiondet as ed2,";
496 $sqlExpeditionsCli .= " ".MAIN_DB_PREFIX."commande as c2,";
497 $sqlExpeditionsCli .= " ".MAIN_DB_PREFIX."commandedet as cd2";
498 $sqlExpeditionsCli .= " WHERE ed2.fk_expedition = e2.rowid AND cd2.rowid = ed2.fk_elementdet AND e2.entity IN (".getEntity(getDolGlobalString('STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE') ? 'stock' : 'expedition').")";
499 $sqlExpeditionsCli .= " AND cd2.fk_commande = c2.rowid";
500 $sqlExpeditionsCli .= " AND c2.fk_statut IN (1,2)";
501 $sqlExpeditionsCli .= " AND cd2.fk_product = p.rowid";
502 $sqlExpeditionsCli .= " AND e2.fk_statut IN (1,2))";
503 } else {
504 $sqlExpeditionsCli = '0';
505 }
506
507 if (isModEnabled("supplier_order")) {
508 $sqlCommandesFourn = "(SELECT " . $db->ifsql("SUM(cd3.qty) IS NULL", "0", "SUM(cd3.qty)") . " as qty"; // We need the ifsql because if result is 0 for product p.rowid, we must return 0 and not NULL
509 $sqlCommandesFourn .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd3,";
510 $sqlCommandesFourn .= " " . MAIN_DB_PREFIX . "commande_fournisseur as c3";
511 $sqlCommandesFourn .= " WHERE c3.rowid = cd3.fk_commande";
512 $sqlCommandesFourn .= " AND c3.entity IN (".getEntity(getDolGlobalString('STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE') ? 'stock' : 'supplier_order').")";
513 $sqlCommandesFourn .= " AND cd3.fk_product = p.rowid";
514 $sqlCommandesFourn .= " AND c3.fk_statut IN (3,4))";
515
516 $sqlReceptionFourn = "(SELECT ".$db->ifsql("SUM(fd4.qty) IS NULL", "0", "SUM(fd4.qty)")." as qty"; // We need the ifsql because if result is 0 for product p.rowid, we must return 0 and not NULL
517 $sqlReceptionFourn .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as cf4,";
518 $sqlReceptionFourn .= " ".MAIN_DB_PREFIX."receptiondet_batch as fd4";
519 $sqlReceptionFourn .= " WHERE fd4.fk_element = cf4.rowid AND cf4.entity IN (".getEntity(getDolGlobalString('STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE') ? 'stock' : 'supplier_order').")";
520 $sqlReceptionFourn .= " AND fd4.fk_product = p.rowid";
521 $sqlReceptionFourn .= " AND cf4.fk_statut IN (3,4))";
522 } else {
523 $sqlCommandesFourn = '0';
524 $sqlReceptionFourn = '0';
525 }
526
527 if (isModEnabled('mrp')) {
528 $sqlProductionToConsume = "(SELECT GREATEST(0, ".$db->ifsql("SUM(".$db->ifsql("mp5.role = 'toconsume'", 'mp5.qty', '- mp5.qty').") IS NULL", "0", "SUM(".$db->ifsql("mp5.role = 'toconsume'", 'mp5.qty', '- mp5.qty').")").") as qty"; // We need the ifsql because if result is 0 for product p.rowid, we must return 0 and not NULL
529 $sqlProductionToConsume .= " FROM ".MAIN_DB_PREFIX."mrp_mo as mm5,";
530 $sqlProductionToConsume .= " ".MAIN_DB_PREFIX."mrp_production as mp5";
531 $sqlProductionToConsume .= " WHERE mm5.rowid = mp5.fk_mo AND mm5.entity IN (".getEntity(getDolGlobalString('STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE') ? 'stock' : 'mo').")";
532 $sqlProductionToConsume .= " AND mp5.fk_product = p.rowid";
533 $sqlProductionToConsume .= " AND mp5.role IN ('toconsume', 'consumed')";
534 $sqlProductionToConsume .= " AND mm5.status IN (1,2))";
535
536 $sqlProductionToProduce = "(SELECT GREATEST(0, ".$db->ifsql("SUM(".$db->ifsql("mp5.role = 'toproduce'", 'mp5.qty', '- mp5.qty').") IS NULL", "0", "SUM(".$db->ifsql("mp5.role = 'toproduce'", 'mp5.qty', '- mp5.qty').")").") as qty"; // We need the ifsql because if result is 0 for product p.rowid, we must return 0 and not NULL
537 $sqlProductionToProduce .= " FROM ".MAIN_DB_PREFIX."mrp_mo as mm5,";
538 $sqlProductionToProduce .= " ".MAIN_DB_PREFIX."mrp_production as mp5";
539 $sqlProductionToProduce .= " WHERE mm5.rowid = mp5.fk_mo AND mm5.entity IN (".getEntity(getDolGlobalString('STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE') ? 'stock' : 'mo').")";
540 $sqlProductionToProduce .= " AND mp5.fk_product = p.rowid";
541 $sqlProductionToProduce .= " AND mp5.role IN ('toproduce', 'produced')";
542 $sqlProductionToProduce .= " AND mm5.status IN (1,2))";
543 } else {
544 $sqlProductionToConsume = '0';
545 $sqlProductionToProduce = '0';
546 }
547
548 $parameters = array();
549 $sqlHookVirtualStock = "0";
550 $reshook = $hookmanager->executeHooks('printFieldListHavingVirtualStock', $parameters); // Note that $action and $object may have been modified by hook
551 if (!empty($hookmanager->resPrint)) {
552 $sqlHookVirtualStock = $hookmanager->resPrint;
553 }
554
555 $sql .= ' HAVING (';
556 $sql .= " (" . $sqldesiredstock . " >= 0 AND (" . $sqldesiredstock . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ')';
557 $sql .= " - (" . $sqlCommandesCli . " - " . $sqlExpeditionsCli . ") + (" . $sqlCommandesFourn . " - " . $sqlReceptionFourn . ") + (" . $sqlProductionToProduce . " - " . $sqlProductionToConsume . ") + ".$sqlHookVirtualStock."))";
558 $sql .= ' OR';
559 if ($includeproductswithoutdesiredqty == 'on') {
560 $sql .= " ((" . $sqlalertstock . " >= 0 OR " . $sqlalertstock . " IS NULL) AND (" . $db->ifsql($sqlalertstock . " IS NULL", "0", $sqlalertstock) . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ")";
561 } else {
562 $sql .= " (" . $sqlalertstock . " >= 0 AND (" . $sqlalertstock . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ')';
563 }
564 $sql .= " - (" . $sqlCommandesCli . " - " . $sqlExpeditionsCli . ") + (" . $sqlCommandesFourn . " - " . $sqlReceptionFourn . ") + (" . $sqlProductionToProduce . " - " . $sqlProductionToConsume . ") + ".$sqlHookVirtualStock."))";
565 $sql .= ")";
566 if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
567 $sql .= " AND (";
568 $sql .= " pse.desiredstock > 0)";
569 }
570
571 if ($salert == 'on') { // Option to see when stock is lower than alert
572 $sql .= ' AND (';
573 if ($includeproductswithoutdesiredqty == 'on') {
574 $sql .= "(" . $sqlalertstock . " >= 0 OR " . $sqlalertstock . " IS NULL) AND (" . $db->ifsql($sqlalertstock . " IS NULL", "0", $sqlalertstock) . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ")";
575 } else {
576 $sql .= $sqlalertstock . " >= 0 AND (" . $sqlalertstock . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ")";
577 }
578 $sql .= " - (" . $sqlCommandesCli . " - " . $sqlExpeditionsCli . ") + (" . $sqlCommandesFourn . " - " . $sqlReceptionFourn . ") + (" . $sqlProductionToProduce . " - " . $sqlProductionToConsume . ") + ".$sqlHookVirtualStock.")";
579 $sql .= ")";
580 $alertchecked = 'checked';
581 }
582} else {
583 $sql .= ' HAVING (';
584 $sql .= "(" . $sqldesiredstock . " >= 0 AND (" . $sqldesiredstock . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ")))";
585 $sql .= ' OR';
586 if ($includeproductswithoutdesiredqty == 'on') {
587 $sql .= " ((" . $sqlalertstock . " >= 0 OR " . $sqlalertstock . " IS NULL) AND (" . $db->ifsql($sqlalertstock . " IS NULL", "0", $sqlalertstock) . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ')))';
588 } else {
589 $sql .= " (" . $sqlalertstock . " >= 0 AND (" . $sqlalertstock . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ')))';
590 }
591 $sql .= ')';
592 if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
593 $sql .= " AND (";
594 $sql .= " pse.desiredstock > 0)";
595 }
596
597 if ($salert == 'on') { // Option to see when stock is lower than alert
598 $sql .= " AND (";
599 if ($includeproductswithoutdesiredqty == 'on') {
600 $sql .= " (" . $sqlalertstock . " >= 0 OR " . $sqlalertstock . " IS NULL) AND (" . $db->ifsql($sqlalertstock . " IS NULL", "0", $sqlalertstock) . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . "))";
601 } else {
602 $sql .= " " . $sqlalertstock . " >= 0 AND (" . $sqlalertstock . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . '))';
603 }
604 $sql .= ')';
605 $alertchecked = 'checked';
606 }
607}
608
609$includeproductswithoutdesiredqtychecked = '';
610if ($includeproductswithoutdesiredqty == 'on') {
611 $includeproductswithoutdesiredqtychecked = 'checked';
612}
613
614$nbtotalofrecords = '';
615if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
616 $result = $db->query($sql);
617 $nbtotalofrecords = $db->num_rows($result);
618 if (($page * $limit) > (int) $nbtotalofrecords) {
619 $page = 0;
620 $offset = 0;
621 }
622}
623
624$sql .= $db->order($sortfield, $sortorder);
625$sql .= $db->plimit($limit + 1, $offset);
626
627//print $sql;
628$resql = $db->query($sql);
629if (empty($resql)) {
630 dol_print_error($db);
631 exit;
632}
633
634$num = $db->num_rows($resql);
635$i = 0;
636
637$helpurl = 'EN:Module_Stocks_En|FR:Module_Stock|';
638$helpurl .= 'ES:M&oacute;dulo_Stocks';
639
640llxHeader('', $title, $helpurl, '', 0, 0, '', '', '', 'mod-product page-stock_replenish');
641
642$head = array();
643
644$head[0][0] = DOL_URL_ROOT . '/product/stock/replenish.php';
645$head[0][1] = $title;
646$head[0][2] = 'replenish';
647
648$head[1][0] = DOL_URL_ROOT . '/product/stock/replenishorders.php';
649$head[1][1] = $langs->trans("ReplenishmentOrders");
650$head[1][2] = 'replenishorders';
651
652
653print load_fiche_titre($langs->trans('Replenishment'), '', 'stock');
654
655print dol_get_fiche_head($head, 'replenish', '', -1, '');
656
657print '<span class="opacitymedium">' . $langs->trans("ReplenishmentStatusDesc") . '</span>' . "\n";
658
659//$link = '<a title=' .$langs->trans("MenuNewWarehouse"). ' href="'.DOL_URL_ROOT.'/product/stock/card.php?action=create">'.$langs->trans("MenuNewWarehouse").'</a>';
660
661if (empty($fk_entrepot) && getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE')) {
662 print '<span class="opacitymedium">'.$langs->trans("ReplenishmentStatusDescPerWarehouse").'</span>'."\n";
663}
664print '<br><br>';
665if ($usevirtualstock == 1) {
666 print $langs->trans("CurentSelectionMode") . ': ';
667 print '<span class="a-mesure">' . $langs->trans("UseVirtualStock") . '</span>';
668 print ' <a class="a-mesure-disabled" href="' . $_SERVER["PHP_SELF"] . '?mode=physical' . ($fk_supplier > 0 ? '&fk_supplier=' . $fk_supplier : '') . ($fk_entrepot > 0 ? '&fk_entrepot=' . $fk_entrepot : '') . '">' . $langs->trans("UsePhysicalStock") . '</a>';
669 print '<br>';
670}
671if ($usevirtualstock == 0) {
672 print $langs->trans("CurentSelectionMode") . ': ';
673 print '<a class="a-mesure-disabled" href="' . $_SERVER["PHP_SELF"] . '?mode=virtual' . ($fk_supplier > 0 ? '&fk_supplier=' . $fk_supplier : '') . ($fk_entrepot > 0 ? '&fk_entrepot=' . $fk_entrepot : '') . '">' . $langs->trans("UseVirtualStock") . '</a>';
674 print ' <span class="a-mesure">' . $langs->trans("UsePhysicalStock") . '</span>';
675 print '<br>';
676}
677print '<br>' . "\n";
678
679print '<form name="formFilterWarehouse" method="POST" action="' . $_SERVER["PHP_SELF"] . '">';
680print '<input type="hidden" name="token" value="' . newToken() . '">';
681print '<input type="hidden" name="action" value="filter">';
682print '<input type="hidden" name="search_ref" value="' . $search_ref . '">';
683print '<input type="hidden" name="search_label" value="' . $search_label . '">';
684print '<input type="hidden" name="salert" value="' . $salert . '">';
685print '<input type="hidden" name="includeproductswithoutdesiredqty" value="' . $includeproductswithoutdesiredqty . '">';
686print '<input type="hidden" name="draftorder" value="' . $draftorder . '">';
687print '<input type="hidden" name="mode" value="' . $mode . '">';
688if ($limit > 0 && $limit != $conf->liste_limit) {
689 print '<input type="hidden" name="limit" value="' . $limit . '">';
690}
691if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE')) {
692 print '<div class="inline-block valignmiddle" style="padding-right: 20px;">';
693 print $langs->trans('Warehouse') . ' ' . $formproduct->selectWarehouses((int) $fk_entrepot, 'fk_entrepot', '', 1);
694 print '</div>';
695}
696print '<div class="inline-block valignmiddle" style="padding-right: 20px;">';
697$filter = '(fournisseur:=:1)';
698print $langs->trans('Supplier') . ' ' . $form->select_company($fk_supplier, 'fk_supplier', $filter, 1);
699print '</div>';
700
701$parameters = array();
702$reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook
703if (empty($reshook)) {
704 print $hookmanager->resPrint;
705}
706
707print '<div class="inline-block valignmiddle">';
708print '<input type="submit" class="button smallpaddingimp" name="valid" value="' . $langs->trans('ToFilter') . '">';
709print '</div>';
710
711print '</form>';
712
713print '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formulaire">';
714print '<input type="hidden" name="token" value="' . newToken() . '">';
715print '<input type="hidden" name="fk_supplier" value="' . $fk_supplier . '">';
716print '<input type="hidden" name="fk_entrepot" value="' . $fk_entrepot . '">';
717print '<input type="hidden" name="sortfield" value="' . $sortfield . '">';
718print '<input type="hidden" name="sortorder" value="' . $sortorder . '">';
719print '<input type="hidden" name="type" value="' . $type . '">';
720print '<input type="hidden" name="linecount" value="' . $num . '">';
721print '<input type="hidden" name="action" value="order">';
722print '<input type="hidden" name="mode" value="' . $mode . '">';
723
724
725if ($search_ref || $search_label || $sall || $salert || $draftorder || GETPOST('search', 'alpha')) {
726 $filters = '&search_ref=' . urlencode($search_ref) . '&search_label=' . urlencode($search_label);
727 $filters .= '&sall=' . urlencode($sall);
728 $filters .= '&salert=' . urlencode($salert);
729 $filters .= '&draftorder=' . urlencode($draftorder);
730 $filters .= '&mode=' . urlencode($mode);
731 if ($fk_supplier > 0) {
732 $filters .= '&fk_supplier='.urlencode((string) ($fk_supplier));
733 }
734 if ($fk_entrepot > 0) {
735 $filters .= '&fk_entrepot='.urlencode((string) ($fk_entrepot));
736 }
737} else {
738 $filters = '&search_ref='.urlencode($search_ref).'&search_label='.urlencode($search_label);
739 $filters .= '&fourn_id='.urlencode((string) ($fourn_id));
740 $filters .= (isset($type) ? '&type='.urlencode((string) ($type)) : '');
741 $filters .= '&salert='.urlencode($salert);
742 $filters .= '&draftorder='.urlencode($draftorder);
743 $filters .= '&mode='.urlencode($mode);
744 if ($fk_supplier > 0) {
745 $filters .= '&fk_supplier='.urlencode((string) ($fk_supplier));
746 }
747 if ($fk_entrepot > 0) {
748 $filters .= '&fk_entrepot='.urlencode((string) ($fk_entrepot));
749 }
750}
751if ($limit > 0 && $limit != $conf->liste_limit) {
752 $filters .= '&limit=' . ((int) $limit);
753}
754if (!empty($includeproductswithoutdesiredqty)) {
755 $filters .= '&includeproductswithoutdesiredqty='.urlencode($includeproductswithoutdesiredqty);
756}
757if (!empty($salert)) {
758 $filters .= '&salert='.urlencode($salert);
759}
760
761$param = (isset($type) ? '&type='.urlencode((string) ($type)) : '');
762$param .= '&fourn_id='.urlencode((string) ($fourn_id)).'&search_label='.urlencode((string) ($search_label)).'&includeproductswithoutdesiredqty='.urlencode((string) ($includeproductswithoutdesiredqty)).'&salert='.urlencode((string) ($salert)).'&draftorder='.urlencode((string) ($draftorder));
763$param .= '&search_ref='.urlencode($search_ref);
764$param .= '&mode='.urlencode($mode);
765$param .= '&fk_supplier='.urlencode((string) ($fk_supplier));
766$param .= '&fk_entrepot='.urlencode((string) ($fk_entrepot));
767if (!empty($includeproductswithoutdesiredqty)) {
768 $param .= '&includeproductswithoutdesiredqty='.urlencode($includeproductswithoutdesiredqty);
769}
770if (!empty($salert)) {
771 $param .= '&salert='.urlencode($salert);
772}
773
774$stocklabel = $langs->trans('Stock');
775$stocklabelbis = $langs->trans('Stock');
776$stocktooltip = '';
777if ($usevirtualstock == 1) {
778 $stocklabel = $langs->trans('VirtualStock');
779 $stocktooltip = $langs->trans("VirtualStockDesc");
780}
781if ($usevirtualstock == 0) {
782 $stocklabel = $langs->trans('PhysicalStock');
783}
784if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
785 $stocklabelbis = $stocklabel.' ('.$langs->trans('SelectedWarehouse').')';
786 $stocklabel .= ' ('.$langs->trans("AllWarehouses").')';
787}
788$texte = $langs->trans('Replenishment');
789
790print '<br>';
791
792
793if (getDolGlobalString('REPLENISH_ALLOW_VARIABLESIZELIST')) {
794 print_barre_liste(
795 $texte,
796 $page,
797 'replenish.php',
798 $filters,
799 $sortfield,
800 $sortorder,
801 '',
802 $num,
803 $nbtotalofrecords,
804 '',
805 0,
806 '',
807 '',
808 $limit
809 );
810} else {
811 print_barre_liste(
812 $texte,
813 $page,
814 'replenish.php',
815 $filters,
816 $sortfield,
817 $sortorder,
818 '',
819 $num,
820 $nbtotalofrecords,
821 ''
822 );
823}
824
825
826print '<div class="div-table-responsive-no-min">';
827print '<table class="liste centpercent">';
828
829// Fields title search
830print '<tr class="liste_titre_filter">';
831print '<td class="liste_titre">&nbsp;</td>';
832print '<td class="liste_titre"><input class="flat" type="text" name="search_ref" size="8" value="' . dol_escape_htmltag($search_ref) . '"></td>';
833print '<td class="liste_titre"><input class="flat" type="text" name="search_label" size="8" value="' . dol_escape_htmltag($search_label) . '"></td>';
834if (isModEnabled("service") && $type == 1) {
835 print '<td class="liste_titre">&nbsp;</td>';
836}
837print '<td class="liste_titre right">' . $form->textwithpicto($langs->trans('IncludeEmptyDesiredStock'), $langs->trans('IncludeProductWithUndefinedAlerts')) . '&nbsp;<input type="checkbox" id="includeproductswithoutdesiredqty" name="includeproductswithoutdesiredqty" ' . (!empty($includeproductswithoutdesiredqtychecked) ? $includeproductswithoutdesiredqtychecked : '') . '></td>';
838print '<td class="liste_titre right"></td>';
839print '<td class="liste_titre right">'.$langs->trans('AlertOnly').'&nbsp;<input type="checkbox" id="salert" name="salert" '.(!empty($alertchecked) ? $alertchecked : '').'></td>';
840if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
841 print '<td class="liste_titre">&nbsp;</td>';
842}
843print '<td class="liste_titre right">';
844if (getDolGlobalString('STOCK_REPLENISH_ADD_CHECKBOX_INCLUDE_DRAFT_ORDER')) {
845 print $langs->trans('IncludeAlsoDraftOrders').'&nbsp;<input type="checkbox" id="draftorder" name="draftorder" '.(!empty($draftchecked) ? $draftchecked : '').'>';
846}
847print '</td>';
848print '<td class="liste_titre">&nbsp;</td>';
849// Fields from hook
850$parameters = array('param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder);
851$reshook = $hookmanager->executeHooks('printFieldListOption', $parameters); // Note that $action and $object may have been modified by hook
852print $hookmanager->resPrint;
853
854print '<td class="liste_titre maxwidthsearch right">';
855$searchpicto = $form->showFilterAndCheckAddButtons(0);
856print $searchpicto;
857print '</td>';
858print '</tr>';
859
860// Lines of title
861print '<tr class="liste_titre">';
862print_liste_field_titre('<input type="checkbox" onClick="toggle(this)" />', $_SERVER["PHP_SELF"], '');
863print_liste_field_titre('ProductRef', $_SERVER["PHP_SELF"], 'p.ref', '', $param, '', $sortfield, $sortorder);
864print_liste_field_titre('Label', $_SERVER["PHP_SELF"], 'p.label', '', $param, '', $sortfield, $sortorder);
865if (isModEnabled("service") && $type == 1) {
866 print_liste_field_titre('Duration', $_SERVER["PHP_SELF"], 'p.duration', '', $param, '', $sortfield, $sortorder, 'center ');
867}
868print_liste_field_titre('DesiredStock', $_SERVER["PHP_SELF"], 'p.desiredstock', '', $param, '', $sortfield, $sortorder, 'right ');
869print_liste_field_titre('StockLimitShort', $_SERVER["PHP_SELF"], 'p.seuil_stock_alerte', '', $param, '', $sortfield, $sortorder, 'right ');
870print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], 'stock_physique', '', $param, '', $sortfield, $sortorder, 'right ', $stocktooltip);
871if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
872 print_liste_field_titre($stocklabelbis, $_SERVER["PHP_SELF"], 'stock_real_warehouse', '', $param, '', $sortfield, $sortorder, 'right ');
873}
874print_liste_field_titre('Ordered', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ');
875print_liste_field_titre('StockToBuy', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ');
876print_liste_field_titre('SupplierRef', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ');
877
878// Hook fields
879$parameters = array('param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder);
880$reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook
881print $hookmanager->resPrint;
882
883print "</tr>\n";
884
885while ($i < ($limit ? min($num, $limit) : $num)) {
886 $objp = $db->fetch_object($resql);
887
888 if (getDolGlobalString('STOCK_SUPPORTS_SERVICES') || $objp->fk_product_type == 0) {
889 $result = $prod->fetch($objp->rowid);
890 if ($result < 0) {
891 dol_print_error($db);
892 exit;
893 }
894
895 $prod->load_stock('warehouseopen, warehouseinternal'.(!$usevirtualstock ? ', novirtual' : ''), $draftchecked === 'checked' ? 1 : 0);
896
897 // Multilangs
898 if (getDolGlobalInt('MAIN_MULTILANGS')) {
899 $sql = 'SELECT label,description';
900 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'product_lang';
901 $sql .= ' WHERE fk_product = ' . ((int) $objp->rowid);
902 $sql .= " AND lang = '" . $db->escape($langs->getDefaultLang()) . "'";
903 $sql .= ' LIMIT 1';
904
905 $resqlm = $db->query($sql);
906 if ($resqlm) {
907 $objtp = $db->fetch_object($resqlm);
908 if (!empty($objtp->description)) {
909 $objp->description = $objtp->description;
910 }
911 if (!empty($objtp->label)) {
912 $objp->label = $objtp->label;
913 }
914 }
915 }
916
917 $stockwarehouse = 0;
918 if ($usevirtualstock) {
919 // If option to increase/decrease is not on an object validation, virtual stock may differs from physical stock.
920 $stock = $prod->stock_theorique;
921 //if conf active, stock virtual by warehouse is calculated
922 if (getDolGlobalString('STOCK_ALLOW_VIRTUAL_STOCK_PER_WAREHOUSE')) {
923 $stockwarehouse = $prod->stock_warehouse[(int) $fk_entrepot]->virtual;
924 }
925 } else {
926 $stock = $prod->stock_reel;
927 if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
928 $stockwarehouse = $prod->stock_warehouse[(int) $fk_entrepot]->real;
929 }
930 }
931
932 // Force call prod->load_stats_xxx to choose status to count (otherwise it is loaded by load_stock function)
933 $result = null;
934 if (isset($draftchecked)) {
935 $result = $prod->load_stats_commande_fournisseur(0, '0,1,2,3,4');
936 } elseif (!$usevirtualstock) {
937 $result = $prod->load_stats_commande_fournisseur(0, '1,2,3,4');
938 }
939
940 if (!$usevirtualstock) {
941 $result = $prod->load_stats_reception(0, '4');
942 }
943
944 //print $prod->stats_commande_fournisseur['qty'].'<br>'."\n";
945 //print $prod->stats_reception['qty'];
946 $ordered = $prod->stats_commande_fournisseur['qty'] - $prod->stats_reception['qty'];
947
948 $desiredstock = $objp->desiredstock;
949 $alertstock = $objp->seuil_stock_alerte;
950 $desiredstockwarehouse = (!empty($objp->desiredstockpse) ? $objp->desiredstockpse : 0);
951 $alertstockwarehouse = (!empty($objp->seuil_stock_alertepse) ? $objp->seuil_stock_alertepse : 0);
952
953 $warning = '';
954 if ($alertstock && ($stock < $alertstock)) {
955 $warning = img_warning($langs->trans('StockTooLow')) . ' ';
956 }
957 $warningwarehouse = '';
958 if ($alertstockwarehouse && ($stockwarehouse < $alertstockwarehouse)) {
959 $warningwarehouse = img_warning($langs->trans('StockTooLow')) . ' ';
960 }
961
962 //depending on conf, use either physical stock or
963 //virtual stock to compute the stock to buy value
964
965 if (empty($usevirtualstock)) {
966 $stocktobuy = max(max($desiredstock, $alertstock) - $stock - $ordered, 0);
967 $stocktobuywarehouse = max(max($desiredstockwarehouse, $alertstockwarehouse) - $stockwarehouse - $ordered, 0);
968 } else {
969 $stocktobuy = max(max($desiredstock, $alertstock) - $stock, 0); //ordered is already in $stock in virtual mode
970 $stocktobuywarehouse = max(max($desiredstockwarehouse, $alertstockwarehouse) - $stockwarehouse, 0); //ordered is already in $stock in virtual mode
971 }
972 $tobuy = ((getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) > 0 ? $stocktobuywarehouse : $stocktobuy);
973 $order_zero_message = '';
974 if ($tobuy <= 0) {
975 $order_zero_message = $langs->trans('ReplenishQuantityZeroExplanation');
976 }
977
978 $picto = '';
979 if ($ordered > 0) {
980 $stockforcompare = ($usevirtualstock ? $stock : $stock + $ordered);
981 /*if ($stockforcompare >= $desiredstock)
982 {
983 $picto = img_picto('', 'help');
984 } else {
985 $picto = img_picto('', 'help');
986 }*/
987 } else {
988 $picto = img_picto($langs->trans("NoPendingReceptionOnSupplierOrder"), 'help');
989 }
990
991 print '<tr class="oddeven" '.($tobuy <= 0 ? 'title="$order_zero_message"' : '').'>';
992
993 // Select field
994 print '<td><input type="checkbox" class="check" name="choose' . $i . '"></td>';
995
996 print '<td class="nowrap">' . $prod->getNomUrl(1, 'stock') . '</td>';
997
998 print '<td class="tdoverflowmax200" title="' . dol_escape_htmltag($objp->label) . '">';
999 print dol_escape_htmltag($objp->label);
1000 print '<input type="hidden" name="desc' . $i . '" value="' . dol_escape_htmltag($objp->description) . '">'; // TODO Remove this and make a fetch to get description when creating order instead of a GETPOST
1001 print '</td>';
1002
1003 if (isModEnabled("service") && $type == 1) {
1004 $regs = array();
1005 if (preg_match('/([0-9]+)y/i', $objp->duration, $regs)) {
1006 $duration = $regs[1] . ' ' . $langs->trans('DurationYear');
1007 } elseif (preg_match('/([0-9]+)m/i', $objp->duration, $regs)) {
1008 $duration = $regs[1] . ' ' . $langs->trans('DurationMonth');
1009 } elseif (preg_match('/([0-9]+)d/i', $objp->duration, $regs)) {
1010 $duration = $regs[1] . ' ' . $langs->trans('DurationDay');
1011 } else {
1012 $duration = $objp->duration;
1013 }
1014 print '<td class="center">' . $duration . '</td>';
1015 }
1016
1017 // Desired stock
1018 print '<td class="right">'.((getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) > 0 ? ($objp->desiredstockpse ? $desiredstockwarehouse : img_info($langs->trans('ProductValuesUsedBecauseNoValuesForThisWarehouse')) . '0') : $desiredstock).'</td>';
1019
1020 // Limit stock for alert
1021 print '<td class="right">'.((getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) > 0 ? ($objp->seuil_stock_alertepse ? $alertstockwarehouse : img_info($langs->trans('ProductValuesUsedBecauseNoValuesForThisWarehouse')) . '0') : $alertstock).'</td>';
1022
1023 // Current stock (all warehouses)
1024 print '<td class="right">' . $warning . price(price2num($stock, 'MS'));
1025 print '<!-- stock returned by main sql is ' . $objp->stock_physique . ' -->';
1026 print '</td>';
1027
1028 // Current stock (warehouse selected only)
1029 if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
1030 print '<td class="right">'.$warningwarehouse.$stockwarehouse.'</td>';
1031 }
1032
1033 // Already ordered
1034 print '<td class="right"><a href="replenishorders.php?search_product=' . $prod->id . '">' . $ordered . '</a> ' . $picto . '</td>';
1035
1036 // To order
1037 print '<td class="right">';
1038 $input_html = '<input type="text" size="4" name="tobuy'.$i.'" value="'.$tobuy.'">';
1039 print ($tobuy <= 0) ? $form->textwithpicto($input_html, $order_zero_message, -1) : $input_html;
1040 print '</td>';
1041
1042 // Supplier
1043 print '<td class="right">';
1044 print $form->select_product_fourn_price($prod->id, 'fourn' . $i, $fk_supplier);
1045 print '</td>';
1046
1047 // Fields from hook
1048 $parameters = array('objp' => $objp, 'i' => $i, 'tobuy' => $tobuy);
1049 $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters); // Note that $action and $object may have been modified by hook
1050 print $hookmanager->resPrint;
1051
1052 print '</tr>';
1053 }
1054 $i++;
1055}
1056
1057if ($num == 0) {
1058 $colspan = 9;
1059 if (isModEnabled("service") && $type == 1) {
1060 $colspan++;
1061 }
1062 if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
1063 $colspan++;
1064 }
1065 print '<tr><td colspan="' . $colspan . '">';
1066 print '<span class="opacitymedium">';
1067 print $langs->trans("None");
1068 print '</span>';
1069 print '</td></tr>';
1070}
1071
1072$parameters = array('sql' => $sql);
1073$reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters); // Note that $action and $object may have been modified by hook
1074print $hookmanager->resPrint;
1075
1076print '</table>';
1077print '</div>';
1078
1079$db->free($resql);
1080
1081print dol_get_fiche_end();
1082
1083
1084$value = $langs->trans("CreateOrders");
1085print '<div class="center"><input type="submit" class="button" name="valid" value="' . $value . '"></div>';
1086
1087
1088print '</form>';
1089
1090
1091// TODO Replace this with jquery
1092print '
1093<script type="text/javascript">
1094function toggle(source)
1095{
1096 checkboxes = document.getElementsByClassName("check");
1097 for (var i=0; i < checkboxes.length;i++) {
1098 if (!checkboxes[i].disabled) {
1099 checkboxes[i].checked = source.checked;
1100 }
1101 }
1102}
1103</script>';
1104
1105
1106llxFooter();
1107
1108$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 predefined suppliers products.
Class to manage line orders.
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 predefined suppliers products.
Class to manage products or services.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
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.
natural_search($fields, $value, $mode=0, $nofirstand=0, $sqltoadd='')
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
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, $nodefault=0)
Return value of a param into GET or POST supervariable.
GETPOSTFLOAT($paramname, $rounding='', $option=2)
Return the value of a $_GET or $_POST supervariable, converted into float.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
print_liste_field_titre($name, $file="", $field="", $begin="", $param="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
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.
Definition html.lib.php:540
dol_get_fiche_end($notab=0)
Return tab footer of a card.
Definition html.lib.php:738
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Definition html.lib.php:181
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $nodie=0, $mode='')
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.