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