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