dolibarr 23.0.4
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$dateIsValid = true;
117if ($mode == 'future') {
118 if ($date && $date < $now) {
119 setEventMessages($langs->trans("ErrorDateMustBeInFuture"), null, 'errors');
120 $dateIsValid = false;
121 }
122} else {
123 if ($date && $date > $now) {
124 setEventMessages($langs->trans("ErrorDateMustBeBeforeToday"), null, 'errors');
125 $dateIsValid = false;
126 }
127}
128
129
130/*
131 * Actions
132 */
133
134$parameters = array();
135$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
136if ($reshook < 0) {
137 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
138}
139
140if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // Both test are required to be compatible with all browsers
141 $date = '';
142 $productid = 0;
143 $search_fk_warehouse = array();
144 $search_ref = '';
145 $search_nom = '';
146}
147
148$warehouseStatus = array();
149if (getDolGlobalString('ENTREPOT_EXTRA_STATUS')) {
150 //$warehouseStatus[] = Entrepot::STATUS_CLOSED;
151 $warehouseStatus[] = Entrepot::STATUS_OPEN_ALL;
152 $warehouseStatus[] = Entrepot::STATUS_OPEN_INTERNAL;
153}
154
155// Get array with current stock per product, warehouse
156$stock_prod_warehouse = array();
157$stock_prod = array();
158if ($date && $dateIsValid) { // Avoid heavy sql if mandatory date is not defined
159 $sql = "SELECT ps.fk_product, ps.fk_entrepot as fk_warehouse,";
160 $sql .= " SUM(ps.reel) AS stock";
161 $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
162 $sql .= ", ".MAIN_DB_PREFIX."entrepot as w";
163 $sql .= ", ".MAIN_DB_PREFIX."product as p";
164 $sql .= " WHERE w.entity IN (".getEntity('stock').")";
165 $sql .= " AND w.rowid = ps.fk_entrepot AND p.rowid = ps.fk_product";
166 if (getDolGlobalString('ENTREPOT_EXTRA_STATUS') && count($warehouseStatus)) {
167 $sql .= " AND w.statut IN (".$db->sanitize(implode(',', $warehouseStatus)).")";
168 }
169 if ($productid > 0) {
170 $sql .= " AND ps.fk_product = ".((int) $productid);
171 }
172 if (! empty($search_fk_warehouse)) {
173 $sql .= " AND ps.fk_entrepot IN (".$db->sanitize(implode(",", $search_fk_warehouse)).")";
174 }
175 if ($search_ref) {
176 $sql .= natural_search("p.ref", $search_ref);
177 }
178 if ($search_nom) {
179 $sql .= natural_search("p.label", $search_nom);
180 }
181 $sql .= " GROUP BY fk_product, fk_entrepot";
182 //print $sql;
183
184 $resql = $db->query($sql);
185 if ($resql) {
186 $num = $db->num_rows($resql);
187 $i = 0;
188
189 while ($i < $num) {
190 $obj = $db->fetch_object($resql);
191
192 $tmp_fk_product = $obj->fk_product;
193 $tmp_fk_warehouse = $obj->fk_warehouse;
194 $stock = $obj->stock;
195
196 $stock_prod_warehouse[$tmp_fk_product][$tmp_fk_warehouse] = $stock;
197 $stock_prod[$tmp_fk_product] = (isset($stock_prod[$tmp_fk_product]) ? $stock_prod[$tmp_fk_product] : 0) + $stock;
198
199 $i++;
200 }
201
202 $db->free($resql);
203 $resql = null; // so the free() at the end of the page does not run on an already closed result
204 } else {
205 dol_print_error($db);
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 $resql = null; // so the free() at the end of the page does not run on an already closed result
272 } else {
273 dol_print_error($db);
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 {
373 dol_print_error($db);
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)) {
391 dol_print_error($db);
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.'"', '"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 (getDolGlobalString('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 (!getDolGlobalString('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 (getDolGlobalString('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 print_liste_field_titre("EstimatedStockValue", $_SERVER["PHP_SELF"], "currentvalue", '', $param, '', $sortfield, $sortorder, 'right ', $tooltiptext, 1);
591 $tooltiptext = $langs->trans("QtyAtDate").' x '.$langs->trans("SellingPrice").' ('.$langs->trans("Currently").')';
592 print_liste_field_titre("EstimatedStockValueSell", $_SERVER["PHP_SELF"], "", '', $param, '', $sortfield, $sortorder, 'right ', $tooltiptext, 1);
593 $tooltiptext = $langs->trans("MovementsSinceDate");
594 print_liste_field_titre("Movements", $_SERVER["PHP_SELF"], '', '', $param, '', '', '', 'right ', $tooltiptext, 1);
595 print_liste_field_titre('CurrentStock', $_SERVER["PHP_SELF"], $fieldtosortcurrentstock, '', $param, '', $sortfield, $sortorder, 'right ');
596 }
597
598 // Hook fields
599 $parameters = array('param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder);
600 $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook
601 print $hookmanager->resPrint;
602
603 // Action column
604 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
605 print_liste_field_titre('', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ');
606 }
607
608 print "</tr>\n";
609}
610
611$totalbuyingprice = 0;
612$totalsellingprice = 0;
613$totalcurrentstock = 0;
614$totalvirtualstock = 0;
615
616$i = 0;
617while ($i < ($limit ? min($num, $limit) : $num)) {
618 if (empty($resql)) {
619 break;
620 }
621
622 $objp = $db->fetch_object($resql);
623
624 if (getDolGlobalString('STOCK_SUPPORTS_SERVICES') || $objp->fk_product_type == 0) {
625 $prod->fetch($objp->rowid);
626
627 // Multilangs
628 /*if (getDolGlobalInt('MAIN_MULTILANGS'))
629 {
630 $sql = 'SELECT label,description';
631 $sql .= ' FROM '.MAIN_DB_PREFIX.'product_lang';
632 $sql .= ' WHERE fk_product = '.((int) $objp->rowid);
633 $sql .= " AND lang = '".$db->escape($langs->getDefaultLang())."'";
634 $sql .= ' LIMIT 1';
635
636 $resqlm = $db->query($sql);
637 if ($resqlm)
638 {
639 $objtp = $db->fetch_object($resqlm);
640 if (!empty($objtp->description)) $objp->description = $objtp->description;
641 if (!empty($objtp->label)) $objp->label = $objtp->label;
642 }
643 }*/
644
645 $currentstock = '';
646 if (!empty($search_fk_warehouse)) {
647 //if ($productid > 0) {
648 foreach ($search_fk_warehouse as $val) {
649 if (!is_numeric($currentstock)) {
650 $currentstock = 0;
651 }
652 $currentstock += empty($stock_prod_warehouse[$objp->rowid][$val]) ? 0 : $stock_prod_warehouse[$objp->rowid][$val];
653 }
654 //} else {
655 // $currentstock = $objp->stock_reel;
656 //}
657 } else {
658 //if ($productid > 0) {
659 $currentstock = empty($stock_prod[$objp->rowid]) ? 0 : $stock_prod[$objp->rowid];
660 //} else {
661 // $currentstock = $objp->stock;
662 //}
663 }
664
665 $nbofmovement = 0;
666 $virtualstock = 0;
667 if ($mode == 'future') {
668 $prod->load_stock('warehouseopen,warehouseinternal,nobatch', 0, $dateendofday);
669 $stock = $prod->stock_theorique; // virtual stock at a date
670 $prod->load_stock('warehouseopen,warehouseinternal,nobatch', 0);
671 $virtualstock = $prod->stock_theorique; // virtual stock in infinite future
672 } else {
673 $stock = $currentstock;
674 if (!empty($search_fk_warehouse)) {
675 foreach ($search_fk_warehouse as $val) {
676 $stock -= empty($movements_prod_warehouse[$objp->rowid][$val]) ? 0 : $movements_prod_warehouse[$objp->rowid][$val];
677 $nbofmovement += empty($movements_prod_warehouse_nb[$objp->rowid][$val]) ? 0 : $movements_prod_warehouse_nb[$objp->rowid][$val];
678 }
679 } else {
680 $stock -= empty($movements_prod[$objp->rowid]) ? 0 : $movements_prod[$objp->rowid];
681 $nbofmovement += empty($movements_prod_nb[$objp->rowid]) ? 0 : $movements_prod_nb[$objp->rowid];
682 }
683 }
684
685
686 if ($ext == 'csv') {
687 if ($mode == 'future') {
688 print implode(";", array(
689 '"'.$objp->ref.'"',
690 '"'.$objp->label.'"',
691 '"'.price2num($currentstock, 'MS').'"',
692 '"'.price2num($stock, 'MS').'"',
693 '"'.price2num($virtualstock, 'MS').'"'))."\r\n";
694 $totalvirtualstock += $virtualstock;
695 } else {
696 print implode(";", array(
697 '"'.$objp->ref.'"',
698 '"'.$objp->label.'"',
699 '"'.price(price2num($stock, 'MS')).'"',
700 price2num($stock * $objp->pmp, 'MT') ? '"'.price2num($stock * $objp->pmp, 'MT').'"' : '',
701 (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) ? '"'.price2num($stock * $objp->price, 'MT').'"' : '"'.$langs->trans("Variable").'('.$langs->trans("OptionMULTIPRICESIsOn").')"',
702 "$nbofmovement",
703 '"'.price2num($currentstock, 'MS').'"'))."\r\n";
704 $totalbuyingprice += $stock * $objp->pmp;
705 $totalsellingprice += $stock * $objp->price;
706 }
707 $totalcurrentstock += $currentstock;
708 } else {
709 print '<tr class="oddeven">';
710
711 // Action column
712 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
713 print '<td class="left"></td>';
714 }
715
716 // Product ref
717 print '<td class="nowrap">'.$prod->getNomUrl(1, '').'</td>';
718
719 // Product label
720 print '<td>';
721 print dol_escape_htmltag($objp->label);
722 print '</td>';
723
724 if ($mode == 'future') {
725 // Current stock
726 print '<td class="right">'.price(price2num($currentstock, 'MS')).'</td>';
727 //$totalcurrentstock += $currentstock;
728
729 print '<td class="right"></td>';
730
731 // Virtual stock at date
732 print '<td class="right">'.price(price2num($stock, 'MS')).'</td>';
733
734 // Final virtual stock
735 print '<td class="right">'.price(price2num($virtualstock, 'MS')).'</td>';
736 $totalvirtualstock += $virtualstock;
737 } else {
738 // Stock at date
739 print '<td class="right">'.($stock ? price(price2num($stock, 'MS')) : ('<span class="opacitymedium">0</span>')).'</td>';
740
741 // PMP value
742 $estimatedvalue = $stock * $objp->pmp;
743 print '<td class="right" title="'.dolPrintHTMLForAttribute($langs->trans("AverageUnitPricePMPShort").' ('.$langs->trans("Currently").'): '.price(price2num($objp->pmp, 'MU'), 1)).'">';
744 if (price2num($estimatedvalue, 'MT')) {
745 print '<span class="amount">'.price(price2num($estimatedvalue, 'MT'), 1).'</span>';
746 } else {
747 print '';
748 }
749 $totalbuyingprice += $estimatedvalue;
750 print '</td>';
751
752 // Selling value
753 print '<td class="right"';
754 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
755 print ' title="'.dolPrintHTMLForAttribute($langs->trans("SellingPrice").' ('.$langs->trans("Currently").'): '.price(price2num($objp->price, 'MU'), 1));
756 }
757 print '">';
758 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
759 print '<span class="amount">';
760 if ($stock || (float) ($stock * $objp->price)) {
761 print price(price2num($stock * $objp->price, 'MT'), 1);
762 }
763 print '</span>';
764 $totalsellingprice += $stock * $objp->price;
765 } else {
766 $htmltext = $langs->trans("OptionMULTIPRICESIsOn");
767 print $form->textwithtooltip('<span class="opacitymedium">'.$langs->trans("Variable").'</span>', $htmltext);
768 }
769 print '</td>';
770
771 // Movements
772 print '<td class="right">';
773 if ($nbofmovement > 0) {
774 $url = DOL_URL_ROOT.'/product/stock/movement_list.php?idproduct='.$objp->rowid;
775 if (GETPOSTISSET('datemonth')) {
776 $url .= '&search_date_startday='.GETPOSTINT('dateday');
777 $url .= '&search_date_startmonth='.GETPOSTINT('datemonth');
778 $url .= '&search_date_startyear='.GETPOSTINT('dateyear');
779 }
780 if (count($search_fk_warehouse) > 1) {
781 $url = ''; // Do not show link, multi warehouse as filter not managed yet by target page
782 } else {
783 foreach ($search_fk_warehouse as $val) {
784 $url .= ($val > 0 ? '&search_warehouse='.((int) $val) : '');
785 }
786 }
787 if ($url) {
788 print '<a href="'.$url.'">';
789 }
790 print $langs->trans("Movements");
791 print '<span class="tabs paddingleft"><span class="badge">'.$nbofmovement.'</span></span>';
792 if ($url) {
793 print '</a>';
794 }
795 }
796 print '</td>';
797
798 // Current stock
799 print '<td class="right">'.($currentstock ? price(price2num($currentstock, 'MS')) : '<span class="opacitymedium">0</span>').'</td>';
800 }
801 $totalcurrentstock += $currentstock;
802
803 // Fields from hook
804 $parameters = array('objp' => $objp);
805 $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters); // Note that $action and $object may have been modified by hook
806 print $hookmanager->resPrint;
807
808 // Action column
809 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
810 print '<td class="right"></td>';
811 }
812
813 print '</tr>'."\n";
814 }
815 }
816 $i++;
817}
818
819$parameters = array('sql' => $sql);
820$reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters); // Note that $action and $object may have been modified by hook
821if ($ext != 'csv') {
822 print $hookmanager->resPrint;
823}
824
825$colspan = 8;
826if ($mode == 'future') {
827 $colspan++;
828}
829
830if ($ext == 'csv') {
831 print implode(
832 ";",
833 ($mode == 'future') ? array(
834 '"'.$langs->trans("Totalforthispage").'"',
835 '',
836 $productid > 0 ? price2num($totalcurrentstock, 'MS') : '',
837 '',
838 price(price2num($totalvirtualstock, 'MS'))) :
839 array(
840 '"'.$langs->trans("Totalforthispage").'"',
841 '',
842 '',
843 '"'.price2num($totalbuyingprice, 'MT').'"',
844 (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) ? '"'.price2num($totalsellingprice, 'MT').'"' : '',
845 '',
846 $productid > 0 ? price2num($totalcurrentstock, 'MS') : '')
847 );
848} else {
849 if (empty($date) || !$dateIsValid) {
850 print '<tr><td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("EnterADateCriteria").'</span></td></tr>';
851 } else {
852 print '<tr class="liste_total">';
853 print '<td>'.$langs->trans("Totalforthispage").'</td>';
854 print '<td></td>';
855 if ($mode == 'future') {
856 print '<td class="right">'.price(price2num($totalcurrentstock, 'MS')).'</td>';
857 print '<td></td>';
858 print '<td></td>';
859 print '<td class="right">'.price(price2num($totalvirtualstock, 'MS')).'</td>';
860 } else {
861 print '<td></td>';
862 print '<td class="right">'.price(price2num($totalbuyingprice, 'MT')).'</td>';
863 if (!getDolGlobalString('PRODUIT_MULTIPRICES') && !getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) {
864 print '<td class="right">'.price(price2num($totalsellingprice, 'MT')).'</td>';
865 } else {
866 print '<td></td>';
867 }
868 print '<td></td>';
869 print '<td class="right">'.($productid > 0 ? price(price2num($totalcurrentstock, 'MS')) : '').'</td>';
870 }
871 print '<td></td>';
872 print '</tr>';
873 }
874
875 print '</table>';
876 print '</div>';
877
878 print dol_get_fiche_end();
879
880 print '</form>';
881
882 llxFooter();
883}
884
885if (!empty($resql)) {
886 $db->free($resql);
887}
888
889$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:469
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.