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