dolibarr 21.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-2024 Frédéric France <frederic.france@free.fr>
8 * Copyright (C) 2024 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 if ($mode == 'future') {
648 $prod->load_stock('warehouseopen,warehouseinternal,nobatch', 0, $dateendofday);
649 $stock = $prod->stock_theorique; // virtual stock at a date
650 $prod->load_stock('warehouseopen,warehouseinternal,nobatch', 0);
651 $virtualstock = $prod->stock_theorique; // virtual stock in infinite future
652 } else {
653 $stock = $currentstock;
654 $nbofmovement = 0;
655 if (!empty($search_fk_warehouse)) {
656 foreach ($search_fk_warehouse as $val) {
657 $stock -= empty($movements_prod_warehouse[$objp->rowid][$val]) ? 0 : $movements_prod_warehouse[$objp->rowid][$val];
658 $nbofmovement += empty($movements_prod_warehouse_nb[$objp->rowid][$val]) ? 0 : $movements_prod_warehouse_nb[$objp->rowid][$val];
659 }
660 } else {
661 $stock -= empty($movements_prod[$objp->rowid]) ? 0 : $movements_prod[$objp->rowid];
662 $nbofmovement += empty($movements_prod_nb[$objp->rowid]) ? 0 : $movements_prod_nb[$objp->rowid];
663 }
664 }
665
666
667 if ($ext == 'csv') {
668 if ($mode == 'future') {
669 print implode(";", array(
670 '"'.$objp->ref.'"',
671 '"'.$objp->label.'"',
672 '"'.price2num($currentstock, 'MS').'"',
673 '"'.price2num($stock, 'MS').'"',
674 '"'.price2num($virtualstock, 'MS').'"'))."\r\n";
675 $totalvirtualstock += $virtualstock;
676 } else {
677 print implode(";", array(
678 '"'.$objp->ref.'"',
679 '"'.$objp->label.'"',
680 '"'.price(price2num($stock, 'MS')).'"',
681 price2num($stock * $objp->pmp, 'MT') ? '"'.price2num($stock * $objp->pmp, 'MT').'"' : '',
682 !getDolGlobalString('PRODUIT_MULTIPRICES') ? '"'.price2num($stock * $objp->price, 'MT').'"' : '"'.$langs->trans("Variable").'('.$langs->trans("OptionMULTIPRICESIsOn").')"',
683 "$nbofmovement",
684 '"'.price2num($currentstock, 'MS').'"'))."\r\n";
685 $totalbuyingprice += $stock * $objp->pmp;
686 $totalsellingprice += $stock * $objp->price;
687 }
688 $totalcurrentstock += $currentstock;
689 } else {
690 print '<tr class="oddeven">';
691
692 // Action column
693 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
694 print '<td class="left"></td>';
695 }
696
697 // Product ref
698 print '<td class="nowrap">'.$prod->getNomUrl(1, '').'</td>';
699
700 // Product label
701 print '<td>';
702 print dol_escape_htmltag($objp->label);
703 print '</td>';
704
705 if ($mode == 'future') {
706 // Current stock
707 print '<td class="right">'.price(price2num($currentstock, 'MS')).'</td>';
708 //$totalcurrentstock += $currentstock;
709
710 print '<td class="right"></td>';
711
712 // Virtual stock at date
713 print '<td class="right">'.price(price2num($stock, 'MS')).'</td>';
714
715 // Final virtual stock
716 print '<td class="right">'.price(price2num($virtualstock, 'MS')).'</td>';
717 $totalvirtualstock += $virtualstock;
718 } else {
719 // Stock at date
720 print '<td class="right">'.($stock ? price(price2num($stock, 'MS')) : ('<span class="opacitymedium">0</span>')).'</td>';
721
722 // PMP value
723 $estimatedvalue = $stock * $objp->pmp;
724 print '<td class="right" title="'.dolPrintHTMLForAttribute($langs->trans("AverageUnitPricePMPShort").' ('.$langs->trans("Currently").'): '.price(price2num($objp->pmp, 'MU'), 1)).'">';
725 if (price2num($estimatedvalue, 'MT')) {
726 print '<span class="amount">'.price(price2num($estimatedvalue, 'MT'), 1).'</span>';
727 } else {
728 print '';
729 }
730 $totalbuyingprice += $estimatedvalue;
731 print '</td>';
732
733 // Selling value
734 print '<td class="right"';
735 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
736 print ' title="'.dolPrintHTMLForAttribute($langs->trans("SellingPrice").' ('.$langs->trans("Currently").'): '.price(price2num($objp->price, 'MU'), 1));
737 }
738 print '">';
739 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
740 print '<span class="amount">';
741 if ($stock || (float) ($stock * $objp->price)) {
742 print price(price2num($stock * $objp->price, 'MT'), 1);
743 }
744 print '</span>';
745 $totalsellingprice += $stock * $objp->price;
746 } else {
747 $htmltext = $langs->trans("OptionMULTIPRICESIsOn");
748 print $form->textwithtooltip('<span class="opacitymedium">'.$langs->trans("Variable").'</span>', $htmltext);
749 }
750 print '</td>';
751
752 // Movements
753 print '<td class="right">';
754 if ($nbofmovement > 0) {
755 $url = DOL_URL_ROOT.'/product/stock/movement_list.php?idproduct='.$objp->rowid;
756 if (GETPOSTISSET('datemonth')) {
757 $url .= '&search_date_startday='.GETPOSTINT('dateday');
758 $url .= '&search_date_startmonth='.GETPOSTINT('datemonth');
759 $url .= '&search_date_startyear='.GETPOSTINT('dateyear');
760 }
761 if (count($search_fk_warehouse) > 1) {
762 $url = ''; // Do not show link, multi warehouse as filter not managed yet by target page
763 } else {
764 foreach ($search_fk_warehouse as $val) {
765 $url .= ($val > 0 ? '&search_warehouse='.((int) $val) : '');
766 }
767 }
768 if ($url) {
769 print '<a href="'.$url.'">';
770 }
771 print $langs->trans("Movements");
772 print '<span class="tabs paddingleft"><span class="badge">'.$nbofmovement.'</span></span>';
773 if ($url) {
774 print '</a>';
775 }
776 }
777 print '</td>';
778
779 // Current stock
780 print '<td class="right">'.($currentstock ? price(price2num($currentstock, 'MS')) : '<span class="opacitymedium">0</span>').'</td>';
781 }
782 $totalcurrentstock += $currentstock;
783
784 // Fields from hook
785 $parameters = array('objp' => $objp);
786 $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters); // Note that $action and $object may have been modified by hook
787 print $hookmanager->resPrint;
788
789 // Action column
790 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
791 print '<td class="right"></td>';
792 }
793
794 print '</tr>'."\n";
795 }
796 }
797 $i++;
798}
799
800$parameters = array('sql' => $sql);
801$reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters); // Note that $action and $object may have been modified by hook
802if ($ext != 'csv') {
803 print $hookmanager->resPrint;
804}
805
806$colspan = 8;
807if ($mode == 'future') {
808 $colspan++;
809}
810
811if ($ext == 'csv') {
812 print implode(
813 ";",
814 ($mode == 'future') ? array(
815 '"'.$langs->trans("Totalforthispage").'"',
816 '',
817 $productid > 0 ? price2num($totalcurrentstock, 'MS') : '',
818 '',
819 price(price2num($totalvirtualstock, 'MS'))) :
820 array(
821 '"'.$langs->trans("Totalforthispage").'"',
822 '',
823 '',
824 '"'.price2num($totalbuyingprice, 'MT').'"',
825 !getDolGlobalString('PRODUIT_MULTIPRICES') ? '"'.price2num($totalsellingprice, 'MT').'"' : '',
826 '',
827 $productid > 0 ? price2num($totalcurrentstock, 'MS') : '')
828 );
829} else {
830 if (empty($date) || !$dateIsValid) {
831 print '<tr><td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("EnterADateCriteria").'</span></td></tr>';
832 } else {
833 print '<tr class="liste_total">';
834 print '<td>'.$langs->trans("Totalforthispage").'</td>';
835 print '<td></td>';
836 if ($mode == 'future') {
837 print '<td class="right">'.price(price2num($totalcurrentstock, 'MS')).'</td>';
838 print '<td></td>';
839 print '<td></td>';
840 print '<td class="right">'.price(price2num($totalvirtualstock, 'MS')).'</td>';
841 } else {
842 print '<td></td>';
843 print '<td class="right">'.price(price2num($totalbuyingprice, 'MT')).'</td>';
844 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
845 print '<td class="right">'.price(price2num($totalsellingprice, 'MT')).'</td>';
846 } else {
847 print '<td></td>';
848 }
849 print '<td></td>';
850 print '<td class="right">'.($productid > 0 ? price(price2num($totalcurrentstock, 'MS')) : '').'</td>';
851 }
852 print '<td></td>';
853 print '</tr>';
854 }
855
856 print '</table>';
857 print '</div>';
858
859 print dol_get_fiche_end();
860
861 print '</form>';
862
863 llxFooter();
864}
865
866if (!empty($resql)) {
867 $db->free($resql);
868}
869
870$db->close();
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:87
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:71
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_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)
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.
print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
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.