dolibarr  18.0.0
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 $sql .= ' FROM '.MAIN_DB_PREFIX.'product as p';
363 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as s ON p.rowid = s.fk_product';
364 $list_warehouse = (empty($listofqualifiedwarehousesid) ? '0' : $listofqualifiedwarehousesid);
365 $sql .= ' AND s.fk_entrepot IN ('.$db->sanitize($list_warehouse) .')';
366 
367 //$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'entrepot AS ent ON s.fk_entrepot = ent.rowid AND ent.entity IN('.getEntity('stock').')';
368 if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0) {
369  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_warehouse_properties AS pse ON (p.rowid = pse.fk_product AND pse.fk_entrepot = '.((int) $fk_entrepot).')';
370 }
371 // Add fields from hooks
372 $parameters = array();
373 $reshook = $hookmanager->executeHooks('printFieldListJoin', $parameters); // Note that $action and $object may have been modified by hook
374 $sql .= $hookmanager->resPrint;
375 
376 $sql .= ' WHERE p.entity IN ('.getEntity('product').')';
377 if ($sall) {
378  $sql .= natural_search(array('p.ref', 'p.label', 'p.description', 'p.note'), $sall);
379 }
380 // if the type is not 1, we show all products (type = 0,2,3)
381 if (dol_strlen($type)) {
382  if ($type == 1) {
383  $sql .= ' AND p.fk_product_type = 1';
384  } else {
385  $sql .= ' AND p.fk_product_type <> 1';
386  }
387 }
388 if ($search_ref) {
389  $sql .= natural_search('p.ref', $search_ref);
390 }
391 if ($search_label) {
392  $sql .= natural_search('p.label', $search_label);
393 }
394 $sql .= ' AND p.tobuy = 1';
395 if (!empty($conf->variants->eabled) && empty($conf->global->VARIANT_ALLOW_STOCK_MOVEMENT_ON_VARIANT_PARENT)) { // Add test to exclude products that has variants
396  $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').'))';
397 }
398 if ($fk_supplier > 0) {
399  $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').'))';
400 }
401 // Add where from hooks
402 $parameters = array();
403 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
404 $sql .= $hookmanager->resPrint;
405 
406 $sql .= ' GROUP BY p.rowid, p.ref, p.label, p.description, p.price';
407 $sql .= ', p.price_ttc, p.price_base_type,p.fk_product_type, p.tms';
408 $sql .= ', p.duration, p.tobuy';
409 $sql .= ', p.desiredstock';
410 $sql .= ', p.seuil_stock_alerte';
411 if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0) {
412  $sql .= ', pse.desiredstock';
413  $sql .= ', pse.seuil_stock_alerte';
414 }
415 $sql .= ', s.fk_product';
416 
417 if ($usevirtualstock) {
418  if (isModEnabled('commande')) {
419  $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
420  $sqlCommandesCli .= " FROM ".MAIN_DB_PREFIX."commandedet as cd1, ".MAIN_DB_PREFIX."commande as c1";
421  $sqlCommandesCli .= " WHERE c1.rowid = cd1.fk_commande AND c1.entity IN (".getEntity(!empty($conf->global->STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE) ? 'stock' : 'commande').")";
422  $sqlCommandesCli .= " AND cd1.fk_product = p.rowid";
423  $sqlCommandesCli .= " AND c1.fk_statut IN (1,2))";
424  } else {
425  $sqlCommandesCli = '0';
426  }
427 
428  if (isModEnabled("expedition")) {
429  $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
430  $sqlExpeditionsCli .= " FROM ".MAIN_DB_PREFIX."expedition as e2,";
431  $sqlExpeditionsCli .= " ".MAIN_DB_PREFIX."expeditiondet as ed2,";
432  $sqlExpeditionsCli .= " ".MAIN_DB_PREFIX."commande as c2,";
433  $sqlExpeditionsCli .= " ".MAIN_DB_PREFIX."commandedet as cd2";
434  $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').")";
435  $sqlExpeditionsCli .= " AND cd2.fk_commande = c2.rowid";
436  $sqlExpeditionsCli .= " AND c2.fk_statut IN (1,2)";
437  $sqlExpeditionsCli .= " AND cd2.fk_product = p.rowid";
438  $sqlExpeditionsCli .= " AND e2.fk_statut IN (1,2))";
439  } else {
440  $sqlExpeditionsCli = '0';
441  }
442 
443  if (isModEnabled("supplier_order")) {
444  $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
445  $sqlCommandesFourn .= " FROM ".MAIN_DB_PREFIX."commande_fournisseurdet as cd3,";
446  $sqlCommandesFourn .= " ".MAIN_DB_PREFIX."commande_fournisseur as c3";
447  $sqlCommandesFourn .= " WHERE c3.rowid = cd3.fk_commande";
448  $sqlCommandesFourn .= " AND c3.entity IN (".getEntity(!empty($conf->global->STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE) ? 'stock' : 'supplier_order').")";
449  $sqlCommandesFourn .= " AND cd3.fk_product = p.rowid";
450  $sqlCommandesFourn .= " AND c3.fk_statut IN (3,4))";
451 
452  $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
453  $sqlReceptionFourn .= " FROM ".MAIN_DB_PREFIX."commande_fournisseur as cf4,";
454  $sqlReceptionFourn .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as fd4";
455  $sqlReceptionFourn .= " WHERE fd4.fk_commande = cf4.rowid AND cf4.entity IN (".getEntity(!empty($conf->global->STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE) ? 'stock' : 'supplier_order').")";
456  $sqlReceptionFourn .= " AND fd4.fk_product = p.rowid";
457  $sqlReceptionFourn .= " AND cf4.fk_statut IN (3,4))";
458  } else {
459  $sqlCommandesFourn = '0';
460  $sqlReceptionFourn = '0';
461  }
462 
463  if (isModEnabled('mrp')) {
464  $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
465  $sqlProductionToConsume .= " FROM ".MAIN_DB_PREFIX."mrp_mo as mm5,";
466  $sqlProductionToConsume .= " ".MAIN_DB_PREFIX."mrp_production as mp5";
467  $sqlProductionToConsume .= " WHERE mm5.rowid = mp5.fk_mo AND mm5.entity IN (".getEntity(!empty($conf->global->STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE) ? 'stock' : 'mo').")";
468  $sqlProductionToConsume .= " AND mp5.fk_product = p.rowid";
469  $sqlProductionToConsume .= " AND mp5.role IN ('toconsume', 'consumed')";
470  $sqlProductionToConsume .= " AND mm5.status IN (1,2))";
471 
472  $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
473  $sqlProductionToProduce .= " FROM ".MAIN_DB_PREFIX."mrp_mo as mm5,";
474  $sqlProductionToProduce .= " ".MAIN_DB_PREFIX."mrp_production as mp5";
475  $sqlProductionToProduce .= " WHERE mm5.rowid = mp5.fk_mo AND mm5.entity IN (".getEntity(!empty($conf->global->STOCK_CALCULATE_VIRTUAL_STOCK_TRANSVERSE_MODE) ? 'stock' : 'mo').")";
476  $sqlProductionToProduce .= " AND mp5.fk_product = p.rowid";
477  $sqlProductionToProduce .= " AND mp5.role IN ('toproduce', 'produced')";
478  $sqlProductionToProduce .= " AND mm5.status IN (1,2))";
479  } else {
480  $sqlProductionToConsume = '0';
481  $sqlProductionToProduce = '0';
482  }
483 
484  $sql .= ' HAVING (';
485  $sql .= " (".$sqldesiredtock." >= 0 AND (".$sqldesiredtock." > SUM(".$db->ifsql("s.reel IS NULL", "0", "s.reel").')';
486  $sql .= " - (".$sqlCommandesCli." - ".$sqlExpeditionsCli.") + (".$sqlCommandesFourn." - ".$sqlReceptionFourn.") + (".$sqlProductionToProduce." - ".$sqlProductionToConsume.")))";
487  $sql .= ' OR';
488  if ($includeproductswithoutdesiredqty == 'on') {
489  $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").")";
490  } else {
491  $sql .= " (".$sqlalertstock." >= 0 AND (".$sqlalertstock." > SUM(".$db->ifsql("s.reel IS NULL", "0", "s.reel").')';
492  }
493  $sql .= " - (".$sqlCommandesCli." - ".$sqlExpeditionsCli.") + (".$sqlCommandesFourn." - ".$sqlReceptionFourn.") + (".$sqlProductionToProduce." - ".$sqlProductionToConsume.")))";
494  $sql .= ")";
495 
496  if ($salert == 'on') { // Option to see when stock is lower than alert
497  $sql .= ' AND (';
498  if ($includeproductswithoutdesiredqty == 'on') {
499  $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").")";
500  } else {
501  $sql .= $sqlalertstock." >= 0 AND (".$sqlalertstock." > SUM(".$db->ifsql("s.reel IS NULL", "0", "s.reel").")";
502  }
503  $sql .= " - (".$sqlCommandesCli." - ".$sqlExpeditionsCli.") + (".$sqlCommandesFourn." - ".$sqlReceptionFourn.") + (".$sqlProductionToProduce." - ".$sqlProductionToConsume."))";
504  $sql .= ")";
505  $alertchecked = 'checked';
506  }
507 } else {
508  $sql .= ' HAVING (';
509  $sql .= "(".$sqldesiredtock." >= 0 AND (".$sqldesiredtock." > SUM(".$db->ifsql("s.reel IS NULL", "0", "s.reel").")))";
510  $sql .= ' OR';
511  if ($includeproductswithoutdesiredqty == 'on') {
512  $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").')))';
513  } else {
514  $sql .= " (".$sqlalertstock." >= 0 AND (".$sqlalertstock." > SUM(".$db->ifsql("s.reel IS NULL", "0", "s.reel").')))';
515  }
516  $sql .= ')';
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 .= ')';
526  $alertchecked = 'checked';
527  }
528 }
529 
530 $includeproductswithoutdesiredqtychecked = '';
531 if ($includeproductswithoutdesiredqty == 'on') {
532  $includeproductswithoutdesiredqtychecked = 'checked';
533 }
534 
535 $nbtotalofrecords = '';
536 if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
537  $result = $db->query($sql);
538  $nbtotalofrecords = $db->num_rows($result);
539  if (($page * $limit) > $nbtotalofrecords) {
540  $page = 0;
541  $offset = 0;
542  }
543 }
544 
545 $sql .= $db->order($sortfield, $sortorder);
546 $sql .= $db->plimit($limit + 1, $offset);
547 
548 //print $sql;
549 $resql = $db->query($sql);
550 if (empty($resql)) {
551  dol_print_error($db);
552  exit;
553 }
554 
555 $num = $db->num_rows($resql);
556 $i = 0;
557 
558 $helpurl = 'EN:Module_Stocks_En|FR:Module_Stock|';
559 $helpurl .= 'ES:M&oacute;dulo_Stocks';
560 
561 llxHeader('', $title, $helpurl, '');
562 
563 $head = array();
564 
565 $head[0][0] = DOL_URL_ROOT.'/product/stock/replenish.php';
566 $head[0][1] = $title;
567 $head[0][2] = 'replenish';
568 
569 $head[1][0] = DOL_URL_ROOT.'/product/stock/replenishorders.php';
570 $head[1][1] = $langs->trans("ReplenishmentOrders");
571 $head[1][2] = 'replenishorders';
572 
573 
574 print load_fiche_titre($langs->trans('Replenishment'), '', 'stock');
575 
576 print dol_get_fiche_head($head, 'replenish', '', -1, '');
577 
578 print '<span class="opacitymedium">'.$langs->trans("ReplenishmentStatusDesc").'</span>'."\n";
579 
580 //$link = '<a title=' .$langs->trans("MenuNewWarehouse"). ' href="'.DOL_URL_ROOT.'/product/stock/card.php?action=create">'.$langs->trans("MenuNewWarehouse").'</a>';
581 
582 if (empty($fk_entrepot) && !empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE)) {
583  print '<span class="opacitymedium">'.$langs->trans("ReplenishmentStatusDescPerWarehouse").'</span>'."\n";
584 }
585 print '<br><br>';
586 if ($usevirtualstock == 1) {
587  print $langs->trans("CurentSelectionMode").': ';
588  print '<span class="a-mesure">'.$langs->trans("UseVirtualStock").'</span>';
589  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>';
590  print '<br>';
591 }
592 if ($usevirtualstock == 0) {
593  print $langs->trans("CurentSelectionMode").': ';
594  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>';
595  print ' <span class="a-mesure">'.$langs->trans("UsePhysicalStock").'</span>';
596  print '<br>';
597 }
598 print '<br>'."\n";
599 
600 print '<form name="formFilterWarehouse" method="POST" action="'.$_SERVER["PHP_SELF"].'">';
601 print '<input type="hidden" name="token" value="'.newToken().'">';
602 print '<input type="hidden" name="action" value="filter">';
603 print '<input type="hidden" name="search_ref" value="'.$search_ref.'">';
604 print '<input type="hidden" name="search_label" value="'.$search_label.'">';
605 print '<input type="hidden" name="salert" value="'.$salert.'">';
606 print '<input type="hidden" name="includeproductswithoutdesiredqty" value="'.$includeproductswithoutdesiredqty.'">';
607 print '<input type="hidden" name="draftorder" value="'.$draftorder.'">';
608 print '<input type="hidden" name="mode" value="'.$mode.'">';
609 if ($limit > 0 && $limit != $conf->liste_limit) {
610  print '<input type="hidden" name="limit" value="'.$limit.'">';
611 }
612 if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE)) {
613  print '<div class="inline-block valignmiddle" style="padding-right: 20px;">';
614  print $langs->trans('Warehouse').' '.$formproduct->selectWarehouses($fk_entrepot, 'fk_entrepot', '', 1);
615  print '</div>';
616 }
617 print '<div class="inline-block valignmiddle" style="padding-right: 20px;">';
618 $filter = '(fournisseur:=:1)';
619 print $langs->trans('Supplier').' '.$form->select_company($fk_supplier, 'fk_supplier', $filter, 1);
620 print '</div>';
621 
622 $parameters = array();
623 $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook
624 if (empty($reshook)) {
625  print $hookmanager->resPrint;
626 }
627 
628 print '<div class="inline-block valignmiddle">';
629 print '<input type="submit" class="button smallpaddingimp" name="valid" value="'.$langs->trans('ToFilter').'">';
630 print '</div>';
631 
632 print '</form>';
633 
634 print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formulaire">';
635 print '<input type="hidden" name="token" value="'.newToken().'">';
636 print '<input type="hidden" name="fk_supplier" value="'.$fk_supplier.'">';
637 print '<input type="hidden" name="fk_entrepot" value="'.$fk_entrepot.'">';
638 print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
639 print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
640 print '<input type="hidden" name="type" value="'.$type.'">';
641 print '<input type="hidden" name="linecount" value="'.$num.'">';
642 print '<input type="hidden" name="action" value="order">';
643 print '<input type="hidden" name="mode" value="'.$mode.'">';
644 
645 
646 if ($search_ref || $search_label || $sall || $salert || $draftorder || GETPOST('search', 'alpha')) {
647  $filters = '&search_ref='.urlencode($search_ref).'&search_label='.urlencode($search_label);
648  $filters .= '&sall='.urlencode($sall);
649  $filters .= '&salert='.urlencode($salert);
650  $filters .= '&draftorder='.urlencode($draftorder);
651  $filters .= '&mode='.urlencode($mode);
652  if ($fk_supplier > 0) {
653  $filters .= '&fk_supplier='.urlencode($fk_supplier);
654  }
655  if ($fk_entrepot > 0) {
656  $filters .= '&fk_entrepot='.urlencode($fk_entrepot);
657  }
658 } else {
659  $filters = '&search_ref='.urlencode($search_ref).'&search_label='.urlencode($search_label);
660  $filters .= '&fourn_id='.urlencode($fourn_id);
661  $filters .= (isset($type) ? '&type='.urlencode($type) : '');
662  $filters .= '&='.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 }
672 if ($limit > 0 && $limit != $conf->liste_limit) {
673  $filters .= '&limit='.((int) $limit);
674 }
675 if (!empty($includeproductswithoutdesiredqty)) $filters .= '&includeproductswithoutdesiredqty='.urlencode($includeproductswithoutdesiredqty);
676 if (!empty($salert)) $filters .= '&salert='.urlencode($salert);
677 
678 $param = (isset($type) ? '&type='.urlencode($type) : '');
679 $param .= '&fourn_id='.urlencode($fourn_id).'&search_label='.urlencode($search_label).'&includeproductswithoutdesiredqty='.urlencode($includeproductswithoutdesiredqty).'&salert='.urlencode($salert).'&draftorder='.urlencode($draftorder);
680 $param .= '&search_ref='.urlencode($search_ref);
681 $param .= '&mode='.urlencode($mode);
682 $param .= '&fk_supplier='.urlencode($fk_supplier);
683 $param .= '&fk_entrepot='.urlencode($fk_entrepot);
684 if (!empty($includeproductswithoutdesiredqty)) $param .= '&includeproductswithoutdesiredqty='.urlencode($includeproductswithoutdesiredqty);
685 if (!empty($salert)) $param .= '&salert='.urlencode($salert);
686 
687 $stocklabel = $langs->trans('Stock');
688 $stocklabelbis = $langs->trans('Stock');
689 $stocktooltip = '';
690 if ($usevirtualstock == 1) {
691  $stocklabel = $langs->trans('VirtualStock');
692  $stocktooltip = $langs->trans("VirtualStockDesc");
693 }
694 if ($usevirtualstock == 0) {
695  $stocklabel = $langs->trans('PhysicalStock');
696 }
697 if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0) {
698  $stocklabelbis = $stocklabel.' (Selected warehouse)';
699  $stocklabel .= ' ('.$langs->trans("AllWarehouses").')';
700 }
701 $texte = $langs->trans('Replenishment');
702 
703 print '<br>';
704 
705 
706 if (!empty($conf->global->REPLENISH_ALLOW_VARIABLESIZELIST)) {
708  $texte,
709  $page,
710  'replenish.php',
711  $filters,
712  $sortfield,
713  $sortorder,
714  '',
715  $num,
716  $nbtotalofrecords,
717  '',
718  0,
719  '',
720  '',
721  $limit
722  );
723 } else {
725  $texte,
726  $page,
727  'replenish.php',
728  $filters,
729  $sortfield,
730  $sortorder,
731  '',
732  $num,
733  $nbtotalofrecords,
734  ''
735  );
736 }
737 
738 
739 print '<div class="div-table-responsive-no-min">';
740 print '<table class="liste centpercent">';
741 
742 // Fields title search
743 print '<tr class="liste_titre_filter">';
744 print '<td class="liste_titre">&nbsp;</td>';
745 print '<td class="liste_titre"><input class="flat" type="text" name="search_ref" size="8" value="'.dol_escape_htmltag($search_ref).'"></td>';
746 print '<td class="liste_titre"><input class="flat" type="text" name="search_label" size="8" value="'.dol_escape_htmltag($search_label).'"></td>';
747 if (isModEnabled("service") && $type == 1) {
748  print '<td class="liste_titre">&nbsp;</td>';
749 }
750 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>';
751 print '<td class="liste_titre right"></td>';
752 print '<td class="liste_titre right">'.$langs->trans('AlertOnly').'&nbsp;<input type="checkbox" id="salert" name="salert" '.(!empty($alertchecked) ? $alertchecked : '').'></td>';
753 if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0) {
754  print '<td class="liste_titre">&nbsp;</td>';
755 }
756 print '<td class="liste_titre right">';
757 if (!empty($conf->global->STOCK_REPLENISH_ADD_CHECKBOX_INCLUDE_DRAFT_ORDER)) {
758  print $langs->trans('IncludeAlsoDraftOrders').'&nbsp;<input type="checkbox" id="draftorder" name="draftorder" '.(!empty($draftchecked) ? $draftchecked : '').'>';
759 }
760 print '</td>';
761 print '<td class="liste_titre">&nbsp;</td>';
762 // Fields from hook
763 $parameters = array('param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder);
764 $reshook = $hookmanager->executeHooks('printFieldListOption', $parameters); // Note that $action and $object may have been modified by hook
765 print $hookmanager->resPrint;
766 
767 print '<td class="liste_titre maxwidthsearch right">';
768 $searchpicto = $form->showFilterAndCheckAddButtons(0);
769 print $searchpicto;
770 print '</td>';
771 print '</tr>';
772 
773 // Lines of title
774 print '<tr class="liste_titre">';
775 print_liste_field_titre('<input type="checkbox" onClick="toggle(this)" />', $_SERVER["PHP_SELF"], '');
776 print_liste_field_titre('Ref', $_SERVER["PHP_SELF"], 'p.ref', $param, '', '', $sortfield, $sortorder);
777 print_liste_field_titre('Label', $_SERVER["PHP_SELF"], 'p.label', $param, '', '', $sortfield, $sortorder);
778 if (isModEnabled("service") && $type == 1) {
779  print_liste_field_titre('Duration', $_SERVER["PHP_SELF"], 'p.duration', $param, '', '', $sortfield, $sortorder, 'center ');
780 }
781 print_liste_field_titre('DesiredStock', $_SERVER["PHP_SELF"], 'p.desiredstock', $param, '', '', $sortfield, $sortorder, 'right ');
782 print_liste_field_titre('StockLimitShort', $_SERVER["PHP_SELF"], 'p.seuil_stock_alerte', $param, '', '', $sortfield, $sortorder, 'right ');
783 print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], 'stock_physique', $param, '', '', $sortfield, $sortorder, 'right ', $stocktooltip);
784 if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0) {
785  print_liste_field_titre($stocklabelbis, $_SERVER["PHP_SELF"], 'stock_real_warehouse', $param, '', '', $sortfield, $sortorder, 'right ');
786 }
787 print_liste_field_titre('Ordered', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ');
788 print_liste_field_titre('StockToBuy', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ');
789 print_liste_field_titre('SupplierRef', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ');
790 
791 // Hook fields
792 $parameters = array('param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder);
793 $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook
794 print $hookmanager->resPrint;
795 
796 print "</tr>\n";
797 
798 while ($i < ($limit ? min($num, $limit) : $num)) {
799  $objp = $db->fetch_object($resql);
800 
801  if (!empty($conf->global->STOCK_SUPPORTS_SERVICES) || $objp->fk_product_type == 0) {
802  $result = $prod->fetch($objp->rowid);
803  if ($result < 0) {
804  dol_print_error($db);
805  exit;
806  }
807 
808  $prod->load_stock('warehouseopen, warehouseinternal'.(!$usevirtualstock?', novirtual':''), $draftchecked);
809 
810  // Multilangs
811  if (getDolGlobalInt('MAIN_MULTILANGS')) {
812  $sql = 'SELECT label,description';
813  $sql .= ' FROM '.MAIN_DB_PREFIX.'product_lang';
814  $sql .= ' WHERE fk_product = '.((int) $objp->rowid);
815  $sql .= " AND lang = '".$db->escape($langs->getDefaultLang())."'";
816  $sql .= ' LIMIT 1';
817 
818  $resqlm = $db->query($sql);
819  if ($resqlm) {
820  $objtp = $db->fetch_object($resqlm);
821  if (!empty($objtp->description)) {
822  $objp->description = $objtp->description;
823  }
824  if (!empty($objtp->label)) {
825  $objp->label = $objtp->label;
826  }
827  }
828  }
829 
830  $stockwarehouse = 0;
831  if ($usevirtualstock) {
832  // If option to increase/decrease is not on an object validation, virtual stock may differs from physical stock.
833  $stock = $prod->stock_theorique;
834  //TODO $stockwarehouse = $prod->stock_warehouse[$fk_entrepot]->;
835  } else {
836  $stock = $prod->stock_reel;
837  $stockwarehouse = $prod->stock_warehouse[$fk_entrepot]->real;
838  }
839 
840  // Force call prod->load_stats_xxx to choose status to count (otherwise it is loaded by load_stock function)
841  if (isset($draftchecked)) {
842  $result = $prod->load_stats_commande_fournisseur(0, '0,1,2,3,4');
843  } elseif (!$usevirtualstock) {
844  $result = $prod->load_stats_commande_fournisseur(0, '1,2,3,4');
845  }
846 
847  if (!$usevirtualstock) {
848  $result = $prod->load_stats_reception(0, '4');
849  }
850 
851  //print $prod->stats_commande_fournisseur['qty'].'<br>'."\n";
852  //print $prod->stats_reception['qty'];
853  $ordered = $prod->stats_commande_fournisseur['qty'] - $prod->stats_reception['qty'];
854 
855  $desiredstock = $objp->desiredstock;
856  $alertstock = $objp->seuil_stock_alerte;
857  $desiredstockwarehouse = (!empty($objp->desiredstockpse) ? $objp->desiredstockpse : 0);
858  $alertstockwarehouse = (!empty($objp->seuil_stock_alertepse) ? $objp->seuil_stock_alertepse : 0);
859 
860  $warning = '';
861  if ($alertstock && ($stock < $alertstock)) {
862  $warning = img_warning($langs->trans('StockTooLow')).' ';
863  }
864  $warningwarehouse = '';
865  if ($alertstockwarehouse && ($stockwarehouse < $alertstockwarehouse)) {
866  $warningwarehouse = img_warning($langs->trans('StockTooLow')).' ';
867  }
868 
869  //depending on conf, use either physical stock or
870  //virtual stock to compute the stock to buy value
871 
872  if (empty($usevirtualstock)) {
873  $stocktobuy = max(max($desiredstock, $alertstock) - $stock - $ordered, 0);
874  } else {
875  $stocktobuy = max(max($desiredstock, $alertstock) - $stock, 0); //ordered is already in $stock in virtual mode
876  }
877  if (empty($usevirtualstock)) {
878  $stocktobuywarehouse = max(max($desiredstockwarehouse, $alertstockwarehouse) - $stockwarehouse - $ordered, 0);
879  } else {
880  $stocktobuywarehouse = max(max($desiredstockwarehouse, $alertstockwarehouse) - $stockwarehouse, 0); //ordered is already in $stock in virtual mode
881  }
882 
883  $picto = '';
884  if ($ordered > 0) {
885  $stockforcompare = ($usevirtualstock ? $stock : $stock + $ordered);
886  /*if ($stockforcompare >= $desiredstock)
887  {
888  $picto = img_picto('', 'help');
889  } else {
890  $picto = img_picto('', 'help');
891  }*/
892  } else {
893  $picto = img_picto($langs->trans("NoPendingReceptionOnSupplierOrder"), 'help');
894  }
895 
896  print '<tr class="oddeven">';
897 
898  // Select field
899  print '<td><input type="checkbox" class="check" name="choose'.$i.'"></td>';
900 
901  print '<td class="nowrap">'.$prod->getNomUrl(1, 'stock').'</td>';
902 
903  print '<td class="tdoverflowmax200" title="'.dol_escape_htmltag($objp->label).'">';
904  print dol_escape_htmltag($objp->label);
905  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
906  print '</td>';
907 
908  if (isModEnabled("service") && $type == 1) {
909  $regs = array();
910  if (preg_match('/([0-9]+)y/i', $objp->duration, $regs)) {
911  $duration = $regs[1].' '.$langs->trans('DurationYear');
912  } elseif (preg_match('/([0-9]+)m/i', $objp->duration, $regs)) {
913  $duration = $regs[1].' '.$langs->trans('DurationMonth');
914  } elseif (preg_match('/([0-9]+)d/i', $objp->duration, $regs)) {
915  $duration = $regs[1].' '.$langs->trans('DurationDay');
916  } else {
917  $duration = $objp->duration;
918  }
919  print '<td class="center">'.$duration.'</td>';
920  }
921 
922  // Desired stock
923  print '<td class="right">'.((!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0) > 0 ? $desiredstockwarehouse : $desiredstock).'</td>';
924 
925  // Limit stock for alert
926  print '<td class="right">'.((!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0) > 0 ? $alertstockwarehouse : $alertstock).'</td>';
927 
928  // Current stock (all warehouses)
929  print '<td class="right">'.$warning.$stock;
930  print '<!-- stock returned by main sql is '.$objp->stock_physique.' -->';
931  print '</td>';
932 
933  // Current stock (warehouse selected only)
934  if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0) {
935  print '<td class="right">'.$warningwarehouse.$stockwarehouse.'</td>';
936  }
937 
938  // Already ordered
939  print '<td class="right"><a href="replenishorders.php?search_product='.$prod->id.'">'.$ordered.'</a> '.$picto.'</td>';
940 
941  // To order
942  print '<td class="right"><input type="text" size="4" name="tobuy'.$i.'" value="'.((!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0) > 0 ? $stocktobuywarehouse : $stocktobuy).'"></td>';
943 
944  // Supplier
945  print '<td class="right">';
946  print $form->select_product_fourn_price($prod->id, 'fourn'.$i, $fk_supplier);
947  print '</td>';
948 
949  // Fields from hook
950  $parameters = array('objp'=>$objp);
951  $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters); // Note that $action and $object may have been modified by hook
952  print $hookmanager->resPrint;
953 
954  print '</tr>';
955  }
956  $i++;
957 }
958 
959 if ($num == 0) {
960  $colspan = 9;
961  if (isModEnabled("service") && $type == 1) {
962  $colspan++;
963  }
964  if (!empty($conf->global->STOCK_ALLOW_ADD_LIMIT_STOCK_BY_WAREHOUSE) && $fk_entrepot > 0) {
965  $colspan++;
966  }
967  print '<tr><td colspan="'.$colspan.'">';
968  print '<span class="opacitymedium">';
969  print $langs->trans("None");
970  print '</span>';
971  print '</td></tr>';
972 }
973 
974 $parameters = array('sql'=>$sql);
975 $reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters); // Note that $action and $object may have been modified by hook
976 print $hookmanager->resPrint;
977 
978 print '</table>';
979 print '</div>';
980 
981 $db->free($resql);
982 
983 print dol_get_fiche_end();
984 
985 
986 $value = $langs->trans("CreateOrders");
987 print '<div class="center"><input type="submit" class="button" name="valid" value="'.$value.'"></div>';
988 
989 
990 print '</form>';
991 
992 
993 // TODO Replace this with jquery
994 print '
995 <script type="text/javascript">
996 function toggle(source)
997 {
998  checkboxes = document.getElementsByClassName("check");
999  for (var i=0; i < checkboxes.length;i++) {
1000  if (!checkboxes[i].disabled) {
1001  checkboxes[i].checked = source.checked;
1002  }
1003  }
1004 }
1005 </script>';
1006 
1007 
1008 llxFooter();
1009 
1010 $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)
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.
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.