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