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