dolibarr  20.0.0-beta
stockatdate.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
3  * Copyright (C) 2013-2020 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-2021 Frédéric France <frederic.france@netlogic.fr>
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <https://www.gnu.org/licenses/>.
21  */
22 
29 // Load Dolibarr environment
30 require '../../main.inc.php';
31 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
32 require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
33 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
34 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
35 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
36 require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
37 require_once './lib/replenishment.lib.php';
38 
39 // Load translation files required by the page
40 $langs->loadLangs(array('products', 'stocks', 'orders'));
41 
42 // Initialize technical object to manage hooks of page. Note that conf->hooks_modules contains array of hook context
43 $hookmanager->initHooks(array('stockatdate'));
44 
45 // Security check
46 if ($user->socid) {
47  $socid = $user->socid;
48 }
49 $result = restrictedArea($user, 'produit|service');
50 
51 //checks if a product has been ordered
52 
53 $action = GETPOST('action', 'aZ09');
54 $type = GETPOSTINT('type');
55 $mode = GETPOST('mode', 'alpha');
56 
57 $ext=(GETPOSTISSET('output') && in_array(GETPOST('output'), array('csv'))) ? GETPOST('output') : '';
58 
59 $date = '';
60 $dateendofday = '';
61 if (GETPOSTISSET('dateday') && GETPOSTISSET('datemonth') && GETPOSTISSET('dateyear')) {
62  $date = dol_mktime(0, 0, 0, GETPOSTINT('datemonth'), GETPOSTINT('dateday'), GETPOSTINT('dateyear'));
63  $dateendofday = dol_mktime(23, 59, 59, GETPOSTINT('datemonth'), GETPOSTINT('dateday'), GETPOSTINT('dateyear'));
64 }
65 
66 $search_ref = GETPOST('search_ref', 'alphanohtml');
67 $search_nom = GETPOST('search_nom', 'alphanohtml');
68 
69 $now = dol_now();
70 
71 $productid = GETPOSTINT('productid');
72 if (GETPOSTISARRAY('search_fk_warehouse')) {
73  $search_fk_warehouse = GETPOST('search_fk_warehouse', 'array:int');
74 } else {
75  $search_fk_warehouse = array(GETPOSTINT('search_fk_warehouse'));
76 }
77 // For backward compatibility
78 if (GETPOSTINT('fk_warehouse')) {
79  $search_fk_warehouse = array(GETPOSTINT('fk_warehouse'));
80 }
81 // Clean value -1
82 foreach ($search_fk_warehouse as $key => $val) {
83  if ($val == -1 || empty($val)) {
84  unset($search_fk_warehouse[$key]);
85  }
86 }
87 
88 $sortfield = GETPOST('sortfield', 'aZ09comma');
89 $sortorder = GETPOST('sortorder', 'aZ09comma');
90 $page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
91 if (empty($page) || $page == -1) {
92  $page = 0;
93 } // If $page is not defined, or '' or -1
94 $limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
95 $offset = $limit * $page;
96 if (!$sortfield) {
97  $sortfield = 'p.ref';
98 }
99 if (!$sortorder) {
100  $sortorder = 'ASC';
101 }
102 
103 $parameters = array();
104 $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
105 if ($reshook < 0) {
106  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
107 }
108 
109 $dateIsValid = true;
110 if ($mode == 'future') {
111  if ($date && $date < $now) {
112  setEventMessages($langs->trans("ErrorDateMustBeInFuture"), null, 'errors');
113  $dateIsValid = false;
114  }
115 } else {
116  if ($date && $date > $now) {
117  setEventMessages($langs->trans("ErrorDateMustBeBeforeToday"), null, 'errors');
118  $dateIsValid = false;
119  }
120 }
121 
122 
123 /*
124  * Actions
125  */
126 
127 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
128  $date = '';
129  $productid = 0;
130  $search_fk_warehouse = array();
131  $search_ref = '';
132  $search_nom = '';
133 }
134 
135 $warehouseStatus = array();
136 if (getDolGlobalString('ENTREPOT_EXTRA_STATUS')) {
137  //$warehouseStatus[] = Entrepot::STATUS_CLOSED;
138  $warehouseStatus[] = Entrepot::STATUS_OPEN_ALL;
139  $warehouseStatus[] = Entrepot::STATUS_OPEN_INTERNAL;
140 }
141 
142 // Get array with current stock per product, warehouse
143 $stock_prod_warehouse = array();
144 $stock_prod = array();
145 if ($date && $dateIsValid) { // Avoid heavy sql if mandatory date is not defined
146  $sql = "SELECT ps.fk_product, ps.fk_entrepot as fk_warehouse,";
147  $sql .= " SUM(ps.reel) AS stock";
148  $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
149  $sql .= ", ".MAIN_DB_PREFIX."entrepot as w";
150  $sql .= ", ".MAIN_DB_PREFIX."product as p";
151  $sql .= " WHERE w.entity IN (".getEntity('stock').")";
152  $sql .= " AND w.rowid = ps.fk_entrepot AND p.rowid = ps.fk_product";
153  if (getDolGlobalString('ENTREPOT_EXTRA_STATUS') && count($warehouseStatus)) {
154  $sql .= " AND w.statut IN (".$db->sanitize(implode(',', $warehouseStatus)).")";
155  }
156  if ($productid > 0) {
157  $sql .= " AND ps.fk_product = ".((int) $productid);
158  }
159  if (! empty($search_fk_warehouse)) {
160  $sql .= " AND ps.fk_entrepot IN (".$db->sanitize(implode(",", $search_fk_warehouse)).")";
161  }
162  if ($search_ref) {
163  $sql .= natural_search("p.ref", $search_ref);
164  }
165  if ($search_nom) {
166  $sql .= natural_search("p.label", $search_nom);
167  }
168  $sql .= " GROUP BY fk_product, fk_entrepot";
169  //print $sql;
170 
171  $resql = $db->query($sql);
172  if ($resql) {
173  $num = $db->num_rows($resql);
174  $i = 0;
175 
176  while ($i < $num) {
177  $obj = $db->fetch_object($resql);
178 
179  $tmp_fk_product = $obj->fk_product;
180  $tmp_fk_warehouse = $obj->fk_warehouse;
181  $stock = $obj->stock;
182 
183  $stock_prod_warehouse[$tmp_fk_product][$tmp_fk_warehouse] = $stock;
184  $stock_prod[$tmp_fk_product] = (isset($stock_prod[$tmp_fk_product]) ? $stock_prod[$tmp_fk_product] : 0) + $stock;
185 
186  $i++;
187  }
188 
189  $db->free($resql);
190  } else {
191  dol_print_error($db);
192  }
193  //var_dump($stock_prod_warehouse);
194 } elseif ($action == 'filter') {
195  setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
196 }
197 
198 // Get array with list of stock movements between date and now (for product/warehouse=
199 $movements_prod_warehouse = array();
200 $movements_prod = array();
201 $movements_prod_warehouse_nb = array();
202 $movements_prod_nb = array();
203 if ($date && $dateIsValid) {
204  $sql = "SELECT sm.fk_product, sm.fk_entrepot, SUM(sm.value) AS stock, COUNT(sm.rowid) AS nbofmovement";
205  $sql .= " FROM ".MAIN_DB_PREFIX."stock_mouvement as sm";
206  $sql .= ", ".MAIN_DB_PREFIX."entrepot as w";
207  $sql .= ", ".MAIN_DB_PREFIX."product as p";
208  $sql .= " WHERE w.entity IN (".getEntity('stock').")";
209  $sql .= " AND w.rowid = sm.fk_entrepot AND p.rowid = sm.fk_product ";
210  if (getDolGlobalString('ENTREPOT_EXTRA_STATUS') && count($warehouseStatus)) {
211  $sql .= " AND w.statut IN (".$db->sanitize(implode(',', $warehouseStatus)).")";
212  }
213  if ($mode == 'future') {
214  $sql .= " AND sm.datem <= '".$db->idate($dateendofday)."'";
215  } else {
216  $sql .= " AND sm.datem >= '".$db->idate($dateendofday)."'";
217  }
218  if ($productid > 0) {
219  $sql .= " AND sm.fk_product = ".((int) $productid);
220  }
221  if (!empty($search_fk_warehouse)) {
222  $sql .= " AND sm.fk_entrepot IN (".$db->sanitize(implode(",", $search_fk_warehouse)).")";
223  }
224  if ($search_ref) {
225  $sql .= " AND p.ref LIKE '%".$db->escape($search_ref)."%' ";
226  }
227  if ($search_nom) {
228  $sql .= " AND p.label LIKE '%".$db->escape($search_nom)."%' ";
229  }
230  $sql .= " GROUP BY sm.fk_product, sm.fk_entrepot";
231 
232  $resql = $db->query($sql);
233 
234  if ($resql) {
235  $num = $db->num_rows($resql);
236  $i = 0;
237 
238  while ($i < $num) {
239  $obj = $db->fetch_object($resql);
240  $fk_product = $obj->fk_product;
241  $fk_entrepot = $obj->fk_entrepot;
242  $stock = $obj->stock;
243  $nbofmovement = $obj->nbofmovement;
244 
245  // Pour llx_product_stock.reel
246  $movements_prod_warehouse[$fk_product][$fk_entrepot] = $stock;
247  $movements_prod_warehouse_nb[$fk_product][$fk_entrepot] = $nbofmovement;
248 
249  // Pour llx_product.stock
250  $movements_prod[$fk_product] = $stock + (array_key_exists($fk_product, $movements_prod)?$movements_prod[$fk_product]:0);
251  $movements_prod_nb[$fk_product] = $nbofmovement + (array_key_exists($fk_product, $movements_prod_nb)?$movements_prod_nb[$fk_product]:0);
252 
253  $i++;
254  }
255 
256  $db->free($resql);
257  } else {
258  dol_print_error($db);
259  }
260 }
261 //var_dump($movements_prod_warehouse);
262 //var_dump($movements_prod);
263 
264 
265 /*
266  * View
267  */
268 
269 $form = new Form($db);
270 $formproduct = new FormProduct($db);
271 $prod = new Product($db);
272 
273 $num = 0;
274 
275 $title = $langs->trans('StockAtDate');
276 
277 $sql = 'SELECT p.rowid, p.ref, p.label, p.description, p.price, p.pmp,';
278 $sql .= ' p.price_ttc, p.price_base_type, p.fk_product_type, p.desiredstock, p.seuil_stock_alerte,';
279 $sql .= ' p.tms as datem, p.duration, p.tobuy, p.stock, ';
280 if (!empty($search_fk_warehouse)) {
281  $sql .= " SUM(p.pmp * ps.reel) as currentvalue, SUM(p.price * ps.reel) as sellvalue";
282  $sql .= ', SUM(ps.reel) as stock_reel';
283 } else {
284  $sql .= " SUM(p.pmp * p.stock) as currentvalue, SUM(p.price * p.stock) as sellvalue";
285 }
286 // Add fields from hooks
287 $parameters = array();
288 $reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters); // Note that $action and $object may have been modified by hook
289 $sql .= $hookmanager->resPrint;
290 
291 $sql .= ' FROM '.MAIN_DB_PREFIX.'product as p';
292 if (!empty($search_fk_warehouse)) {
293  $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as ps ON p.rowid = ps.fk_product AND ps.fk_entrepot IN ('.$db->sanitize(implode(",", $search_fk_warehouse)).")";
294 }
295 // Add fields from hooks
296 $parameters = array();
297 $reshook = $hookmanager->executeHooks('printFieldListJoin', $parameters); // Note that $action and $object may have been modified by hook
298 $sql .= $hookmanager->resPrint;
299 $sql .= ' WHERE p.entity IN ('.getEntity('product').')';
300 if ($productid > 0) {
301  $sql .= " AND p.rowid = ".((int) $productid);
302 }
303 if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
304  $sql .= " AND p.fk_product_type = 0";
305 }
306 if (!empty($canvas)) {
307  $sql .= " AND p.canvas = '".$db->escape($canvas)."'";
308 }
309 if ($search_ref) {
310  $sql .= natural_search('p.ref', $search_ref);
311 }
312 if ($search_nom) {
313  $sql .= natural_search('p.label', $search_nom);
314 }
315 $sql .= ' GROUP BY p.rowid, p.ref, p.label, p.description, p.price, p.pmp, p.price_ttc, p.price_base_type, p.fk_product_type, p.desiredstock, p.seuil_stock_alerte,';
316 $sql .= ' p.tms, p.duration, p.tobuy, p.stock';
317 // Add where from hooks
318 $parameters = array();
319 $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
320 $sql .= $hookmanager->resPrint;
321 
322 if ($sortfield == 'stock_reel' && empty($search_fk_warehouse)) {
323  $sortfield = 'stock';
324 }
325 if ($sortfield == 'stock' && !empty($search_fk_warehouse)) {
326  $sortfield = 'stock_reel';
327 }
328 $sql .= $db->order($sortfield, $sortorder);
329 
330 $nbtotalofrecords = '';
331 if ($date && $dateIsValid) { // We avoid a heavy sql if mandatory parameter date not yet defined
332  if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
333  $result = $db->query($sql);
334  $nbtotalofrecords = $db->num_rows($result);
335  if (($page * $limit) > $nbtotalofrecords || $ext == 'csv') { // if total resultset is smaller then paging size (filtering), goto and load page 0
336  $page = 0;
337  $offset = 0;
338  }
339  }
340 
341  //print $sql;
342  if ($ext != 'csv') {
343  $sql .= $db->plimit($limit + 1, $offset);
344  $resql = $db->query($sql);
345  } else {
346  $resql = $result;
347  $limit = 0;
348  }
349  if (empty($resql)) {
350  dol_print_error($db);
351  exit;
352  }
353 
354  $num = $db->num_rows($resql);
355 }
356 
357 $i = 0;
358 
359 $helpurl = 'EN:Module_Stocks_En|FR:Module_Stock|';
360 $helpurl .= 'ES:M&oacute;dulo_Stocks';
361 
362 $stocklabel = $langs->trans('StockAtDate');
363 if ($mode == 'future') {
364  $stocklabel = $langs->trans("VirtualStockAtDate");
365 }
366 
367 // TODO Move this action into a separated files: We should not mix output with MIME type HTML and MIME type CSV in the same file.
368 if ($ext == 'csv') {
369  top_httphead("text/csv");
370  //header("Content-Type: text/csv");
371  header("Content-Disposition: attachment; filename=stock".($date?'-'.date("Y-m-d", $date):'').".csv");
372 
373  // Lines of title
374  print implode(";", ($mode == 'future') ?
375  array('"Product Reference"', '"Label"', '"Current Stock"', '"'.$stocklabel.'"', '"Virtual Stock"'):
376  array('"Product Reference"', '"Label"', '"'.$stocklabel.'"', '"Estimated Stock Value"', '"Estimate Sell Value"', '"Movements"', '"Current Stock"'))."\r\n";
377 } else {
378  llxHeader('', $title, $helpurl, '');
379 
380  $head = array();
381 
382  $head[0][0] = DOL_URL_ROOT.'/product/stock/stockatdate.php';
383  $head[0][1] = $langs->trans("StockAtDateInPast");
384  $head[0][2] = 'stockatdatepast';
385 
386  $head[1][0] = DOL_URL_ROOT.'/product/stock/stockatdate.php?mode=future';
387  $head[1][1] = $langs->trans("StockAtDateInFuture");
388  $head[1][2] = 'stockatdatefuture';
389 
390 
391  print load_fiche_titre($langs->trans('StockAtDate'), '', 'stock');
392 
393  print dol_get_fiche_head($head, ($mode == 'future' ? 'stockatdatefuture' : 'stockatdatepast'), '', -1, '');
394 
395  $desc = $langs->trans("StockAtDatePastDesc");
396  if ($mode == 'future') {
397  $desc = $langs->trans("StockAtDateFutureDesc");
398  }
399  print '<span class="opacitymedium">'.$desc.'</span><br>'."\n";
400  print '<br>'."\n";
401 
402  print '<form name="formFilterWarehouse" method="POST" action="'.$_SERVER["PHP_SELF"].'">';
403  print '<input type="hidden" name="token" value="'.newToken().'">';
404  print '<input type="hidden" name="action" value="filter">';
405  print '<input type="hidden" name="mode" value="'.$mode.'">';
406 
407  print '<div class="inline-block valignmiddle" style="padding-right: 20px;">';
408  print '<span class="fieldrequired">'.$langs->trans('Date').'</span> '.$form->selectDate(($date ? $date : -1), 'date');
409 
410  print ' <span class="clearbothonsmartphone marginleftonly paddingleftonly marginrightonly paddingrightonly">&nbsp;</span> ';
411  print img_picto('', 'product', 'class="pictofiwedwidth"').' ';
412  print '</span> ';
413  print $form->select_produits($productid, 'productid', '', 0, 0, -1, 2, '', 0, array(), 0, $langs->trans('Product'), 0, 'maxwidth300', 0, '', null, 1);
414 
415  if ($mode != 'future') {
416  // A virtual stock in future has no sense on a per warehouse view, so no filter on warehouse is available for stock at date in future
417  print ' <span class="clearbothonsmartphone marginleftonly paddingleftonly marginrightonly paddingrightonly">&nbsp;</span> ';
418  print img_picto('', 'stock', 'class="pictofixedwidth"').$langs->trans("Warehouse").' :';
419  print '</span> ';
420  $selected = ((GETPOSTISSET('search_fk_warehouse') || GETPOSTISSET('fk_warehouse')) ? $search_fk_warehouse : 'ifonenodefault');
421  print $formproduct->selectWarehouses($selected, 'search_fk_warehouse', '', 1, 0, 0, $langs->trans('Warehouse'), 0, 0, null, 'minwidth200', null, 1, false, 'e.ref', 1);
422  }
423 
424  print '</div>';
425 
426  $parameters = array();
427  $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook
428  if (empty($reshook)) {
429  print $hookmanager->resPrint;
430  }
431 
432  print '<div class="inline-block valignmiddle">';
433  print '<input type="submit" class="button" name="valid" value="'.$langs->trans('Refresh').'">';
434  print '</div>';
435 
436  //print '</form>';
437 
438  $param = '';
439  if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) {
440  $param .= '&contextpage='.urlencode($contextpage);
441  }
442  if ($limit > 0 && $limit != $conf->liste_limit) {
443  $param .= '&limit='.((int) $limit);
444  }
445  $param .= '&mode='.$mode;
446  $param_warehouse = '';
447  if (!empty($search_fk_warehouse)) {
448  foreach ($search_fk_warehouse as $val) {
449  $param_warehouse .= '&search_fk_warehouse[]='.$val;
450  }
451  $param .= $param_warehouse;
452  }
453  if ($productid > 0) {
454  $param .= '&productid='.(int) $productid;
455  }
456  if (GETPOSTINT('dateday') > 0) {
457  $param .= '&dateday='.GETPOSTINT('dateday');
458  }
459  if (GETPOSTINT('datemonth') > 0) {
460  $param .= '&datemonth='.GETPOSTINT('datemonth');
461  }
462  if (GETPOSTINT('dateyear') > 0) {
463  $param .= '&dateyear='.GETPOSTINT('dateyear');
464  }
465 
466  // TODO Move this into the title line ?
467  print_barre_liste('', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'stock', 0, '', '', $limit, 0, 0, 1);
468 
469  print '<div class="div-table-responsive">'; // You can use div-table-responsive-no-min if you don't need reserved height for your table
470  if ($num) {
471  print '<p>';
472  print '<a href="stockatdate.php?output=csv&sortfield='.urlencode($sortfield).'&sortorder='.urlencode($sortorder).'&type='.((int) $type).'&mode='.urlencode($mode).
473  (($productid > 0)?"&productid=".urlencode($productid):'').
474  $param_warehouse.
475  "&search_ref=".dol_escape_htmltag($search_ref).
476  "&search_nom=".dol_escape_htmltag($search_nom).
477  (GETPOSTISSET('dateday')?"&dateday=".GETPOSTINT('dateday'):'').
478  (GETPOSTISSET('datemonth')?"&datemonth=".GETPOSTINT('datemonth'):'').
479  (GETPOSTISSET('dateyear')?"&dateyear=".GETPOSTINT('dateyear'):'').
480  '" title="Download CSV" />';
481  print img_picto('', 'download', 'class="pictofixedwidth"');
482  print 'Download CSV';
483  print '</a>';
484  print '</p>';
485  }
486  print '<table class="liste centpercent">';
487 
488  print '<input type="hidden" name="token" value="'.newToken().'">';
489  print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
490  print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
491  print '<input type="hidden" name="type" value="'.$type.'">';
492  print '<input type="hidden" name="mode" value="'.$mode.'">';
493 
494  // Fields title search
495  print '<tr class="liste_titre_filter">';
496  print '<td class="liste_titre"><input class="flat" type="text" name="search_ref" size="8" value="'.dol_escape_htmltag($search_ref).'"></td>';
497  print '<td class="liste_titre"><input class="flat" type="text" name="search_nom" size="8" value="'.dol_escape_htmltag($search_nom).'"></td>';
498  print '<td class="liste_titre"></td>';
499  print '<td class="liste_titre"></td>';
500  print '<td class="liste_titre"></td>';
501  if ($mode == 'future') {
502  print '<td class="liste_titre"></td>';
503  } else {
504  print '<td class="liste_titre"></td>';
505  print '<td class="liste_titre"></td>';
506  }
507  // Fields from hook
508  $parameters = array('param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder);
509  $reshook = $hookmanager->executeHooks('printFieldListOption', $parameters); // Note that $action and $object may have been modified by hook
510  print $hookmanager->resPrint;
511 
512  print '<td class="liste_titre maxwidthsearch">';
513  $searchpicto = $form->showFilterAndCheckAddButtons(0);
514  print $searchpicto;
515  print '</td>';
516  print '</tr>';
517 
518  $fieldtosortcurrentstock = 'stock';
519  if (!empty($search_fk_warehouse)) {
520  $fieldtosortcurrentstock = 'stock_reel';
521  }
522 
523  // Lines of title
524  print '<tr class="liste_titre">';
525  print_liste_field_titre('ProductRef', $_SERVER["PHP_SELF"], 'p.ref', $param, '', '', $sortfield, $sortorder);
526  print_liste_field_titre('Label', $_SERVER["PHP_SELF"], 'p.label', $param, '', '', $sortfield, $sortorder);
527 
528  if ($mode == 'future') {
529  print_liste_field_titre('CurrentStock', $_SERVER["PHP_SELF"], $fieldtosortcurrentstock, $param, '', '', $sortfield, $sortorder, 'right ');
530  print_liste_field_titre('', $_SERVER["PHP_SELF"]);
531  print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ', 'VirtualStockAtDateDesc');
532  print_liste_field_titre('VirtualStock', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ', 'VirtualStockDesc');
533  } else {
534  print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ');
535  $tooltiptext = $langs->trans("QtyAtDate").' x '.$langs->trans("AverageUnitPricePMPShort").' ('.$langs->trans("Currently").')';
536  print_liste_field_titre("EstimatedStockValue", $_SERVER["PHP_SELF"], "estimatedvalue", '', $param, '', $sortfield, $sortorder, 'right ', $tooltiptext, 1);
537  $tooltiptext = $langs->trans("QtyAtDate").' x '.$langs->trans("SellingPrice").' ('.$langs->trans("Currently").')';
538  print_liste_field_titre("EstimatedStockValueSell", $_SERVER["PHP_SELF"], "", '', $param, '', $sortfield, $sortorder, 'right ', $tooltiptext, 1);
539  $tooltiptext = $langs->trans("MovementsSinceDate");
540  print_liste_field_titre('', $_SERVER["PHP_SELF"], '', '', $param, '', '', '', 'right ', $tooltiptext, 1);
541  print_liste_field_titre('CurrentStock', $_SERVER["PHP_SELF"], $fieldtosortcurrentstock, $param, '', '', $sortfield, $sortorder, 'right ');
542  }
543 
544  // Hook fields
545  $parameters = array('param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder);
546  $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook
547  print $hookmanager->resPrint;
548 
549  print_liste_field_titre('', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ');
550 
551  print "</tr>\n";
552 }
553 
554 $totalbuyingprice = 0;
555 $totalsellingprice = 0;
556 $totalcurrentstock = 0;
557 $totalvirtualstock = 0;
558 
559 $i = 0;
560 while ($i < ($limit ? min($num, $limit) : $num)) {
561  $objp = $db->fetch_object($resql);
562 
563  if (getDolGlobalString('STOCK_SUPPORTS_SERVICES') || $objp->fk_product_type == 0) {
564  $prod->fetch($objp->rowid);
565 
566  // Multilangs
567  /*if (getDolGlobalInt('MAIN_MULTILANGS'))
568  {
569  $sql = 'SELECT label,description';
570  $sql .= ' FROM '.MAIN_DB_PREFIX.'product_lang';
571  $sql .= ' WHERE fk_product = '.((int) $objp->rowid);
572  $sql .= " AND lang = '".$db->escape($langs->getDefaultLang())."'";
573  $sql .= ' LIMIT 1';
574 
575  $resqlm = $db->query($sql);
576  if ($resqlm)
577  {
578  $objtp = $db->fetch_object($resqlm);
579  if (!empty($objtp->description)) $objp->description = $objtp->description;
580  if (!empty($objtp->label)) $objp->label = $objtp->label;
581  }
582  }*/
583 
584  $currentstock = '';
585  if (!empty($search_fk_warehouse)) {
586  //if ($productid > 0) {
587  foreach ($search_fk_warehouse as $val) {
588  if (!is_numeric($currentstock)) {
589  $currentstock = 0;
590  }
591  $currentstock += empty($stock_prod_warehouse[$objp->rowid][$val]) ? 0 : $stock_prod_warehouse[$objp->rowid][$val];
592  }
593  //} else {
594  // $currentstock = $objp->stock_reel;
595  //}
596  } else {
597  //if ($productid > 0) {
598  $currentstock = empty($stock_prod[$objp->rowid]) ? 0 : $stock_prod[$objp->rowid];
599  //} else {
600  // $currentstock = $objp->stock;
601  //}
602  }
603 
604  if ($mode == 'future') {
605  $prod->load_stock('warehouseopen,warehouseinternal,nobatch', 0, $dateendofday);
606  $stock = $prod->stock_theorique; // virtual stock at a date
607  $prod->load_stock('warehouseopen,warehouseinternal,nobatch', 0);
608  $virtualstock = $prod->stock_theorique; // virtual stock in infinite future
609  } else {
610  $stock = $currentstock;
611  $nbofmovement = 0;
612  if (!empty($search_fk_warehouse)) {
613  foreach ($search_fk_warehouse as $val) {
614  $stock -= empty($movements_prod_warehouse[$objp->rowid][$val]) ? 0 : $movements_prod_warehouse[$objp->rowid][$val];
615  $nbofmovement += empty($movements_prod_warehouse_nb[$objp->rowid][$val]) ? 0 : $movements_prod_warehouse_nb[$objp->rowid][$val];
616  }
617  } else {
618  $stock -= empty($movements_prod[$objp->rowid]) ? 0 : $movements_prod[$objp->rowid];
619  $nbofmovement += empty($movements_prod_nb[$objp->rowid]) ? 0 : $movements_prod_nb[$objp->rowid];
620  }
621  }
622 
623 
624  if ($ext == 'csv') {
625  if ($mode == 'future') {
626  print implode(";", array(
627  '"'.$objp->ref.'"',
628  '"'.$objp->label.'"',
629  '"'.price2num($currentstock, 'MS').'"',
630  '"'.price2num($stock, 'MS').'"',
631  '"'.price2num($virtualstock, 'MS').'"'))."\r\n";
632  $totalvirtualstock += $virtualstock;
633  } else {
634  print implode(";", array(
635  '"'.$objp->ref.'"',
636  '"'.$objp->label.'"',
637  '"'.price(price2num($stock, 'MS')).'"',
638  price2num($stock * $objp->pmp, 'MT')?'"'.price2num($stock * $objp->pmp, 'MT').'"':'',
639  !getDolGlobalString('PRODUIT_MULTIPRICES')?'"'.price2num($stock * $objp->price, 'MT').'"':'"'.$langs->trans("Variable").'('.$langs->trans("OptionMULTIPRICESIsOn").')"',
640  "$nbofmovement",
641  '"'.price2num($currentstock, 'MS').'"'))."\r\n";
642  $totalbuyingprice += $stock * $objp->pmp;
643  $totalsellingprice += $stock * $objp->price;
644  }
645  $totalcurrentstock += $currentstock;
646  } else {
647  print '<tr class="oddeven">';
648 
649  // Product ref
650  print '<td class="nowrap">'.$prod->getNomUrl(1, '').'</td>';
651 
652  // Product label
653  print '<td>';
654  print dol_escape_htmltag($objp->label);
655  print '</td>';
656 
657  if ($mode == 'future') {
658  // Current stock
659  print '<td class="right">'.price(price2num($currentstock, 'MS')).'</td>';
660  //$totalcurrentstock += $currentstock;
661 
662  print '<td class="right"></td>';
663 
664  // Virtual stock at date
665  print '<td class="right">'.price(price2num($stock, 'MS')).'</td>';
666 
667  // Final virtual stock
668  print '<td class="right">'.price(price2num($virtualstock, 'MS')).'</td>';
669  $totalvirtualstock += $virtualstock;
670  } else {
671  // Stock at date
672  print '<td class="right">'.($stock ? price(price2num($stock, 'MS')) : ('<span class="opacitymedium">0</span>')).'</td>';
673 
674  // PMP value
675  $estimatedvalue = $stock * $objp->pmp;
676  print '<td class="right" title="'.dolPrintHTMLForAttribute($langs->trans("AverageUnitPricePMPShort").' ('.$langs->trans("Currently").'): '.price(price2num($objp->pmp, 'MU'), 1)).'">';
677  if (price2num($estimatedvalue, 'MT')) {
678  print '<span class="amount">'.price(price2num($estimatedvalue, 'MT'), 1).'</span>';
679  } else {
680  print '';
681  }
682  $totalbuyingprice += $estimatedvalue;
683  print '</td>';
684 
685  // Selling value
686  print '<td class="right"';
687  if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
688  print ' title="'.dolPrintHTMLForAttribute($langs->trans("SellingPrice").' ('.$langs->trans("Currently").'): '.price(price2num($objp->price, 'MU'), 1));
689  }
690  print '">';
691  if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
692  print '<span class="amount">';
693  if ($stock || (float) ($stock * $objp->price)) {
694  print price(price2num($stock * $objp->price, 'MT'), 1);
695  }
696  print '</span>';
697  $totalsellingprice += $stock * $objp->price;
698  } else {
699  $htmltext = $langs->trans("OptionMULTIPRICESIsOn");
700  print $form->textwithtooltip('<span class="opacitymedium">'.$langs->trans("Variable").'</span>', $htmltext);
701  }
702  print '</td>';
703 
704  // Movements
705  print '<td class="right">';
706  if ($nbofmovement > 0) {
707  $url = DOL_URL_ROOT.'/product/stock/movement_list.php?idproduct='.$objp->rowid;
708  if (GETPOSTISSET('datemonth')) {
709  $url .= '&search_date_startday='.GETPOSTINT('dateday');
710  $url .= '&search_date_startmonth='.GETPOSTINT('datemonth');
711  $url .= '&search_date_startyear='.GETPOSTINT('dateyear');
712  }
713  if (count($search_fk_warehouse) > 1) {
714  $url = ''; // Do not show link, multi warehouse as filter not managed yet by target page
715  } else {
716  foreach ($search_fk_warehouse as $val) {
717  $url .= ($val > 0 ? '&search_warehouse='.((int) $val) : '');
718  }
719  }
720  if ($url) {
721  print '<a href="'.$url.'">';
722  }
723  print $langs->trans("Movements");
724  print '<span class="tabs paddingleft"><span class="badge">'.$nbofmovement.'</span></span>';
725  if ($url) {
726  print '</a>';
727  }
728  }
729  print '</td>';
730 
731  // Current stock
732  print '<td class="right">'.($currentstock ? price(price2num($currentstock, 'MS')) : '<span class="opacitymedium">0</span>').'</td>';
733  }
734  $totalcurrentstock += $currentstock;
735 
736  // Fields from hook
737  $parameters = array('objp'=>$objp);
738  $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters); // Note that $action and $object may have been modified by hook
739  print $hookmanager->resPrint;
740 
741  // Action
742  print '<td class="right"></td>';
743 
744  print '</tr>'."\n";
745  }
746  }
747  $i++;
748 }
749 
750 $parameters = array('sql'=>$sql);
751 $reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters); // Note that $action and $object may have been modified by hook
752 if ($ext!='csv') {
753  print $hookmanager->resPrint;
754 }
755 
756 $colspan = 8;
757 if ($mode == 'future') {
758  $colspan++;
759 }
760 
761 if ($ext=='csv') {
762  print implode(";",
763  ($mode == 'future')?array(
764  '"'.$langs->trans("Totalforthispage").'"',
765  '',
766  $productid > 0 ? price2num($totalcurrentstock, 'MS') : '',
767  '',
768  price(price2num($totalvirtualstock, 'MS'))):
769  array(
770  '"'.$langs->trans("Totalforthispage").'"',
771  '',
772  '',
773  '"'.price2num($totalbuyingprice, 'MT').'"',
774  !getDolGlobalString('PRODUIT_MULTIPRICES')?'"'.price2num($totalsellingprice, 'MT').'"':'',
775  '',
776  $productid > 0 ? price2num($totalcurrentstock, 'MS') : ''));
777 } else {
778  if (empty($date) || !$dateIsValid) {
779  print '<tr><td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("EnterADateCriteria").'</span></td></tr>';
780  } else {
781  print '<tr class="liste_total">';
782  print '<td>'.$langs->trans("Totalforthispage").'</td>';
783  print '<td></td>';
784  if ($mode == 'future') {
785  print '<td class="right">'.price(price2num($totalcurrentstock, 'MS')).'</td>';
786  print '<td></td>';
787  print '<td></td>';
788  print '<td class="right">'.price(price2num($totalvirtualstock, 'MS')).'</td>';
789  } else {
790  print '<td></td>';
791  print '<td class="right">'.price(price2num($totalbuyingprice, 'MT')).'</td>';
792  if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
793  print '<td class="right">'.price(price2num($totalsellingprice, 'MT')).'</td>';
794  } else {
795  print '<td></td>';
796  }
797  print '<td></td>';
798  print '<td class="right">'.($productid > 0 ? price(price2num($totalcurrentstock, 'MS')) : '').'</td>';
799  }
800  print '<td></td>';
801  print '</tr>';
802  }
803 
804  print '</table>';
805  print '</div>';
806 
807  print dol_get_fiche_end();
808 
809  print '</form>';
810 
811  llxFooter();
812 }
813 
814 if (!empty($resql)) {
815  $db->free($resql);
816 }
817 
818 $db->close();
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
if(!defined('NOREQUIRESOC')) if(!defined('NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) llxHeader()
Empty header.
Definition: wrapper.php:55
llxFooter()
Empty footer.
Definition: wrapper.php:69
const STATUS_OPEN_INTERNAL
Warehouse open and only operations for stock transfers/corrections allowed (not for customer shipping...
const STATUS_OPEN_ALL
Warehouse open and any operations are allowed (customer shipping, supplier dispatch,...
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 products or services.
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('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') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:745
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
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.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
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...
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
print_barre_liste($title, $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.
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.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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.
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...
if(!defined('NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
Definition: main.inc.php:1648
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.