dolibarr 24.0.1
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();
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 }
324 if ($result < 0) {
325 $fail++;
326 $msg = $langs->trans('OrderFail') . "&nbsp;:&nbsp;";
327 $msg .= $order->error;
328 setEventMessages($msg, null, 'errors');
329 } else {
330 $id = $result;
331 }
332 $i++;
333 } else {
334 $order->socid = $suppliersid[$i];
335 $order->fetch_thirdparty();
336 $order->multicurrency_code = $order->thirdparty->multicurrency_code;
337
338 // Trick to know which orders have been generated using the replenishment feature
339 $order->source = $order::SOURCE_ID_REPLENISHMENT;
340
341 foreach ($supplier['lines'] as $line) {
342 if (empty($line->remise_percent)) {
343 $line->remise_percent = (float) $order->thirdparty->remise_supplier_percent;
344 }
345 $order->lines[] = $line;
346 }
347 $order->cond_reglement_id = (int) $order->thirdparty->cond_reglement_supplier_id;
348 $order->mode_reglement_id = (int) $order->thirdparty->mode_reglement_supplier_id;
349
350 $id = $order->create($user);
351 if ($id < 0) {
352 $fail++;
353 $msg = $langs->trans('OrderFail') . "&nbsp;:&nbsp;";
354 $msg .= $order->error;
355 setEventMessages($msg, null, 'errors');
356 }
357 $i++;
358 }
359 }
360
361 if ($errorQty) {
362 setEventMessages($langs->trans('ErrorOrdersNotCreatedQtyTooLow'), null, 'warnings');
363 }
364
365 if (!$fail && $id) {
366 $db->commit();
367
368 setEventMessages($langs->trans('OrderCreated'), null, 'mesgs');
369 header('Location: replenishorders.php');
370 exit;
371 } else {
372 $db->rollback();
373 }
374 }
375 if ($box == 0) {
376 setEventMessages($langs->trans('SelectProductWithNotNullQty'), null, 'warnings');
377 }
378}
379
380
381/*
382 * View
383 */
384
385$form = new Form($db);
386$formproduct = new FormProduct($db);
387$prod = new Product($db);
388
389$title = $langs->trans('MissingStocks');
390
391if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
392 $sqldesiredstock = $db->ifsql("pse.desiredstock IS NULL", "p.desiredstock", "pse.desiredstock");
393 $sqlalertstock = $db->ifsql("pse.seuil_stock_alerte IS NULL", "p.seuil_stock_alerte", "pse.seuil_stock_alerte");
394} else {
395 $sqldesiredstock = 'p.desiredstock';
396 $sqlalertstock = 'p.seuil_stock_alerte';
397}
398
399$sql = 'SELECT p.rowid, p.ref, p.label, p.description, p.price,';
400$sql .= ' p.price_ttc, p.price_base_type, p.fk_product_type,';
401$sql .= ' p.tms as datem, p.duration, p.tobuy,';
402$sql .= ' p.desiredstock, p.seuil_stock_alerte,';
403if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
404 $sql .= ' pse.desiredstock as desiredstockpse, pse.seuil_stock_alerte as seuil_stock_alertepse,';
405}
406$sql .= " " . $sqldesiredstock . " as desiredstockcombined, " . $sqlalertstock . " as seuil_stock_alertecombined,";
407$sql .= ' s.fk_product,';
408$sql .= " SUM(".$db->ifsql("s.reel IS NULL", "0", "s.reel").') as stock_physique';
409if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
410 $sql .= ", SUM(".$db->ifsql("s.reel IS NULL OR s.fk_entrepot <> ".((int) $fk_entrepot), "0", "s.reel").') as stock_real_warehouse';
411}
412
413// Add fields from hooks
414$parameters = array();
415$reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters); // Note that $action and $object may have been modified by hook
416$sql .= $hookmanager->resPrint;
417
418$list_warehouse = (empty($listofqualifiedwarehousesid) ? '0' : $listofqualifiedwarehousesid);
419
420$sql .= ' FROM ' . MAIN_DB_PREFIX . 'product as p';
421$sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product_stock as s ON p.rowid = s.fk_product';
422$sql .= ' AND s.fk_entrepot IN (' . $db->sanitize($list_warehouse) . ')';
423
424$list_warehouse_selected = ($fk_entrepot < 0 || empty($fk_entrepot)) ? $list_warehouse : $fk_entrepot;
425$sql .= ' AND s.fk_entrepot IN (' . $db->sanitize($list_warehouse_selected) . ')';
426
427
428//$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'entrepot AS ent ON s.fk_entrepot = ent.rowid AND ent.entity IN('.getEntity('stock').')';
429if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
430 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_warehouse_properties AS pse ON (p.rowid = pse.fk_product AND pse.fk_entrepot = '.((int) $fk_entrepot).')';
431}
432// Add fields from hooks
433$parameters = array();
434$reshook = $hookmanager->executeHooks('printFieldListJoin', $parameters); // Note that $action and $object may have been modified by hook
435$sql .= $hookmanager->resPrint;
436
437$sql .= ' WHERE p.entity IN (' . getEntity('product') . ')';
438if ($sall) {
439 $sql .= natural_search(array('p.ref', 'p.label', 'p.description', 'p.note'), $sall);
440}
441// if the type is not 1, we show all products (type = 0,2,3)
442if (dol_strlen((string) $type)) {
443 if ($type == 1) {
444 $sql .= ' AND p.fk_product_type = 1';
445 } else {
446 $sql .= ' AND p.fk_product_type <> 1';
447 }
448}
449if ($search_ref) {
450 $sql .= natural_search('p.ref', $search_ref);
451}
452if ($search_label) {
453 $sql .= natural_search('p.label', $search_label);
454}
455$sql .= ' AND p.tobuy = 1';
456if (isModEnabled('variants') && !getDolGlobalString('VARIANT_ALLOW_STOCK_MOVEMENT_ON_VARIANT_PARENT')) { // Add test to exclude products that has variants
457 $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').'))';
458}
459if ($fk_supplier > 0) {
460 $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') . '))';
461}
462// Add where from hooks
463$parameters = array();
464$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
465$sql .= $hookmanager->resPrint;
466
467$sql .= ' GROUP BY p.rowid, p.ref, p.label, p.description, p.price';
468$sql .= ', p.price_ttc, p.price_base_type,p.fk_product_type, p.tms';
469$sql .= ', p.duration, p.tobuy';
470$sql .= ', p.desiredstock';
471$sql .= ', p.seuil_stock_alerte';
472if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
473 $sql .= ', pse.desiredstock';
474 $sql .= ', pse.seuil_stock_alerte';
475}
476$sql .= ', s.fk_product';
477
478if ($usevirtualstock) {
479 if (isModEnabled('order')) {
480 $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
481 $sqlCommandesCli .= " FROM ".MAIN_DB_PREFIX."commandedet as cd1, ".MAIN_DB_PREFIX."commande as c1";
482 $sqlCommandesCli .= " WHERE c1.rowid = cd1.fk_commande AND c1.entity IN (".getEntity(getDolGlobalString('STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE') ? 'stock' : 'commande').")";
483 $sqlCommandesCli .= " AND cd1.fk_product = p.rowid";
484 $sqlCommandesCli .= " AND c1.fk_statut IN (1,2))";
485 } else {
486 $sqlCommandesCli = '0';
487 }
488
489 if (isModEnabled("shipping")) {
490 $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
491 $sqlExpeditionsCli .= " FROM ".MAIN_DB_PREFIX."expedition as e2,";
492 $sqlExpeditionsCli .= " ".MAIN_DB_PREFIX."expeditiondet as ed2,";
493 $sqlExpeditionsCli .= " ".MAIN_DB_PREFIX."commande as c2,";
494 $sqlExpeditionsCli .= " ".MAIN_DB_PREFIX."commandedet as cd2";
495 $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').")";
496 $sqlExpeditionsCli .= " AND cd2.fk_commande = c2.rowid";
497 $sqlExpeditionsCli .= " AND c2.fk_statut IN (1,2)";
498 $sqlExpeditionsCli .= " AND cd2.fk_product = p.rowid";
499 $sqlExpeditionsCli .= " AND e2.fk_statut IN (1,2))";
500 } else {
501 $sqlExpeditionsCli = '0';
502 }
503
504 if (isModEnabled("supplier_order")) {
505 $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
506 $sqlCommandesFourn .= " FROM " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd3,";
507 $sqlCommandesFourn .= " " . MAIN_DB_PREFIX . "commande_fournisseur as c3";
508 $sqlCommandesFourn .= " WHERE c3.rowid = cd3.fk_commande";
509 $sqlCommandesFourn .= " AND c3.entity IN (".getEntity(getDolGlobalString('STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE') ? 'stock' : 'supplier_order').")";
510 $sqlCommandesFourn .= " AND cd3.fk_product = p.rowid";
511 $sqlCommandesFourn .= " AND c3.fk_statut IN (3,4))";
512
513 $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
514 $sqlReceptionFourn .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as cf4,";
515 $sqlReceptionFourn .= " ".MAIN_DB_PREFIX."receptiondet_batch as fd4";
516 $sqlReceptionFourn .= " WHERE fd4.fk_element = cf4.rowid AND cf4.entity IN (".getEntity(getDolGlobalString('STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE') ? 'stock' : 'supplier_order').")";
517 $sqlReceptionFourn .= " AND fd4.fk_product = p.rowid";
518 $sqlReceptionFourn .= " AND cf4.fk_statut IN (3,4))";
519 } else {
520 $sqlCommandesFourn = '0';
521 $sqlReceptionFourn = '0';
522 }
523
524 if (isModEnabled('mrp')) {
525 $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
526 $sqlProductionToConsume .= " FROM ".MAIN_DB_PREFIX."mrp_mo as mm5,";
527 $sqlProductionToConsume .= " ".MAIN_DB_PREFIX."mrp_production as mp5";
528 $sqlProductionToConsume .= " WHERE mm5.rowid = mp5.fk_mo AND mm5.entity IN (".getEntity(getDolGlobalString('STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE') ? 'stock' : 'mo').")";
529 $sqlProductionToConsume .= " AND mp5.fk_product = p.rowid";
530 $sqlProductionToConsume .= " AND mp5.role IN ('toconsume', 'consumed')";
531 $sqlProductionToConsume .= " AND mm5.status IN (1,2))";
532
533 $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
534 $sqlProductionToProduce .= " FROM ".MAIN_DB_PREFIX."mrp_mo as mm5,";
535 $sqlProductionToProduce .= " ".MAIN_DB_PREFIX."mrp_production as mp5";
536 $sqlProductionToProduce .= " WHERE mm5.rowid = mp5.fk_mo AND mm5.entity IN (".getEntity(getDolGlobalString('STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE') ? 'stock' : 'mo').")";
537 $sqlProductionToProduce .= " AND mp5.fk_product = p.rowid";
538 $sqlProductionToProduce .= " AND mp5.role IN ('toproduce', 'produced')";
539 $sqlProductionToProduce .= " AND mm5.status IN (1,2))";
540 } else {
541 $sqlProductionToConsume = '0';
542 $sqlProductionToProduce = '0';
543 }
544
545 $parameters = array();
546 $sqlHookVirtualStock = "0";
547 $reshook = $hookmanager->executeHooks('printFieldListHavingVirtualStock', $parameters); // Note that $action and $object may have been modified by hook
548 if (!empty($hookmanager->resPrint)) {
549 $sqlHookVirtualStock = $hookmanager->resPrint;
550 }
551
552 $sql .= ' HAVING (';
553 $sql .= " (" . $sqldesiredstock . " >= 0 AND (" . $sqldesiredstock . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ')';
554 $sql .= " - (" . $sqlCommandesCli . " - " . $sqlExpeditionsCli . ") + (" . $sqlCommandesFourn . " - " . $sqlReceptionFourn . ") + (" . $sqlProductionToProduce . " - " . $sqlProductionToConsume . ") + ".$sqlHookVirtualStock."))";
555 $sql .= ' OR';
556 if ($includeproductswithoutdesiredqty == 'on') {
557 $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") . ")";
558 } else {
559 $sql .= " (" . $sqlalertstock . " >= 0 AND (" . $sqlalertstock . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ')';
560 }
561 $sql .= " - (" . $sqlCommandesCli . " - " . $sqlExpeditionsCli . ") + (" . $sqlCommandesFourn . " - " . $sqlReceptionFourn . ") + (" . $sqlProductionToProduce . " - " . $sqlProductionToConsume . ") + ".$sqlHookVirtualStock."))";
562 $sql .= ")";
563 if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
564 $sql .= " AND (";
565 $sql .= " pse.desiredstock > 0)";
566 }
567
568 if ($salert == 'on') { // Option to see when stock is lower than alert
569 $sql .= ' AND (';
570 if ($includeproductswithoutdesiredqty == 'on') {
571 $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") . ")";
572 } else {
573 $sql .= $sqlalertstock . " >= 0 AND (" . $sqlalertstock . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ")";
574 }
575 $sql .= " - (" . $sqlCommandesCli . " - " . $sqlExpeditionsCli . ") + (" . $sqlCommandesFourn . " - " . $sqlReceptionFourn . ") + (" . $sqlProductionToProduce . " - " . $sqlProductionToConsume . ") + ".$sqlHookVirtualStock.")";
576 $sql .= ")";
577 $alertchecked = 'checked';
578 }
579} else {
580 $sql .= ' HAVING (';
581 $sql .= "(" . $sqldesiredstock . " >= 0 AND (" . $sqldesiredstock . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ")))";
582 $sql .= ' OR';
583 if ($includeproductswithoutdesiredqty == 'on') {
584 $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") . ')))';
585 } else {
586 $sql .= " (" . $sqlalertstock . " >= 0 AND (" . $sqlalertstock . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . ')))';
587 }
588 $sql .= ')';
589 if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
590 $sql .= " AND (";
591 $sql .= " pse.desiredstock > 0)";
592 }
593
594 if ($salert == 'on') { // Option to see when stock is lower than alert
595 $sql .= " AND (";
596 if ($includeproductswithoutdesiredqty == 'on') {
597 $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") . "))";
598 } else {
599 $sql .= " " . $sqlalertstock . " >= 0 AND (" . $sqlalertstock . " > SUM(" . $db->ifsql("s.reel IS NULL", "0", "s.reel") . '))';
600 }
601 $sql .= ')';
602 $alertchecked = 'checked';
603 }
604}
605
606$includeproductswithoutdesiredqtychecked = '';
607if ($includeproductswithoutdesiredqty == 'on') {
608 $includeproductswithoutdesiredqtychecked = 'checked';
609}
610
611$nbtotalofrecords = '';
612if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
613 $result = $db->query($sql);
614 $nbtotalofrecords = $db->num_rows($result);
615 if (($page * $limit) > (int) $nbtotalofrecords) {
616 $page = 0;
617 $offset = 0;
618 }
619}
620
621$sql .= $db->order($sortfield, $sortorder);
622$sql .= $db->plimit($limit + 1, $offset);
623
624//print $sql;
625$resql = $db->query($sql);
626if (empty($resql)) {
628 exit;
629}
630
631$num = $db->num_rows($resql);
632$i = 0;
633
634$helpurl = 'EN:Module_Stocks_En|FR:Module_Stock|';
635$helpurl .= 'ES:M&oacute;dulo_Stocks';
636
637llxHeader('', $title, $helpurl, '', 0, 0, '', '', '', 'mod-product page-stock_replenish');
638
639$head = array();
640
641$head[0][0] = DOL_URL_ROOT . '/product/stock/replenish.php';
642$head[0][1] = $title;
643$head[0][2] = 'replenish';
644
645$head[1][0] = DOL_URL_ROOT . '/product/stock/replenishorders.php';
646$head[1][1] = $langs->trans("ReplenishmentOrders");
647$head[1][2] = 'replenishorders';
648
649
650print load_fiche_titre($langs->trans('Replenishment'), '', 'stock');
651
652print dol_get_fiche_head($head, 'replenish', '', -1, '');
653
654print '<span class="opacitymedium">' . $langs->trans("ReplenishmentStatusDesc") . '</span>' . "\n";
655
656//$link = '<a title=' .$langs->trans("MenuNewWarehouse"). ' href="'.DOL_URL_ROOT.'/product/stock/card.php?action=create">'.$langs->trans("MenuNewWarehouse").'</a>';
657
658if (empty($fk_entrepot) && getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE')) {
659 print '<span class="opacitymedium">'.$langs->trans("ReplenishmentStatusDescPerWarehouse").'</span>'."\n";
660}
661print '<br><br>';
662if ($usevirtualstock == 1) {
663 print $langs->trans("CurentSelectionMode") . ': ';
664 print '<span class="a-mesure">' . $langs->trans("UseVirtualStock") . '</span>';
665 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>';
666 print '<br>';
667}
668if ($usevirtualstock == 0) {
669 print $langs->trans("CurentSelectionMode") . ': ';
670 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>';
671 print ' <span class="a-mesure">' . $langs->trans("UsePhysicalStock") . '</span>';
672 print '<br>';
673}
674print '<br>' . "\n";
675
676print '<form name="formFilterWarehouse" method="POST" action="' . $_SERVER["PHP_SELF"] . '">';
677print '<input type="hidden" name="token" value="' . newToken() . '">';
678print '<input type="hidden" name="action" value="filter">';
679print '<input type="hidden" name="search_ref" value="' . $search_ref . '">';
680print '<input type="hidden" name="search_label" value="' . $search_label . '">';
681print '<input type="hidden" name="salert" value="' . $salert . '">';
682print '<input type="hidden" name="includeproductswithoutdesiredqty" value="' . $includeproductswithoutdesiredqty . '">';
683print '<input type="hidden" name="draftorder" value="' . $draftorder . '">';
684print '<input type="hidden" name="mode" value="' . $mode . '">';
685if ($limit > 0 && $limit != $conf->liste_limit) {
686 print '<input type="hidden" name="limit" value="' . $limit . '">';
687}
688if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE')) {
689 print '<div class="inline-block valignmiddle" style="padding-right: 20px;">';
690 print $langs->trans('Warehouse') . ' ' . $formproduct->selectWarehouses((int) $fk_entrepot, 'fk_entrepot', '', 1);
691 print '</div>';
692}
693print '<div class="inline-block valignmiddle" style="padding-right: 20px;">';
694$filter = '(fournisseur:=:1)';
695print $langs->trans('Supplier') . ' ' . $form->select_company($fk_supplier, 'fk_supplier', $filter, 1);
696print '</div>';
697
698$parameters = array();
699$reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook
700if (empty($reshook)) {
701 print $hookmanager->resPrint;
702}
703
704print '<div class="inline-block valignmiddle">';
705print '<input type="submit" class="button smallpaddingimp" name="valid" value="' . $langs->trans('ToFilter') . '">';
706print '</div>';
707
708print '</form>';
709
710print '<form action="' . $_SERVER["PHP_SELF"] . '" method="POST" name="formulaire">';
711print '<input type="hidden" name="token" value="' . newToken() . '">';
712print '<input type="hidden" name="fk_supplier" value="' . $fk_supplier . '">';
713print '<input type="hidden" name="fk_entrepot" value="' . $fk_entrepot . '">';
714print '<input type="hidden" name="sortfield" value="' . $sortfield . '">';
715print '<input type="hidden" name="sortorder" value="' . $sortorder . '">';
716print '<input type="hidden" name="type" value="' . $type . '">';
717print '<input type="hidden" name="linecount" value="' . $num . '">';
718print '<input type="hidden" name="action" value="order">';
719print '<input type="hidden" name="mode" value="' . $mode . '">';
720
721
722if ($search_ref || $search_label || $sall || $salert || $draftorder || GETPOST('search', 'alpha')) {
723 $filters = '&search_ref=' . urlencode($search_ref) . '&search_label=' . urlencode($search_label);
724 $filters .= '&sall=' . urlencode($sall);
725 $filters .= '&salert=' . urlencode($salert);
726 $filters .= '&draftorder=' . urlencode($draftorder);
727 $filters .= '&mode=' . urlencode($mode);
728 if ($fk_supplier > 0) {
729 $filters .= '&fk_supplier='.urlencode((string) ($fk_supplier));
730 }
731 if ($fk_entrepot > 0) {
732 $filters .= '&fk_entrepot='.urlencode((string) ($fk_entrepot));
733 }
734} else {
735 $filters = '&search_ref='.urlencode($search_ref).'&search_label='.urlencode($search_label);
736 $filters .= '&fourn_id='.urlencode((string) ($fourn_id));
737 $filters .= (isset($type) ? '&type='.urlencode((string) ($type)) : '');
738 $filters .= '&salert='.urlencode($salert);
739 $filters .= '&draftorder='.urlencode($draftorder);
740 $filters .= '&mode='.urlencode($mode);
741 if ($fk_supplier > 0) {
742 $filters .= '&fk_supplier='.urlencode((string) ($fk_supplier));
743 }
744 if ($fk_entrepot > 0) {
745 $filters .= '&fk_entrepot='.urlencode((string) ($fk_entrepot));
746 }
747}
748if ($limit > 0 && $limit != $conf->liste_limit) {
749 $filters .= '&limit=' . ((int) $limit);
750}
751if (!empty($includeproductswithoutdesiredqty)) {
752 $filters .= '&includeproductswithoutdesiredqty='.urlencode($includeproductswithoutdesiredqty);
753}
754if (!empty($salert)) {
755 $filters .= '&salert='.urlencode($salert);
756}
757
758$param = (isset($type) ? '&type='.urlencode((string) ($type)) : '');
759$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));
760$param .= '&search_ref='.urlencode($search_ref);
761$param .= '&mode='.urlencode($mode);
762$param .= '&fk_supplier='.urlencode((string) ($fk_supplier));
763$param .= '&fk_entrepot='.urlencode((string) ($fk_entrepot));
764if (!empty($includeproductswithoutdesiredqty)) {
765 $param .= '&includeproductswithoutdesiredqty='.urlencode($includeproductswithoutdesiredqty);
766}
767if (!empty($salert)) {
768 $param .= '&salert='.urlencode($salert);
769}
770
771$stocklabel = $langs->trans('Stock');
772$stocklabelbis = $langs->trans('Stock');
773$stocktooltip = '';
774if ($usevirtualstock == 1) {
775 $stocklabel = $langs->trans('VirtualStock');
776 $stocktooltip = $langs->trans("VirtualStockDesc");
777}
778if ($usevirtualstock == 0) {
779 $stocklabel = $langs->trans('PhysicalStock');
780}
781if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
782 $stocklabelbis = $stocklabel.' ('.$langs->trans('SelectedWarehouse').')';
783 $stocklabel .= ' ('.$langs->trans("AllWarehouses").')';
784}
785$texte = $langs->trans('Replenishment');
786
787print '<br>';
788
789
790if (getDolGlobalString('REPLENISH_ALLOW_VARIABLESIZELIST')) {
792 $texte,
793 $page,
794 'replenish.php',
795 $filters,
796 $sortfield,
797 $sortorder,
798 '',
799 $num,
800 $nbtotalofrecords,
801 '',
802 0,
803 '',
804 '',
805 $limit
806 );
807} else {
809 $texte,
810 $page,
811 'replenish.php',
812 $filters,
813 $sortfield,
814 $sortorder,
815 '',
816 $num,
817 $nbtotalofrecords,
818 ''
819 );
820}
821
822
823print '<div class="div-table-responsive-no-min">';
824print '<table class="liste centpercent">';
825
826// Fields title search
827print '<tr class="liste_titre_filter">';
828print '<td class="liste_titre">&nbsp;</td>';
829print '<td class="liste_titre"><input class="flat" type="text" name="search_ref" size="8" value="' . dol_escape_htmltag($search_ref) . '"></td>';
830print '<td class="liste_titre"><input class="flat" type="text" name="search_label" size="8" value="' . dol_escape_htmltag($search_label) . '"></td>';
831if (isModEnabled("service") && $type == 1) {
832 print '<td class="liste_titre">&nbsp;</td>';
833}
834print '<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>';
835print '<td class="liste_titre right"></td>';
836print '<td class="liste_titre right">'.$langs->trans('AlertOnly').'&nbsp;<input type="checkbox" id="salert" name="salert" '.(!empty($alertchecked) ? $alertchecked : '').'></td>';
837if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
838 print '<td class="liste_titre">&nbsp;</td>';
839}
840print '<td class="liste_titre right">';
841if (getDolGlobalString('STOCK_REPLENISH_ADD_CHECKBOX_INCLUDE_DRAFT_ORDER')) {
842 print $langs->trans('IncludeAlsoDraftOrders').'&nbsp;<input type="checkbox" id="draftorder" name="draftorder" '.(!empty($draftchecked) ? $draftchecked : '').'>';
843}
844print '</td>';
845print '<td class="liste_titre">&nbsp;</td>';
846// Fields from hook
847$parameters = array('param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder);
848$reshook = $hookmanager->executeHooks('printFieldListOption', $parameters); // Note that $action and $object may have been modified by hook
849print $hookmanager->resPrint;
850
851print '<td class="liste_titre maxwidthsearch right">';
852$searchpicto = $form->showFilterAndCheckAddButtons(0);
853print $searchpicto;
854print '</td>';
855print '</tr>';
856
857// Lines of title
858print '<tr class="liste_titre">';
859print_liste_field_titre('<input type="checkbox" onClick="toggle(this)" />', $_SERVER["PHP_SELF"], '');
860print_liste_field_titre('ProductRef', $_SERVER["PHP_SELF"], 'p.ref', '', $param, '', $sortfield, $sortorder);
861print_liste_field_titre('Label', $_SERVER["PHP_SELF"], 'p.label', '', $param, '', $sortfield, $sortorder);
862if (isModEnabled("service") && $type == 1) {
863 print_liste_field_titre('Duration', $_SERVER["PHP_SELF"], 'p.duration', '', $param, '', $sortfield, $sortorder, 'center ');
864}
865print_liste_field_titre('DesiredStock', $_SERVER["PHP_SELF"], 'p.desiredstock', '', $param, '', $sortfield, $sortorder, 'right ');
866print_liste_field_titre('StockLimitShort', $_SERVER["PHP_SELF"], 'p.seuil_stock_alerte', '', $param, '', $sortfield, $sortorder, 'right ');
867print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], 'stock_physique', '', $param, '', $sortfield, $sortorder, 'right ', $stocktooltip);
868if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
869 print_liste_field_titre($stocklabelbis, $_SERVER["PHP_SELF"], 'stock_real_warehouse', '', $param, '', $sortfield, $sortorder, 'right ');
870}
871print_liste_field_titre('Ordered', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ');
872print_liste_field_titre('StockToBuy', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ');
873print_liste_field_titre('SupplierRef', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ');
874
875// Hook fields
876$parameters = array('param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder);
877$reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook
878print $hookmanager->resPrint;
879
880print "</tr>\n";
881
882while ($i < ($limit ? min($num, $limit) : $num)) {
883 $objp = $db->fetch_object($resql);
884
885 if (getDolGlobalString('STOCK_SUPPORTS_SERVICES') || $objp->fk_product_type == 0) {
886 $result = $prod->fetch($objp->rowid);
887 if ($result < 0) {
889 exit;
890 }
891
892 $prod->load_stock('warehouseopen, warehouseinternal'.(!$usevirtualstock ? ', novirtual' : ''), $draftchecked === 'checked' ? 1 : 0);
893
894 // Multilangs
895 if (getDolGlobalInt('MAIN_MULTILANGS')) {
896 $sql = 'SELECT label,description';
897 $sql .= ' FROM ' . MAIN_DB_PREFIX . 'product_lang';
898 $sql .= ' WHERE fk_product = ' . ((int) $objp->rowid);
899 $sql .= " AND lang = '" . $db->escape($langs->getDefaultLang()) . "'";
900 $sql .= ' LIMIT 1';
901
902 $resqlm = $db->query($sql);
903 if ($resqlm) {
904 $objtp = $db->fetch_object($resqlm);
905 if (!empty($objtp->description)) {
906 $objp->description = $objtp->description;
907 }
908 if (!empty($objtp->label)) {
909 $objp->label = $objtp->label;
910 }
911 }
912 }
913
914 $stockwarehouse = 0;
915 if ($usevirtualstock) {
916 // If option to increase/decrease is not on an object validation, virtual stock may differs from physical stock.
917 $stock = $prod->stock_theorique;
918 //if conf active, stock virtual by warehouse is calculated
919 if (getDolGlobalString('STOCK_ALLOW_VIRTUAL_STOCK_PER_WAREHOUSE')) {
920 $stockwarehouse = $prod->stock_warehouse[(int) $fk_entrepot]->virtual;
921 }
922 } else {
923 $stock = $prod->stock_reel;
924 if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
925 $stockwarehouse = $prod->stock_warehouse[(int) $fk_entrepot]->real;
926 }
927 }
928
929 // Force call prod->load_stats_xxx to choose status to count (otherwise it is loaded by load_stock function)
930 if (isset($draftchecked)) {
931 $result = $prod->load_stats_commande_fournisseur(0, '0,1,2,3,4');
932 } elseif (!$usevirtualstock) {
933 $result = $prod->load_stats_commande_fournisseur(0, '1,2,3,4');
934 }
935
936 if (!$usevirtualstock) {
937 $result = $prod->load_stats_reception(0, '4');
938 }
939
940 //print $prod->stats_commande_fournisseur['qty'].'<br>'."\n";
941 //print $prod->stats_reception['qty'];
942 $ordered = $prod->stats_commande_fournisseur['qty'] - $prod->stats_reception['qty'];
943
944 $desiredstock = $objp->desiredstock;
945 $alertstock = $objp->seuil_stock_alerte;
946 $desiredstockwarehouse = (!empty($objp->desiredstockpse) ? $objp->desiredstockpse : 0);
947 $alertstockwarehouse = (!empty($objp->seuil_stock_alertepse) ? $objp->seuil_stock_alertepse : 0);
948
949 $warning = '';
950 if ($alertstock && ($stock < $alertstock)) {
951 $warning = img_warning($langs->trans('StockTooLow')) . ' ';
952 }
953 $warningwarehouse = '';
954 if ($alertstockwarehouse && ($stockwarehouse < $alertstockwarehouse)) {
955 $warningwarehouse = img_warning($langs->trans('StockTooLow')) . ' ';
956 }
957
958 //depending on conf, use either physical stock or
959 //virtual stock to compute the stock to buy value
960
961 if (empty($usevirtualstock)) {
962 $stocktobuy = max(max($desiredstock, $alertstock) - $stock - $ordered, 0);
963 } else {
964 $stocktobuy = max(max($desiredstock, $alertstock) - $stock, 0); //ordered is already in $stock in virtual mode
965 }
966 if (empty($usevirtualstock)) {
967 $stocktobuywarehouse = max(max($desiredstockwarehouse, $alertstockwarehouse) - $stockwarehouse - $ordered, 0);
968 } else {
969 $stocktobuywarehouse = max(max($desiredstockwarehouse, $alertstockwarehouse) - $stockwarehouse, 0); //ordered is already in $stock in virtual mode
970 }
971
972 $picto = '';
973 if ($ordered > 0) {
974 $stockforcompare = ($usevirtualstock ? $stock : $stock + $ordered);
975 /*if ($stockforcompare >= $desiredstock)
976 {
977 $picto = img_picto('', 'help');
978 } else {
979 $picto = img_picto('', 'help');
980 }*/
981 } else {
982 $picto = img_picto($langs->trans("NoPendingReceptionOnSupplierOrder"), 'help');
983 }
984
985 print '<tr class="oddeven">';
986
987 // Select field
988 print '<td><input type="checkbox" class="check" name="choose' . $i . '"></td>';
989
990 print '<td class="nowrap">' . $prod->getNomUrl(1, 'stock') . '</td>';
991
992 print '<td class="tdoverflowmax200" title="' . dol_escape_htmltag($objp->label) . '">';
993 print dol_escape_htmltag($objp->label);
994 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
995 print '</td>';
996
997 if (isModEnabled("service") && $type == 1) {
998 $regs = array();
999 if (preg_match('/([0-9]+)y/i', $objp->duration, $regs)) {
1000 $duration = $regs[1] . ' ' . $langs->trans('DurationYear');
1001 } elseif (preg_match('/([0-9]+)m/i', $objp->duration, $regs)) {
1002 $duration = $regs[1] . ' ' . $langs->trans('DurationMonth');
1003 } elseif (preg_match('/([0-9]+)d/i', $objp->duration, $regs)) {
1004 $duration = $regs[1] . ' ' . $langs->trans('DurationDay');
1005 } else {
1006 $duration = $objp->duration;
1007 }
1008 print '<td class="center">' . $duration . '</td>';
1009 }
1010
1011 // Desired stock
1012 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>';
1013
1014 // Limit stock for alert
1015 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>';
1016
1017 // Current stock (all warehouses)
1018 print '<td class="right">' . $warning . price(price2num($stock, 'MS'));
1019 print '<!-- stock returned by main sql is ' . $objp->stock_physique . ' -->';
1020 print '</td>';
1021
1022 // Current stock (warehouse selected only)
1023 if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
1024 print '<td class="right">'.$warningwarehouse.$stockwarehouse.'</td>';
1025 }
1026
1027 // Already ordered
1028 print '<td class="right"><a href="replenishorders.php?search_product=' . $prod->id . '">' . $ordered . '</a> ' . $picto . '</td>';
1029
1030 // To order
1031 $tobuy = ((getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) > 0 ? $stocktobuywarehouse : $stocktobuy);
1032 print '<td class="right"><input type="text" size="4" name="tobuy'.$i.'" value="'.$tobuy.'"></td>';
1033
1034 // Supplier
1035 print '<td class="right">';
1036 print $form->select_product_fourn_price($prod->id, 'fourn' . $i, $fk_supplier);
1037 print '</td>';
1038
1039 // Fields from hook
1040 $parameters = array('objp' => $objp, 'i' => $i, 'tobuy' => $tobuy);
1041 $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters); // Note that $action and $object may have been modified by hook
1042 print $hookmanager->resPrint;
1043
1044 print '</tr>';
1045 }
1046 $i++;
1047}
1048
1049if ($num == 0) {
1050 $colspan = 9;
1051 if (isModEnabled("service") && $type == 1) {
1052 $colspan++;
1053 }
1054 if (getDolGlobalString('STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE') && $fk_entrepot > 0) {
1055 $colspan++;
1056 }
1057 print '<tr><td colspan="' . $colspan . '">';
1058 print '<span class="opacitymedium">';
1059 print $langs->trans("None");
1060 print '</span>';
1061 print '</td></tr>';
1062}
1063
1064$parameters = array('sql' => $sql);
1065$reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters); // Note that $action and $object may have been modified by hook
1066print $hookmanager->resPrint;
1067
1068print '</table>';
1069print '</div>';
1070
1071$db->free($resql);
1072
1073print dol_get_fiche_end();
1074
1075
1076$value = $langs->trans("CreateOrders");
1077print '<div class="center"><input type="submit" class="button" name="valid" value="' . $value . '"></div>';
1078
1079
1080print '</form>';
1081
1082
1083// TODO Replace this with jquery
1084print '
1085<script type="text/javascript">
1086function toggle(source)
1087{
1088 checkboxes = document.getElementsByClassName("check");
1089 for (var i=0; i < checkboxes.length;i++) {
1090 if (!checkboxes[i].disabled) {
1091 checkboxes[i].checked = source.checked;
1092 }
1093 }
1094}
1095</script>';
1096
1097
1098llxFooter();
1099
1100$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:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
print_liste_field_titre($name, $file="", $field="", $begin="", $param="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
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_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
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 '.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
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.
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.
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.
img_info($titlealt='default')
Show info logo.
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...
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.