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