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