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