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