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