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