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