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