dolibarr 22.0.5
stockatdate.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
3 * Copyright (C) 2013-2020 Laurent Destaileur <ely@users.sourceforge.net>
4 * Copyright (C) 2014 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2016 Juanjo Menent <jmenent@2byte.es>
6 * Copyright (C) 2016 ATM Consulting <support@atm-consulting.fr>
7 * Copyright (C) 2019-2024 Frédéric France <frederic.france@free.fr>
8 * 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';
33require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
34require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
35require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
36require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
37require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php';
38require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php';
39require_once './lib/replenishment.lib.php';
40
49// Load translation files required by the page
50$langs->loadLangs(array('products', 'stocks', 'orders'));
51
52// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
53$hookmanager->initHooks(array('stockatdate'));
54
55//checks if a product has been ordered
56
57$action = GETPOST('action', 'aZ09');
58$type = GETPOSTINT('type');
59$mode = GETPOST('mode', 'alpha');
60
61$ext = (GETPOSTISSET('output') && in_array(GETPOST('output'), array('csv'))) ? GETPOST('output') : '';
62
63$date = '';
64$dateendofday = '';
65if (GETPOSTISSET('dateday') && GETPOSTISSET('datemonth') && GETPOSTISSET('dateyear')) {
66 $date = dol_mktime(0, 0, 0, GETPOSTINT('datemonth'), GETPOSTINT('dateday'), GETPOSTINT('dateyear'));
67 $dateendofday = dol_mktime(23, 59, 59, GETPOSTINT('datemonth'), GETPOSTINT('dateday'), GETPOSTINT('dateyear'));
68}
69
70$search_ref = GETPOST('search_ref', 'alphanohtml');
71$search_nom = GETPOST('search_nom', 'alphanohtml');
72
73$now = dol_now();
74
75$productid = GETPOSTINT('productid');
76if (GETPOSTISARRAY('search_fk_warehouse')) {
77 $search_fk_warehouse = GETPOST('search_fk_warehouse', 'array:int');
78} else {
79 $search_fk_warehouse = array(GETPOSTINT('search_fk_warehouse'));
80}
81// For backward compatibility
82if (GETPOSTINT('fk_warehouse')) {
83 $search_fk_warehouse = array(GETPOSTINT('fk_warehouse'));
84}
85// Clean value -1
86foreach ($search_fk_warehouse as $key => $val) {
87 if ($val == -1 || empty($val)) {
88 unset($search_fk_warehouse[$key]);
89 }
90}
91
92$sortfield = GETPOST('sortfield', 'aZ09comma');
93$sortorder = GETPOST('sortorder', 'aZ09comma');
94$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
95if (empty($page) || $page == -1) {
96 $page = 0;
97} // If $page is not defined, or '' or -1
98$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
99$offset = $limit * $page;
100if (!$sortfield) {
101 $sortfield = 'p.ref';
102}
103if (!$sortorder) {
104 $sortorder = 'ASC';
105}
106
107$parameters = array();
108$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
109if ($reshook < 0) {
110 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
111}
112
113$dateIsValid = true;
114if ($mode == 'future') {
115 if ($date && $date < $now) {
116 setEventMessages($langs->trans("ErrorDateMustBeInFuture"), null, 'errors');
117 $dateIsValid = false;
118 }
119} else {
120 if ($date && $date > $now) {
121 setEventMessages($langs->trans("ErrorDateMustBeBeforeToday"), null, 'errors');
122 $dateIsValid = false;
123 }
124}
125
126// Security check
127if ($user->socid) {
128 $socid = $user->socid;
129}
130
131$result = restrictedArea($user, 'produit|service'); // Must have permission to read product
132$result = restrictedArea($user, 'stock'); // Must have permission to read stock
133
134
135/*
136 * Actions
137 */
138
139if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // Both test are required to be compatible with all browsers
140 $date = '';
141 $productid = 0;
142 $search_fk_warehouse = array();
143 $search_ref = '';
144 $search_nom = '';
145}
146
147$warehouseStatus = array();
148if (getDolGlobalString('ENTREPOT_EXTRA_STATUS')) {
149 //$warehouseStatus[] = Entrepot::STATUS_CLOSED;
150 $warehouseStatus[] = Entrepot::STATUS_OPEN_ALL;
151 $warehouseStatus[] = Entrepot::STATUS_OPEN_INTERNAL;
152}
153
154// Get array with current stock per product, warehouse
155$stock_prod_warehouse = array();
156$stock_prod = array();
157if ($date && $dateIsValid) { // Avoid heavy sql if mandatory date is not defined
158 $sql = "SELECT ps.fk_product, ps.fk_entrepot as fk_warehouse,";
159 $sql .= " SUM(ps.reel) AS stock";
160 $sql .= " FROM ".MAIN_DB_PREFIX."product_stock as ps";
161 $sql .= ", ".MAIN_DB_PREFIX."entrepot as w";
162 $sql .= ", ".MAIN_DB_PREFIX."product as p";
163 $sql .= " WHERE w.entity IN (".getEntity('stock').")";
164 $sql .= " AND w.rowid = ps.fk_entrepot AND p.rowid = ps.fk_product";
165 if (getDolGlobalString('ENTREPOT_EXTRA_STATUS') && count($warehouseStatus)) {
166 $sql .= " AND w.statut IN (".$db->sanitize(implode(',', $warehouseStatus)).")";
167 }
168 if ($productid > 0) {
169 $sql .= " AND ps.fk_product = ".((int) $productid);
170 }
171 if (! empty($search_fk_warehouse)) {
172 $sql .= " AND ps.fk_entrepot IN (".$db->sanitize(implode(",", $search_fk_warehouse)).")";
173 }
174 if ($search_ref) {
175 $sql .= natural_search("p.ref", $search_ref);
176 }
177 if ($search_nom) {
178 $sql .= natural_search("p.label", $search_nom);
179 }
180 $sql .= " GROUP BY fk_product, fk_entrepot";
181 //print $sql;
182
183 $resql = $db->query($sql);
184 if ($resql) {
185 $num = $db->num_rows($resql);
186 $i = 0;
187
188 while ($i < $num) {
189 $obj = $db->fetch_object($resql);
190
191 $tmp_fk_product = $obj->fk_product;
192 $tmp_fk_warehouse = $obj->fk_warehouse;
193 $stock = $obj->stock;
194
195 $stock_prod_warehouse[$tmp_fk_product][$tmp_fk_warehouse] = $stock;
196 $stock_prod[$tmp_fk_product] = (isset($stock_prod[$tmp_fk_product]) ? $stock_prod[$tmp_fk_product] : 0) + $stock;
197
198 $i++;
199 }
200
201 $db->free($resql);
202 $resql = null; // so the free() at the end of the page does not run on an already closed result
203 } else {
204 dol_print_error($db);
205 }
206 //var_dump($stock_prod_warehouse);
207} elseif ($action == 'filter') { // Test on permissions not required here
208 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")), null, 'errors');
209}
210
211// Get array with list of stock movements between date and now (for product/warehouse=
212$movements_prod_warehouse = array();
213$movements_prod = array();
214$movements_prod_warehouse_nb = array();
215$movements_prod_nb = array();
216if ($date && $dateIsValid) {
217 $sql = "SELECT sm.fk_product, sm.fk_entrepot, SUM(sm.value) AS stock, COUNT(sm.rowid) AS nbofmovement";
218 $sql .= " FROM ".MAIN_DB_PREFIX."stock_mouvement as sm";
219 $sql .= ", ".MAIN_DB_PREFIX."entrepot as w";
220 $sql .= ", ".MAIN_DB_PREFIX."product as p";
221 $sql .= " WHERE w.entity IN (".getEntity('stock').")";
222 $sql .= " AND w.rowid = sm.fk_entrepot AND p.rowid = sm.fk_product ";
223 if (getDolGlobalString('ENTREPOT_EXTRA_STATUS') && count($warehouseStatus)) {
224 $sql .= " AND w.statut IN (".$db->sanitize(implode(',', $warehouseStatus)).")";
225 }
226 if ($mode == 'future') {
227 $sql .= " AND sm.datem <= '".$db->idate($dateendofday)."'";
228 } else {
229 $sql .= " AND sm.datem >= '".$db->idate($dateendofday)."'";
230 }
231 if ($productid > 0) {
232 $sql .= " AND sm.fk_product = ".((int) $productid);
233 }
234 if (!empty($search_fk_warehouse)) {
235 $sql .= " AND sm.fk_entrepot IN (".$db->sanitize(implode(",", $search_fk_warehouse)).")";
236 }
237 if ($search_ref) {
238 $sql .= " AND p.ref LIKE '%".$db->escape($search_ref)."%' ";
239 }
240 if ($search_nom) {
241 $sql .= " AND p.label LIKE '%".$db->escape($search_nom)."%' ";
242 }
243 $sql .= " GROUP BY sm.fk_product, sm.fk_entrepot";
244
245 $resql = $db->query($sql);
246
247 if ($resql) {
248 $num = $db->num_rows($resql);
249 $i = 0;
250
251 while ($i < $num) {
252 $obj = $db->fetch_object($resql);
253 $fk_product = $obj->fk_product;
254 $fk_entrepot = $obj->fk_entrepot;
255 $stock = $obj->stock;
256 $nbofmovement = $obj->nbofmovement;
257
258 // Pour llx_product_stock.reel
259 $movements_prod_warehouse[$fk_product][$fk_entrepot] = $stock;
260 $movements_prod_warehouse_nb[$fk_product][$fk_entrepot] = $nbofmovement;
261
262 // Pour llx_product.stock
263 $movements_prod[$fk_product] = $stock + (array_key_exists($fk_product, $movements_prod) ? $movements_prod[$fk_product] : 0);
264 $movements_prod_nb[$fk_product] = $nbofmovement + (array_key_exists($fk_product, $movements_prod_nb) ? $movements_prod_nb[$fk_product] : 0);
265
266 $i++;
267 }
268
269 $db->free($resql);
270 $resql = null; // so the free() at the end of the page does not run on an already closed result
271 } else {
272 dol_print_error($db);
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$sql .= ' FROM '.MAIN_DB_PREFIX.'product as p';
306if (!empty($search_fk_warehouse)) {
307 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as ps ON p.rowid = ps.fk_product AND ps.fk_entrepot IN ('.$db->sanitize(implode(",", $search_fk_warehouse)).")";
308}
309// Add fields from hooks
310$parameters = array();
311$reshook = $hookmanager->executeHooks('printFieldListJoin', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
312$sql .= $hookmanager->resPrint;
313$sql .= ' WHERE p.entity IN ('.getEntity('product').')';
314if ($productid > 0) {
315 $sql .= " AND p.rowid = ".((int) $productid);
316}
317if (!getDolGlobalString('STOCK_SUPPORTS_SERVICES')) {
318 $sql .= " AND p.fk_product_type = 0";
319}
320if (!empty($canvas)) {
321 $sql .= " AND p.canvas = '".$db->escape($canvas)."'";
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$nbtotalofrecords = '';
361if ($date && $dateIsValid) { // We avoid a heavy sql if mandatory parameter date not yet defined
362 if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
363 $result = $db->query($sql);
364 $nbtotalofrecords = $db->num_rows($result);
365 if (($page * $limit) > $nbtotalofrecords || $ext == 'csv') { // if total resultset is smaller then paging size (filtering), goto and load page 0
366 $page = 0;
367 $offset = 0;
368 }
369 }
370
371 //print $sql;
372 if ($ext != 'csv') {
373 $sql .= $db->plimit($limit + 1, $offset);
374 $resql = $db->query($sql);
375 } else {
376 $resql = $result;
377 $limit = 0;
378 }
379 if (empty($resql)) {
380 dol_print_error($db);
381 exit;
382 }
383
384 $num = $db->num_rows($resql);
385}
386
387$i = 0;
388
389$helpurl = 'EN:Module_Stocks_En|FR:Module_Stock|';
390$helpurl .= 'ES:M&oacute;dulo_Stocks';
391
392$stocklabel = $langs->trans('StockAtDate');
393if ($mode == 'future') {
394 $stocklabel = $langs->trans("VirtualStockAtDate");
395}
396
397// 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.
398if ($ext == 'csv') {
399 top_httphead("text/csv");
400 //header("Content-Type: text/csv");
401 header("Content-Disposition: attachment; filename=stock".($date ? '-'.date("Y-m-d", $date) : '').".csv");
402
403 // Lines of title
404 print implode(";", ($mode == 'future') ?
405 array('"Product Reference"', '"Label"', '"Current Stock"', '"'.$stocklabel.'"', '"Virtual Stock"') :
406 array('"Product Reference"', '"Label"', '"'.$stocklabel.'"', '"Estimated Stock Value"', '"Estimate Sell Value"', '"Movements"', '"Current Stock"'))."\r\n";
407} else {
408 llxHeader('', $title, $helpurl, '', 0, 0, '', '', '', 'mod-product page-stock_stockatdate');
409
410 $head = array();
411
412 $head[0][0] = DOL_URL_ROOT.'/product/stock/stockatdate.php';
413 $head[0][1] = $langs->trans("StockAtDateInPast");
414 $head[0][2] = 'stockatdatepast';
415
416 $head[1][0] = DOL_URL_ROOT.'/product/stock/stockatdate.php?mode=future';
417 $head[1][1] = $langs->trans("StockAtDateInFuture");
418 $head[1][2] = 'stockatdatefuture';
419
420
421 print load_fiche_titre($langs->trans('StockAtDate'), '', 'stock');
422
423 print dol_get_fiche_head($head, ($mode == 'future' ? 'stockatdatefuture' : 'stockatdatepast'), '', -1, '');
424
425 $desc = $langs->trans("StockAtDatePastDesc");
426 if ($mode == 'future') {
427 $desc = $langs->trans("StockAtDateFutureDesc");
428 }
429 print '<span class="opacitymedium">'.$desc.'</span><br>'."\n";
430 print '<br>'."\n";
431
432 print '<form name="formFilterWarehouse" method="POST" action="'.$_SERVER["PHP_SELF"].'">';
433 print '<input type="hidden" name="token" value="'.newToken().'">';
434 print '<input type="hidden" name="action" value="filter">';
435 print '<input type="hidden" name="mode" value="'.$mode.'">';
436
437 print '<div class="inline-block valignmiddle" style="padding-right: 20px;">';
438 print '<span class="fieldrequired">'.$langs->trans('Date').'</span> '.$form->selectDate(($date ? $date : -1), 'date');
439
440 print ' <span class="clearbothonsmartphone marginleftonly paddingleftonly marginrightonly paddingrightonly">&nbsp;</span> ';
441 print img_picto('', 'product', 'class="pictofixedwidth"').' ';
442 print '</span> ';
443 print $form->select_produits($productid, 'productid', '', 0, 0, -1, 2, '', 0, array(), 0, $langs->trans('Product'), 0, 'maxwidth300', 0, '', null, 1);
444
445 if ($mode != 'future') {
446 // 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
447 print ' <span class="clearbothonsmartphone marginleftonly paddingleftonly marginrightonly paddingrightonly">&nbsp;</span> ';
448 print img_picto('', 'stock', 'class="pictofixedwidth"').$langs->trans("Warehouse").' :';
449 print '</span> ';
450 $selected = ((GETPOSTISSET('search_fk_warehouse') || GETPOSTISSET('fk_warehouse')) ? $search_fk_warehouse : 'ifonenodefault');
451 print $formproduct->selectWarehouses($selected, 'search_fk_warehouse', '', 1, 0, 0, $langs->trans('Warehouse'), 0, 0, array(), 'minwidth200', array(), 1, false, 'e.ref', 1);
452 }
453
454 print '</div>';
455
456 $parameters = array();
457 $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook
458 if (empty($reshook)) {
459 print $hookmanager->resPrint;
460 }
461
462 print '<div class="inline-block valignmiddle">';
463 print '<input type="submit" class="button" name="valid" value="'.$langs->trans('Refresh').'">';
464 print '</div>';
465
466 //print '</form>';
467
468 $param = '';
469 if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) {
470 $param .= '&contextpage='.urlencode($contextpage);
471 }
472 if ($limit > 0 && $limit != $conf->liste_limit) {
473 $param .= '&limit='.((int) $limit);
474 }
475 $param .= '&mode='.$mode;
476 $param_warehouse = '';
477 if (!empty($search_fk_warehouse)) {
478 foreach ($search_fk_warehouse as $val) {
479 $param_warehouse .= '&search_fk_warehouse[]='.$val;
480 }
481 $param .= $param_warehouse;
482 }
483 if ($productid > 0) {
484 $param .= '&productid='.(int) $productid;
485 }
486 if (GETPOSTINT('dateday') > 0) {
487 $param .= '&dateday='.GETPOSTINT('dateday');
488 }
489 if (GETPOSTINT('datemonth') > 0) {
490 $param .= '&datemonth='.GETPOSTINT('datemonth');
491 }
492 if (GETPOSTINT('dateyear') > 0) {
493 $param .= '&dateyear='.GETPOSTINT('dateyear');
494 }
495
496 // TODO Move this into the title line ?
497 print_barre_liste('', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'stock', 0, '', '', $limit, 0, 0, 1);
498
499 print '<div class="div-table-responsive">'; // You can use div-table-responsive-no-min if you don't need reserved height for your table
500 if ($num) {
501 print '<p>';
502 print '<a href="stockatdate.php?output=csv&sortfield='.urlencode($sortfield).'&sortorder='.urlencode($sortorder).'&type='.((int) $type).'&mode='.urlencode($mode).
503 (($productid > 0) ? "&productid=".((int) $productid) : '').
504 $param_warehouse.
505 "&search_ref=".dol_escape_htmltag($search_ref).
506 "&search_nom=".dol_escape_htmltag($search_nom).
507 (GETPOSTISSET('dateday') ? "&dateday=".GETPOSTINT('dateday') : '').
508 (GETPOSTISSET('datemonth') ? "&datemonth=".GETPOSTINT('datemonth') : '').
509 (GETPOSTISSET('dateyear') ? "&dateyear=".GETPOSTINT('dateyear') : '').
510 '" title="Download CSV" />';
511 print img_picto('', 'download', 'class="pictofixedwidth"');
512 print 'Download CSV';
513 print '</a>';
514 print '</p>';
515 }
516 print '<table class="liste centpercent">';
517
518 print '<input type="hidden" name="token" value="'.newToken().'">';
519 print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
520 print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
521 print '<input type="hidden" name="type" value="'.$type.'">';
522 print '<input type="hidden" name="mode" value="'.$mode.'">';
523
524 // Fields title search
525 print '<tr class="liste_titre_filter">';
526 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
527 print '<td class="liste_titre center maxwidthsearch">';
528 $searchpicto = $form->showFilterButtons('left');
529 print $searchpicto;
530 print '</td>';
531 }
532 print '<td class="liste_titre"><input class="flat" type="text" name="search_ref" size="8" value="'.dol_escape_htmltag($search_ref).'"></td>';
533 print '<td class="liste_titre"><input class="flat" type="text" name="search_nom" size="8" value="'.dol_escape_htmltag($search_nom).'"></td>';
534 print '<td class="liste_titre"></td>';
535 print '<td class="liste_titre"></td>';
536 print '<td class="liste_titre"></td>';
537 if ($mode == 'future') {
538 print '<td class="liste_titre"></td>';
539 } else {
540 print '<td class="liste_titre"></td>';
541 print '<td class="liste_titre"></td>';
542 }
543 // Fields from hook
544 $parameters = array('param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder);
545 $reshook = $hookmanager->executeHooks('printFieldListOption', $parameters); // Note that $action and $object may have been modified by hook
546 print $hookmanager->resPrint;
547
548 // Action column
549 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
550 print '<td class="liste_titre center maxwidthsearch">';
551 $searchpicto = $form->showFilterButtons();
552 print $searchpicto;
553 print '</td>';
554 }
555 print '</tr>';
556
557 $fieldtosortcurrentstock = 'stock';
558 if (!empty($search_fk_warehouse)) {
559 $fieldtosortcurrentstock = 'stock_reel';
560 }
561
562 // Lines of title
563 print '<tr class="liste_titre">';
564 // Action column
565 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
566 print_liste_field_titre('', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'left ');
567 }
568 print_liste_field_titre('ProductRef', $_SERVER["PHP_SELF"], 'p.ref', '', $param, '', $sortfield, $sortorder);
569 print_liste_field_titre('Label', $_SERVER["PHP_SELF"], 'p.label', '', $param, '', $sortfield, $sortorder);
570
571 if ($mode == 'future') {
572 print_liste_field_titre('CurrentStock', $_SERVER["PHP_SELF"], $fieldtosortcurrentstock, '', $param, '', $sortfield, $sortorder, 'right ');
573 print_liste_field_titre('', $_SERVER["PHP_SELF"]);
574 print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ', 'VirtualStockAtDateDesc');
575 print_liste_field_titre('VirtualStock', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ', 'VirtualStockDesc');
576 } else {
577 print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ');
578 $tooltiptext = $langs->trans("QtyAtDate").' x '.$langs->trans("AverageUnitPricePMPShort").' ('.$langs->trans("Currently").')';
579 print_liste_field_titre("EstimatedStockValue", $_SERVER["PHP_SELF"], "currentvalue", '', $param, '', $sortfield, $sortorder, 'right ', $tooltiptext, 1);
580 $tooltiptext = $langs->trans("QtyAtDate").' x '.$langs->trans("SellingPrice").' ('.$langs->trans("Currently").')';
581 print_liste_field_titre("EstimatedStockValueSell", $_SERVER["PHP_SELF"], "", '', $param, '', $sortfield, $sortorder, 'right ', $tooltiptext, 1);
582 $tooltiptext = $langs->trans("MovementsSinceDate");
583 print_liste_field_titre("Movements", $_SERVER["PHP_SELF"], '', '', $param, '', '', '', 'right ', $tooltiptext, 1);
584 print_liste_field_titre('CurrentStock', $_SERVER["PHP_SELF"], $fieldtosortcurrentstock, '', $param, '', $sortfield, $sortorder, 'right ');
585 }
586
587 // Hook fields
588 $parameters = array('param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder);
589 $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook
590 print $hookmanager->resPrint;
591
592 // Action column
593 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
594 print_liste_field_titre('', $_SERVER["PHP_SELF"], '', '', $param, '', $sortfield, $sortorder, 'right ');
595 }
596
597 print "</tr>\n";
598}
599
600$totalbuyingprice = 0;
601$totalsellingprice = 0;
602$totalcurrentstock = 0;
603$totalvirtualstock = 0;
604
605$i = 0;
606while ($i < ($limit ? min($num, $limit) : $num)) {
607 if (empty($resql)) {
608 break;
609 }
610
611 $objp = $db->fetch_object($resql);
612
613 if (getDolGlobalString('STOCK_SUPPORTS_SERVICES') || $objp->fk_product_type == 0) {
614 $prod->fetch($objp->rowid);
615
616 // Multilangs
617 /*if (getDolGlobalInt('MAIN_MULTILANGS'))
618 {
619 $sql = 'SELECT label,description';
620 $sql .= ' FROM '.MAIN_DB_PREFIX.'product_lang';
621 $sql .= ' WHERE fk_product = '.((int) $objp->rowid);
622 $sql .= " AND lang = '".$db->escape($langs->getDefaultLang())."'";
623 $sql .= ' LIMIT 1';
624
625 $resqlm = $db->query($sql);
626 if ($resqlm)
627 {
628 $objtp = $db->fetch_object($resqlm);
629 if (!empty($objtp->description)) $objp->description = $objtp->description;
630 if (!empty($objtp->label)) $objp->label = $objtp->label;
631 }
632 }*/
633
634 $currentstock = '';
635 if (!empty($search_fk_warehouse)) {
636 //if ($productid > 0) {
637 foreach ($search_fk_warehouse as $val) {
638 if (!is_numeric($currentstock)) {
639 $currentstock = 0;
640 }
641 $currentstock += empty($stock_prod_warehouse[$objp->rowid][$val]) ? 0 : $stock_prod_warehouse[$objp->rowid][$val];
642 }
643 //} else {
644 // $currentstock = $objp->stock_reel;
645 //}
646 } else {
647 //if ($productid > 0) {
648 $currentstock = empty($stock_prod[$objp->rowid]) ? 0 : $stock_prod[$objp->rowid];
649 //} else {
650 // $currentstock = $objp->stock;
651 //}
652 }
653
654 $nbofmovement = 0;
655 $virtualstock = 0;
656 if ($mode == 'future') {
657 $prod->load_stock('warehouseopen,warehouseinternal,nobatch', 0, $dateendofday);
658 $stock = $prod->stock_theorique; // virtual stock at a date
659 $prod->load_stock('warehouseopen,warehouseinternal,nobatch', 0);
660 $virtualstock = $prod->stock_theorique; // virtual stock in infinite future
661 } else {
662 $stock = $currentstock;
663 if (!empty($search_fk_warehouse)) {
664 foreach ($search_fk_warehouse as $val) {
665 $stock -= empty($movements_prod_warehouse[$objp->rowid][$val]) ? 0 : $movements_prod_warehouse[$objp->rowid][$val];
666 $nbofmovement += empty($movements_prod_warehouse_nb[$objp->rowid][$val]) ? 0 : $movements_prod_warehouse_nb[$objp->rowid][$val];
667 }
668 } else {
669 $stock -= empty($movements_prod[$objp->rowid]) ? 0 : $movements_prod[$objp->rowid];
670 $nbofmovement += empty($movements_prod_nb[$objp->rowid]) ? 0 : $movements_prod_nb[$objp->rowid];
671 }
672 }
673
674
675 if ($ext == 'csv') {
676 if ($mode == 'future') {
677 print implode(";", array(
678 '"'.$objp->ref.'"',
679 '"'.$objp->label.'"',
680 '"'.price2num($currentstock, 'MS').'"',
681 '"'.price2num($stock, 'MS').'"',
682 '"'.price2num($virtualstock, 'MS').'"'))."\r\n";
683 $totalvirtualstock += $virtualstock;
684 } else {
685 print implode(";", array(
686 '"'.$objp->ref.'"',
687 '"'.$objp->label.'"',
688 '"'.price(price2num($stock, 'MS')).'"',
689 price2num($stock * $objp->pmp, 'MT') ? '"'.price2num($stock * $objp->pmp, 'MT').'"' : '',
690 !getDolGlobalString('PRODUIT_MULTIPRICES') ? '"'.price2num($stock * $objp->price, 'MT').'"' : '"'.$langs->trans("Variable").'('.$langs->trans("OptionMULTIPRICESIsOn").')"',
691 "$nbofmovement",
692 '"'.price2num($currentstock, 'MS').'"'))."\r\n";
693 $totalbuyingprice += $stock * $objp->pmp;
694 $totalsellingprice += $stock * $objp->price;
695 }
696 $totalcurrentstock += $currentstock;
697 } else {
698 print '<tr class="oddeven">';
699
700 // Action column
701 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
702 print '<td class="left"></td>';
703 }
704
705 // Product ref
706 print '<td class="nowrap">'.$prod->getNomUrl(1, '').'</td>';
707
708 // Product label
709 print '<td>';
710 print dol_escape_htmltag($objp->label);
711 print '</td>';
712
713 if ($mode == 'future') {
714 // Current stock
715 print '<td class="right">'.price(price2num($currentstock, 'MS')).'</td>';
716 //$totalcurrentstock += $currentstock;
717
718 print '<td class="right"></td>';
719
720 // Virtual stock at date
721 print '<td class="right">'.price(price2num($stock, 'MS')).'</td>';
722
723 // Final virtual stock
724 print '<td class="right">'.price(price2num($virtualstock, 'MS')).'</td>';
725 $totalvirtualstock += $virtualstock;
726 } else {
727 // Stock at date
728 print '<td class="right">'.($stock ? price(price2num($stock, 'MS')) : ('<span class="opacitymedium">0</span>')).'</td>';
729
730 // PMP value
731 $estimatedvalue = $stock * $objp->pmp;
732 print '<td class="right" title="'.dolPrintHTMLForAttribute($langs->trans("AverageUnitPricePMPShort").' ('.$langs->trans("Currently").'): '.price(price2num($objp->pmp, 'MU'), 1)).'">';
733 if (price2num($estimatedvalue, 'MT')) {
734 print '<span class="amount">'.price(price2num($estimatedvalue, 'MT'), 1).'</span>';
735 } else {
736 print '';
737 }
738 $totalbuyingprice += $estimatedvalue;
739 print '</td>';
740
741 // Selling value
742 print '<td class="right"';
743 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
744 print ' title="'.dolPrintHTMLForAttribute($langs->trans("SellingPrice").' ('.$langs->trans("Currently").'): '.price(price2num($objp->price, 'MU'), 1));
745 }
746 print '">';
747 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
748 print '<span class="amount">';
749 if ($stock || (float) ($stock * $objp->price)) {
750 print price(price2num($stock * $objp->price, 'MT'), 1);
751 }
752 print '</span>';
753 $totalsellingprice += $stock * $objp->price;
754 } else {
755 $htmltext = $langs->trans("OptionMULTIPRICESIsOn");
756 print $form->textwithtooltip('<span class="opacitymedium">'.$langs->trans("Variable").'</span>', $htmltext);
757 }
758 print '</td>';
759
760 // Movements
761 print '<td class="right">';
762 if ($nbofmovement > 0) {
763 $url = DOL_URL_ROOT.'/product/stock/movement_list.php?idproduct='.$objp->rowid;
764 if (GETPOSTISSET('datemonth')) {
765 $url .= '&search_date_startday='.GETPOSTINT('dateday');
766 $url .= '&search_date_startmonth='.GETPOSTINT('datemonth');
767 $url .= '&search_date_startyear='.GETPOSTINT('dateyear');
768 }
769 if (count($search_fk_warehouse) > 1) {
770 $url = ''; // Do not show link, multi warehouse as filter not managed yet by target page
771 } else {
772 foreach ($search_fk_warehouse as $val) {
773 $url .= ($val > 0 ? '&search_warehouse='.((int) $val) : '');
774 }
775 }
776 if ($url) {
777 print '<a href="'.$url.'">';
778 }
779 print $langs->trans("Movements");
780 print '<span class="tabs paddingleft"><span class="badge">'.$nbofmovement.'</span></span>';
781 if ($url) {
782 print '</a>';
783 }
784 }
785 print '</td>';
786
787 // Current stock
788 print '<td class="right">'.($currentstock ? price(price2num($currentstock, 'MS')) : '<span class="opacitymedium">0</span>').'</td>';
789 }
790 $totalcurrentstock += $currentstock;
791
792 // Fields from hook
793 $parameters = array('objp' => $objp);
794 $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters); // Note that $action and $object may have been modified by hook
795 print $hookmanager->resPrint;
796
797 // Action column
798 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
799 print '<td class="right"></td>';
800 }
801
802 print '</tr>'."\n";
803 }
804 }
805 $i++;
806}
807
808$parameters = array('sql' => $sql);
809$reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters); // Note that $action and $object may have been modified by hook
810if ($ext != 'csv') {
811 print $hookmanager->resPrint;
812}
813
814$colspan = 8;
815if ($mode == 'future') {
816 $colspan++;
817}
818
819if ($ext == 'csv') {
820 print implode(
821 ";",
822 ($mode == 'future') ? array(
823 '"'.$langs->trans("Totalforthispage").'"',
824 '',
825 $productid > 0 ? price2num($totalcurrentstock, 'MS') : '',
826 '',
827 price(price2num($totalvirtualstock, 'MS'))) :
828 array(
829 '"'.$langs->trans("Totalforthispage").'"',
830 '',
831 '',
832 '"'.price2num($totalbuyingprice, 'MT').'"',
833 !getDolGlobalString('PRODUIT_MULTIPRICES') ? '"'.price2num($totalsellingprice, 'MT').'"' : '',
834 '',
835 $productid > 0 ? price2num($totalcurrentstock, 'MS') : '')
836 );
837} else {
838 if (empty($date) || !$dateIsValid) {
839 print '<tr><td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("EnterADateCriteria").'</span></td></tr>';
840 } else {
841 print '<tr class="liste_total">';
842 print '<td>'.$langs->trans("Totalforthispage").'</td>';
843 print '<td></td>';
844 if ($mode == 'future') {
845 print '<td class="right">'.price(price2num($totalcurrentstock, 'MS')).'</td>';
846 print '<td></td>';
847 print '<td></td>';
848 print '<td class="right">'.price(price2num($totalvirtualstock, 'MS')).'</td>';
849 } else {
850 print '<td></td>';
851 print '<td class="right">'.price(price2num($totalbuyingprice, 'MT')).'</td>';
852 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
853 print '<td class="right">'.price(price2num($totalsellingprice, 'MT')).'</td>';
854 } else {
855 print '<td></td>';
856 }
857 print '<td></td>';
858 print '<td class="right">'.($productid > 0 ? price(price2num($totalcurrentstock, 'MS')) : '').'</td>';
859 }
860 print '<td></td>';
861 print '</tr>';
862 }
863
864 print '</table>';
865 print '</div>';
866
867 print dol_get_fiche_end();
868
869 print '</form>';
870
871 llxFooter();
872}
873
874if (!empty($resql)) {
875 $db->free($resql);
876}
877
878$db->close();
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:67
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
const STATUS_OPEN_INTERNAL
Warehouse open and only operations for stock transfers/corrections allowed (not for customer shipping...
const STATUS_OPEN_ALL
Warehouse open and any operations are allowed (customer shipping, supplier dispatch,...
Class to manage generation of HTML components Only common components must be here.
Class with static methods for building HTML components related to products Only components common to ...
Class to manage products or services.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
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.
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
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...
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.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
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.