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