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