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); // 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); // 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$sql .= ' 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,';
318$sql .= ' p.tms, p.duration, p.tobuy, p.stock';
319// Add where from hooks
320$parameters = array();
321$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters); // Note that $action and $object may have been modified by hook
322$sql .= $hookmanager->resPrint;
323
324if ($sortfield == 'stock_reel' && empty($search_fk_warehouse)) {
325 $sortfield = 'stock';
326}
327if ($sortfield == 'stock' && !empty($search_fk_warehouse)) {
328 $sortfield = 'stock_reel';
329}
330$sql .= $db->order($sortfield, $sortorder);
331
332$nbtotalofrecords = '';
333if ($date && $dateIsValid) { // We avoid a heavy sql if mandatory parameter date not yet defined
334 if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
335 $result = $db->query($sql);
336 $nbtotalofrecords = $db->num_rows($result);
337 if (($page * $limit) > $nbtotalofrecords || $ext == 'csv') { // if total resultset is smaller then paging size (filtering), goto and load page 0
338 $page = 0;
339 $offset = 0;
340 }
341 }
342
343 //print $sql;
344 if ($ext != 'csv') {
345 $sql .= $db->plimit($limit + 1, $offset);
346 $resql = $db->query($sql);
347 } else {
348 $resql = $result;
349 $limit = 0;
350 }
351 if (empty($resql)) {
352 dol_print_error($db);
353 exit;
354 }
355
356 $num = $db->num_rows($resql);
357}
358
359$i = 0;
360
361$helpurl = 'EN:Module_Stocks_En|FR:Module_Stock|';
362$helpurl .= 'ES:M&oacute;dulo_Stocks';
363
364$stocklabel = $langs->trans('StockAtDate');
365if ($mode == 'future') {
366 $stocklabel = $langs->trans("VirtualStockAtDate");
367}
368
369// 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.
370if ($ext == 'csv') {
371 top_httphead("text/csv");
372 //header("Content-Type: text/csv");
373 header("Content-Disposition: attachment; filename=stock".($date?'-'.date("Y-m-d", $date):'').".csv");
374
375 // Lines of title
376 print implode(";", ($mode == 'future') ?
377 array('"Product Reference"', '"Label"', '"Current Stock"', '"'.$stocklabel.'"', '"Virtual Stock"'):
378 array('"Product Reference"', '"Label"', '"'.$stocklabel.'"', '"Estimated Stock Value"', '"Estimate Sell Value"', '"Movements"', '"Current Stock"'))."\r\n";
379} else {
380 llxHeader('', $title, $helpurl, '', 0, 0, '', '', '', 'mod-product page-stock_stockatdate');
381
382 $head = array();
383
384 $head[0][0] = DOL_URL_ROOT.'/product/stock/stockatdate.php';
385 $head[0][1] = $langs->trans("StockAtDateInPast");
386 $head[0][2] = 'stockatdatepast';
387
388 $head[1][0] = DOL_URL_ROOT.'/product/stock/stockatdate.php?mode=future';
389 $head[1][1] = $langs->trans("StockAtDateInFuture");
390 $head[1][2] = 'stockatdatefuture';
391
392
393 print load_fiche_titre($langs->trans('StockAtDate'), '', 'stock');
394
395 print dol_get_fiche_head($head, ($mode == 'future' ? 'stockatdatefuture' : 'stockatdatepast'), '', -1, '');
396
397 $desc = $langs->trans("StockAtDatePastDesc");
398 if ($mode == 'future') {
399 $desc = $langs->trans("StockAtDateFutureDesc");
400 }
401 print '<span class="opacitymedium">'.$desc.'</span><br>'."\n";
402 print '<br>'."\n";
403
404 print '<form name="formFilterWarehouse" method="POST" action="'.$_SERVER["PHP_SELF"].'">';
405 print '<input type="hidden" name="token" value="'.newToken().'">';
406 print '<input type="hidden" name="action" value="filter">';
407 print '<input type="hidden" name="mode" value="'.$mode.'">';
408
409 print '<div class="inline-block valignmiddle" style="padding-right: 20px;">';
410 print '<span class="fieldrequired">'.$langs->trans('Date').'</span> '.$form->selectDate(($date ? $date : -1), 'date');
411
412 print ' <span class="clearbothonsmartphone marginleftonly paddingleftonly marginrightonly paddingrightonly">&nbsp;</span> ';
413 print img_picto('', 'product', 'class="pictofiwedwidth"').' ';
414 print '</span> ';
415 print $form->select_produits($productid, 'productid', '', 0, 0, -1, 2, '', 0, array(), 0, $langs->trans('Product'), 0, 'maxwidth300', 0, '', null, 1);
416
417 if ($mode != 'future') {
418 // 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
419 print ' <span class="clearbothonsmartphone marginleftonly paddingleftonly marginrightonly paddingrightonly">&nbsp;</span> ';
420 print img_picto('', 'stock', 'class="pictofixedwidth"').$langs->trans("Warehouse").' :';
421 print '</span> ';
422 $selected = ((GETPOSTISSET('search_fk_warehouse') || GETPOSTISSET('fk_warehouse')) ? $search_fk_warehouse : 'ifonenodefault');
423 print $formproduct->selectWarehouses($selected, 'search_fk_warehouse', '', 1, 0, 0, $langs->trans('Warehouse'), 0, 0, null, 'minwidth200', null, 1, false, 'e.ref', 1);
424 }
425
426 print '</div>';
427
428 $parameters = array();
429 $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook
430 if (empty($reshook)) {
431 print $hookmanager->resPrint;
432 }
433
434 print '<div class="inline-block valignmiddle">';
435 print '<input type="submit" class="button" name="valid" value="'.$langs->trans('Refresh').'">';
436 print '</div>';
437
438 //print '</form>';
439
440 $param = '';
441 if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) {
442 $param .= '&contextpage='.urlencode($contextpage);
443 }
444 if ($limit > 0 && $limit != $conf->liste_limit) {
445 $param .= '&limit='.((int) $limit);
446 }
447 $param .= '&mode='.$mode;
448 $param_warehouse = '';
449 if (!empty($search_fk_warehouse)) {
450 foreach ($search_fk_warehouse as $val) {
451 $param_warehouse .= '&search_fk_warehouse[]='.$val;
452 }
453 $param .= $param_warehouse;
454 }
455 if ($productid > 0) {
456 $param .= '&productid='.(int) $productid;
457 }
458 if (GETPOSTINT('dateday') > 0) {
459 $param .= '&dateday='.GETPOSTINT('dateday');
460 }
461 if (GETPOSTINT('datemonth') > 0) {
462 $param .= '&datemonth='.GETPOSTINT('datemonth');
463 }
464 if (GETPOSTINT('dateyear') > 0) {
465 $param .= '&dateyear='.GETPOSTINT('dateyear');
466 }
467
468 // TODO Move this into the title line ?
469 print_barre_liste('', $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'stock', 0, '', '', $limit, 0, 0, 1);
470
471 print '<div class="div-table-responsive">'; // You can use div-table-responsive-no-min if you don't need reserved height for your table
472 if ($num) {
473 print '<p>';
474 print '<a href="stockatdate.php?output=csv&sortfield='.urlencode($sortfield).'&sortorder='.urlencode($sortorder).'&type='.((int) $type).'&mode='.urlencode($mode).
475 (($productid > 0)?"&productid=".((int) $productid):'').
476 $param_warehouse.
477 "&search_ref=".dol_escape_htmltag($search_ref).
478 "&search_nom=".dol_escape_htmltag($search_nom).
479 (GETPOSTISSET('dateday')?"&dateday=".GETPOSTINT('dateday'):'').
480 (GETPOSTISSET('datemonth')?"&datemonth=".GETPOSTINT('datemonth'):'').
481 (GETPOSTISSET('dateyear')?"&dateyear=".GETPOSTINT('dateyear'):'').
482 '" title="Download CSV" />';
483 print img_picto('', 'download', 'class="pictofixedwidth"');
484 print 'Download CSV';
485 print '</a>';
486 print '</p>';
487 }
488 print '<table class="liste centpercent">';
489
490 print '<input type="hidden" name="token" value="'.newToken().'">';
491 print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
492 print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
493 print '<input type="hidden" name="type" value="'.$type.'">';
494 print '<input type="hidden" name="mode" value="'.$mode.'">';
495
496 // Fields title search
497 print '<tr class="liste_titre_filter">';
498 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
499 print '<td class="liste_titre center maxwidthsearch">';
500 $searchpicto = $form->showFilterButtons('left');
501 print $searchpicto;
502 print '</td>';
503 }
504 print '<td class="liste_titre"><input class="flat" type="text" name="search_ref" size="8" value="'.dol_escape_htmltag($search_ref).'"></td>';
505 print '<td class="liste_titre"><input class="flat" type="text" name="search_nom" size="8" value="'.dol_escape_htmltag($search_nom).'"></td>';
506 print '<td class="liste_titre"></td>';
507 print '<td class="liste_titre"></td>';
508 print '<td class="liste_titre"></td>';
509 if ($mode == 'future') {
510 print '<td class="liste_titre"></td>';
511 } else {
512 print '<td class="liste_titre"></td>';
513 print '<td class="liste_titre"></td>';
514 }
515 // Fields from hook
516 $parameters = array('param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder);
517 $reshook = $hookmanager->executeHooks('printFieldListOption', $parameters); // Note that $action and $object may have been modified by hook
518 print $hookmanager->resPrint;
519
520 // Action column
521 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
522 print '<td class="liste_titre center maxwidthsearch">';
523 $searchpicto = $form->showFilterButtons();
524 print $searchpicto;
525 print '</td>';
526 }
527 print '</tr>';
528
529 $fieldtosortcurrentstock = 'stock';
530 if (!empty($search_fk_warehouse)) {
531 $fieldtosortcurrentstock = 'stock_reel';
532 }
533
534 // Lines of title
535 print '<tr class="liste_titre">';
536 // Action column
537 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
538 print_liste_field_titre('', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'left ');
539 }
540 print_liste_field_titre('ProductRef', $_SERVER["PHP_SELF"], 'p.ref', $param, '', '', $sortfield, $sortorder);
541 print_liste_field_titre('Label', $_SERVER["PHP_SELF"], 'p.label', $param, '', '', $sortfield, $sortorder);
542
543 if ($mode == 'future') {
544 print_liste_field_titre('CurrentStock', $_SERVER["PHP_SELF"], $fieldtosortcurrentstock, $param, '', '', $sortfield, $sortorder, 'right ');
545 print_liste_field_titre('', $_SERVER["PHP_SELF"]);
546 print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ', 'VirtualStockAtDateDesc');
547 print_liste_field_titre('VirtualStock', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ', 'VirtualStockDesc');
548 } else {
549 print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ');
550 $tooltiptext = $langs->trans("QtyAtDate").' x '.$langs->trans("AverageUnitPricePMPShort").' ('.$langs->trans("Currently").')';
551 print_liste_field_titre("EstimatedStockValue", $_SERVER["PHP_SELF"], "currentvalue", '', $param, '', $sortfield, $sortorder, 'right ', $tooltiptext, 1);
552 $tooltiptext = $langs->trans("QtyAtDate").' x '.$langs->trans("SellingPrice").' ('.$langs->trans("Currently").')';
553 print_liste_field_titre("EstimatedStockValueSell", $_SERVER["PHP_SELF"], "", '', $param, '', $sortfield, $sortorder, 'right ', $tooltiptext, 1);
554 $tooltiptext = $langs->trans("MovementsSinceDate");
555 print_liste_field_titre('', $_SERVER["PHP_SELF"], '', '', $param, '', '', '', 'right ', $tooltiptext, 1);
556 print_liste_field_titre('CurrentStock', $_SERVER["PHP_SELF"], $fieldtosortcurrentstock, $param, '', '', $sortfield, $sortorder, 'right ');
557 }
558
559 // Hook fields
560 $parameters = array('param'=>$param, 'sortfield'=>$sortfield, 'sortorder'=>$sortorder);
561 $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook
562 print $hookmanager->resPrint;
563
564 // Action column
565 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
566 print_liste_field_titre('', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ');
567 }
568
569 print "</tr>\n";
570}
571
572$totalbuyingprice = 0;
573$totalsellingprice = 0;
574$totalcurrentstock = 0;
575$totalvirtualstock = 0;
576
577$i = 0;
578while ($i < ($limit ? min($num, $limit) : $num)) {
579 $objp = $db->fetch_object($resql);
580
581 if (getDolGlobalString('STOCK_SUPPORTS_SERVICES') || $objp->fk_product_type == 0) {
582 $prod->fetch($objp->rowid);
583
584 // Multilangs
585 /*if (getDolGlobalInt('MAIN_MULTILANGS'))
586 {
587 $sql = 'SELECT label,description';
588 $sql .= ' FROM '.MAIN_DB_PREFIX.'product_lang';
589 $sql .= ' WHERE fk_product = '.((int) $objp->rowid);
590 $sql .= " AND lang = '".$db->escape($langs->getDefaultLang())."'";
591 $sql .= ' LIMIT 1';
592
593 $resqlm = $db->query($sql);
594 if ($resqlm)
595 {
596 $objtp = $db->fetch_object($resqlm);
597 if (!empty($objtp->description)) $objp->description = $objtp->description;
598 if (!empty($objtp->label)) $objp->label = $objtp->label;
599 }
600 }*/
601
602 $currentstock = '';
603 if (!empty($search_fk_warehouse)) {
604 //if ($productid > 0) {
605 foreach ($search_fk_warehouse as $val) {
606 if (!is_numeric($currentstock)) {
607 $currentstock = 0;
608 }
609 $currentstock += empty($stock_prod_warehouse[$objp->rowid][$val]) ? 0 : $stock_prod_warehouse[$objp->rowid][$val];
610 }
611 //} else {
612 // $currentstock = $objp->stock_reel;
613 //}
614 } else {
615 //if ($productid > 0) {
616 $currentstock = empty($stock_prod[$objp->rowid]) ? 0 : $stock_prod[$objp->rowid];
617 //} else {
618 // $currentstock = $objp->stock;
619 //}
620 }
621
622 if ($mode == 'future') {
623 $prod->load_stock('warehouseopen,warehouseinternal,nobatch', 0, $dateendofday);
624 $stock = $prod->stock_theorique; // virtual stock at a date
625 $prod->load_stock('warehouseopen,warehouseinternal,nobatch', 0);
626 $virtualstock = $prod->stock_theorique; // virtual stock in infinite future
627 } else {
628 $stock = $currentstock;
629 $nbofmovement = 0;
630 if (!empty($search_fk_warehouse)) {
631 foreach ($search_fk_warehouse as $val) {
632 $stock -= empty($movements_prod_warehouse[$objp->rowid][$val]) ? 0 : $movements_prod_warehouse[$objp->rowid][$val];
633 $nbofmovement += empty($movements_prod_warehouse_nb[$objp->rowid][$val]) ? 0 : $movements_prod_warehouse_nb[$objp->rowid][$val];
634 }
635 } else {
636 $stock -= empty($movements_prod[$objp->rowid]) ? 0 : $movements_prod[$objp->rowid];
637 $nbofmovement += empty($movements_prod_nb[$objp->rowid]) ? 0 : $movements_prod_nb[$objp->rowid];
638 }
639 }
640
641
642 if ($ext == 'csv') {
643 if ($mode == 'future') {
644 print implode(";", array(
645 '"'.$objp->ref.'"',
646 '"'.$objp->label.'"',
647 '"'.price2num($currentstock, 'MS').'"',
648 '"'.price2num($stock, 'MS').'"',
649 '"'.price2num($virtualstock, 'MS').'"'))."\r\n";
650 $totalvirtualstock += $virtualstock;
651 } else {
652 print implode(";", array(
653 '"'.$objp->ref.'"',
654 '"'.$objp->label.'"',
655 '"'.price(price2num($stock, 'MS')).'"',
656 price2num($stock * $objp->pmp, 'MT')?'"'.price2num($stock * $objp->pmp, 'MT').'"':'',
657 !getDolGlobalString('PRODUIT_MULTIPRICES')?'"'.price2num($stock * $objp->price, 'MT').'"':'"'.$langs->trans("Variable").'('.$langs->trans("OptionMULTIPRICESIsOn").')"',
658 "$nbofmovement",
659 '"'.price2num($currentstock, 'MS').'"'))."\r\n";
660 $totalbuyingprice += $stock * $objp->pmp;
661 $totalsellingprice += $stock * $objp->price;
662 }
663 $totalcurrentstock += $currentstock;
664 } else {
665 print '<tr class="oddeven">';
666
667 // Action column
668 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
669 print '<td class="left"></td>';
670 }
671
672 // Product ref
673 print '<td class="nowrap">'.$prod->getNomUrl(1, '').'</td>';
674
675 // Product label
676 print '<td>';
677 print dol_escape_htmltag($objp->label);
678 print '</td>';
679
680 if ($mode == 'future') {
681 // Current stock
682 print '<td class="right">'.price(price2num($currentstock, 'MS')).'</td>';
683 //$totalcurrentstock += $currentstock;
684
685 print '<td class="right"></td>';
686
687 // Virtual stock at date
688 print '<td class="right">'.price(price2num($stock, 'MS')).'</td>';
689
690 // Final virtual stock
691 print '<td class="right">'.price(price2num($virtualstock, 'MS')).'</td>';
692 $totalvirtualstock += $virtualstock;
693 } else {
694 // Stock at date
695 print '<td class="right">'.($stock ? price(price2num($stock, 'MS')) : ('<span class="opacitymedium">0</span>')).'</td>';
696
697 // PMP value
698 $estimatedvalue = $stock * $objp->pmp;
699 print '<td class="right" title="'.dolPrintHTMLForAttribute($langs->trans("AverageUnitPricePMPShort").' ('.$langs->trans("Currently").'): '.price(price2num($objp->pmp, 'MU'), 1)).'">';
700 if (price2num($estimatedvalue, 'MT')) {
701 print '<span class="amount">'.price(price2num($estimatedvalue, 'MT'), 1).'</span>';
702 } else {
703 print '';
704 }
705 $totalbuyingprice += $estimatedvalue;
706 print '</td>';
707
708 // Selling value
709 print '<td class="right"';
710 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
711 print ' title="'.dolPrintHTMLForAttribute($langs->trans("SellingPrice").' ('.$langs->trans("Currently").'): '.price(price2num($objp->price, 'MU'), 1));
712 }
713 print '">';
714 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
715 print '<span class="amount">';
716 if ($stock || (float) ($stock * $objp->price)) {
717 print price(price2num($stock * $objp->price, 'MT'), 1);
718 }
719 print '</span>';
720 $totalsellingprice += $stock * $objp->price;
721 } else {
722 $htmltext = $langs->trans("OptionMULTIPRICESIsOn");
723 print $form->textwithtooltip('<span class="opacitymedium">'.$langs->trans("Variable").'</span>', $htmltext);
724 }
725 print '</td>';
726
727 // Movements
728 print '<td class="right">';
729 if ($nbofmovement > 0) {
730 $url = DOL_URL_ROOT.'/product/stock/movement_list.php?idproduct='.$objp->rowid;
731 if (GETPOSTISSET('datemonth')) {
732 $url .= '&search_date_startday='.GETPOSTINT('dateday');
733 $url .= '&search_date_startmonth='.GETPOSTINT('datemonth');
734 $url .= '&search_date_startyear='.GETPOSTINT('dateyear');
735 }
736 if (count($search_fk_warehouse) > 1) {
737 $url = ''; // Do not show link, multi warehouse as filter not managed yet by target page
738 } else {
739 foreach ($search_fk_warehouse as $val) {
740 $url .= ($val > 0 ? '&search_warehouse='.((int) $val) : '');
741 }
742 }
743 if ($url) {
744 print '<a href="'.$url.'">';
745 }
746 print $langs->trans("Movements");
747 print '<span class="tabs paddingleft"><span class="badge">'.$nbofmovement.'</span></span>';
748 if ($url) {
749 print '</a>';
750 }
751 }
752 print '</td>';
753
754 // Current stock
755 print '<td class="right">'.($currentstock ? price(price2num($currentstock, 'MS')) : '<span class="opacitymedium">0</span>').'</td>';
756 }
757 $totalcurrentstock += $currentstock;
758
759 // Fields from hook
760 $parameters = array('objp'=>$objp);
761 $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters); // Note that $action and $object may have been modified by hook
762 print $hookmanager->resPrint;
763
764 // Action column
765 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
766 print '<td class="right"></td>';
767 }
768
769 print '</tr>'."\n";
770 }
771 }
772 $i++;
773}
774
775$parameters = array('sql'=>$sql);
776$reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters); // Note that $action and $object may have been modified by hook
777if ($ext!='csv') {
778 print $hookmanager->resPrint;
779}
780
781$colspan = 8;
782if ($mode == 'future') {
783 $colspan++;
784}
785
786if ($ext=='csv') {
787 print implode(";",
788 ($mode == 'future')?array(
789 '"'.$langs->trans("Totalforthispage").'"',
790 '',
791 $productid > 0 ? price2num($totalcurrentstock, 'MS') : '',
792 '',
793 price(price2num($totalvirtualstock, 'MS'))):
794 array(
795 '"'.$langs->trans("Totalforthispage").'"',
796 '',
797 '',
798 '"'.price2num($totalbuyingprice, 'MT').'"',
799 !getDolGlobalString('PRODUIT_MULTIPRICES')?'"'.price2num($totalsellingprice, 'MT').'"':'',
800 '',
801 $productid > 0 ? price2num($totalcurrentstock, 'MS') : ''));
802} else {
803 if (empty($date) || !$dateIsValid) {
804 print '<tr><td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("EnterADateCriteria").'</span></td></tr>';
805 } else {
806 print '<tr class="liste_total">';
807 print '<td>'.$langs->trans("Totalforthispage").'</td>';
808 print '<td></td>';
809 if ($mode == 'future') {
810 print '<td class="right">'.price(price2num($totalcurrentstock, 'MS')).'</td>';
811 print '<td></td>';
812 print '<td></td>';
813 print '<td class="right">'.price(price2num($totalvirtualstock, 'MS')).'</td>';
814 } else {
815 print '<td></td>';
816 print '<td class="right">'.price(price2num($totalbuyingprice, 'MT')).'</td>';
817 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
818 print '<td class="right">'.price(price2num($totalsellingprice, 'MT')).'</td>';
819 } else {
820 print '<td></td>';
821 }
822 print '<td></td>';
823 print '<td class="right">'.($productid > 0 ? price(price2num($totalcurrentstock, 'MS')) : '').'</td>';
824 }
825 print '<td></td>';
826 print '</tr>';
827 }
828
829 print '</table>';
830 print '</div>';
831
832 print dol_get_fiche_end();
833
834 print '</form>';
835
836 llxFooter();
837}
838
839if (!empty($resql)) {
840 $db->free($resql);
841}
842
843$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.