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