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