dolibarr 21.0.3
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 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
508 print '<td class="liste_titre center maxwidthsearch">';
509 $searchpicto = $form->showFilterButtons('left');
510 print $searchpicto;
511 print '</td>';
512 }
513 print '<td class="liste_titre"><input class="flat" type="text" name="search_ref" size="8" value="'.dol_escape_htmltag($search_ref).'"></td>';
514 print '<td class="liste_titre"><input class="flat" type="text" name="search_nom" size="8" value="'.dol_escape_htmltag($search_nom).'"></td>';
515 print '<td class="liste_titre"></td>';
516 print '<td class="liste_titre"></td>';
517 print '<td class="liste_titre"></td>';
518 if ($mode == 'future') {
519 print '<td class="liste_titre"></td>';
520 } else {
521 print '<td class="liste_titre"></td>';
522 print '<td class="liste_titre"></td>';
523 }
524 // Fields from hook
525 $parameters = array('param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder);
526 $reshook = $hookmanager->executeHooks('printFieldListOption', $parameters); // Note that $action and $object may have been modified by hook
527 print $hookmanager->resPrint;
528
529 // Action column
530 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
531 print '<td class="liste_titre center maxwidthsearch">';
532 $searchpicto = $form->showFilterButtons();
533 print $searchpicto;
534 print '</td>';
535 }
536 print '</tr>';
537
538 $fieldtosortcurrentstock = 'stock';
539 if (!empty($search_fk_warehouse)) {
540 $fieldtosortcurrentstock = 'stock_reel';
541 }
542
543 // Lines of title
544 print '<tr class="liste_titre">';
545 // Action column
546 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
547 print_liste_field_titre('', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'left ');
548 }
549 print_liste_field_titre('ProductRef', $_SERVER["PHP_SELF"], 'p.ref', $param, '', '', $sortfield, $sortorder);
550 print_liste_field_titre('Label', $_SERVER["PHP_SELF"], 'p.label', $param, '', '', $sortfield, $sortorder);
551
552 if ($mode == 'future') {
553 print_liste_field_titre('CurrentStock', $_SERVER["PHP_SELF"], $fieldtosortcurrentstock, $param, '', '', $sortfield, $sortorder, 'right ');
554 print_liste_field_titre('', $_SERVER["PHP_SELF"]);
555 print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ', 'VirtualStockAtDateDesc');
556 print_liste_field_titre('VirtualStock', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ', 'VirtualStockDesc');
557 } else {
558 print_liste_field_titre($stocklabel, $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ');
559 $tooltiptext = $langs->trans("QtyAtDate").' x '.$langs->trans("AverageUnitPricePMPShort").' ('.$langs->trans("Currently").')';
560 print_liste_field_titre("EstimatedStockValue", $_SERVER["PHP_SELF"], "currentvalue", '', $param, '', $sortfield, $sortorder, 'right ', $tooltiptext, 1);
561 $tooltiptext = $langs->trans("QtyAtDate").' x '.$langs->trans("SellingPrice").' ('.$langs->trans("Currently").')';
562 print_liste_field_titre("EstimatedStockValueSell", $_SERVER["PHP_SELF"], "", '', $param, '', $sortfield, $sortorder, 'right ', $tooltiptext, 1);
563 $tooltiptext = $langs->trans("MovementsSinceDate");
564 print_liste_field_titre('', $_SERVER["PHP_SELF"], '', '', $param, '', '', '', 'right ', $tooltiptext, 1);
565 print_liste_field_titre('CurrentStock', $_SERVER["PHP_SELF"], $fieldtosortcurrentstock, $param, '', '', $sortfield, $sortorder, 'right ');
566 }
567
568 // Hook fields
569 $parameters = array('param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder);
570 $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook
571 print $hookmanager->resPrint;
572
573 // Action column
574 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
575 print_liste_field_titre('', $_SERVER["PHP_SELF"], '', $param, '', '', $sortfield, $sortorder, 'right ');
576 }
577
578 print "</tr>\n";
579}
580
581$totalbuyingprice = 0;
582$totalsellingprice = 0;
583$totalcurrentstock = 0;
584$totalvirtualstock = 0;
585
586$i = 0;
587while ($i < ($limit ? min($num, $limit) : $num)) {
588 $objp = $db->fetch_object($resql);
589
590 if (getDolGlobalString('STOCK_SUPPORTS_SERVICES') || $objp->fk_product_type == 0) {
591 $prod->fetch($objp->rowid);
592
593 // Multilangs
594 /*if (getDolGlobalInt('MAIN_MULTILANGS'))
595 {
596 $sql = 'SELECT label,description';
597 $sql .= ' FROM '.MAIN_DB_PREFIX.'product_lang';
598 $sql .= ' WHERE fk_product = '.((int) $objp->rowid);
599 $sql .= " AND lang = '".$db->escape($langs->getDefaultLang())."'";
600 $sql .= ' LIMIT 1';
601
602 $resqlm = $db->query($sql);
603 if ($resqlm)
604 {
605 $objtp = $db->fetch_object($resqlm);
606 if (!empty($objtp->description)) $objp->description = $objtp->description;
607 if (!empty($objtp->label)) $objp->label = $objtp->label;
608 }
609 }*/
610
611 $currentstock = '';
612 if (!empty($search_fk_warehouse)) {
613 //if ($productid > 0) {
614 foreach ($search_fk_warehouse as $val) {
615 if (!is_numeric($currentstock)) {
616 $currentstock = 0;
617 }
618 $currentstock += empty($stock_prod_warehouse[$objp->rowid][$val]) ? 0 : $stock_prod_warehouse[$objp->rowid][$val];
619 }
620 //} else {
621 // $currentstock = $objp->stock_reel;
622 //}
623 } else {
624 //if ($productid > 0) {
625 $currentstock = empty($stock_prod[$objp->rowid]) ? 0 : $stock_prod[$objp->rowid];
626 //} else {
627 // $currentstock = $objp->stock;
628 //}
629 }
630
631 if ($mode == 'future') {
632 $prod->load_stock('warehouseopen,warehouseinternal,nobatch', 0, $dateendofday);
633 $stock = $prod->stock_theorique; // virtual stock at a date
634 $prod->load_stock('warehouseopen,warehouseinternal,nobatch', 0);
635 $virtualstock = $prod->stock_theorique; // virtual stock in infinite future
636 } else {
637 $stock = $currentstock;
638 $nbofmovement = 0;
639 if (!empty($search_fk_warehouse)) {
640 foreach ($search_fk_warehouse as $val) {
641 $stock -= empty($movements_prod_warehouse[$objp->rowid][$val]) ? 0 : $movements_prod_warehouse[$objp->rowid][$val];
642 $nbofmovement += empty($movements_prod_warehouse_nb[$objp->rowid][$val]) ? 0 : $movements_prod_warehouse_nb[$objp->rowid][$val];
643 }
644 } else {
645 $stock -= empty($movements_prod[$objp->rowid]) ? 0 : $movements_prod[$objp->rowid];
646 $nbofmovement += empty($movements_prod_nb[$objp->rowid]) ? 0 : $movements_prod_nb[$objp->rowid];
647 }
648 }
649
650
651 if ($ext == 'csv') {
652 if ($mode == 'future') {
653 print implode(";", array(
654 '"'.$objp->ref.'"',
655 '"'.$objp->label.'"',
656 '"'.price2num($currentstock, 'MS').'"',
657 '"'.price2num($stock, 'MS').'"',
658 '"'.price2num($virtualstock, 'MS').'"'))."\r\n";
659 $totalvirtualstock += $virtualstock;
660 } else {
661 print implode(";", array(
662 '"'.$objp->ref.'"',
663 '"'.$objp->label.'"',
664 '"'.price(price2num($stock, 'MS')).'"',
665 price2num($stock * $objp->pmp, 'MT') ? '"'.price2num($stock * $objp->pmp, 'MT').'"' : '',
666 !getDolGlobalString('PRODUIT_MULTIPRICES') ? '"'.price2num($stock * $objp->price, 'MT').'"' : '"'.$langs->trans("Variable").'('.$langs->trans("OptionMULTIPRICESIsOn").')"',
667 "$nbofmovement",
668 '"'.price2num($currentstock, 'MS').'"'))."\r\n";
669 $totalbuyingprice += $stock * $objp->pmp;
670 $totalsellingprice += $stock * $objp->price;
671 }
672 $totalcurrentstock += $currentstock;
673 } else {
674 print '<tr class="oddeven">';
675
676 // Action column
677 if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
678 print '<td class="left"></td>';
679 }
680
681 // Product ref
682 print '<td class="nowrap">'.$prod->getNomUrl(1, '').'</td>';
683
684 // Product label
685 print '<td>';
686 print dol_escape_htmltag($objp->label);
687 print '</td>';
688
689 if ($mode == 'future') {
690 // Current stock
691 print '<td class="right">'.price(price2num($currentstock, 'MS')).'</td>';
692 //$totalcurrentstock += $currentstock;
693
694 print '<td class="right"></td>';
695
696 // Virtual stock at date
697 print '<td class="right">'.price(price2num($stock, 'MS')).'</td>';
698
699 // Final virtual stock
700 print '<td class="right">'.price(price2num($virtualstock, 'MS')).'</td>';
701 $totalvirtualstock += $virtualstock;
702 } else {
703 // Stock at date
704 print '<td class="right">'.($stock ? price(price2num($stock, 'MS')) : ('<span class="opacitymedium">0</span>')).'</td>';
705
706 // PMP value
707 $estimatedvalue = $stock * $objp->pmp;
708 print '<td class="right" title="'.dolPrintHTMLForAttribute($langs->trans("AverageUnitPricePMPShort").' ('.$langs->trans("Currently").'): '.price(price2num($objp->pmp, 'MU'), 1)).'">';
709 if (price2num($estimatedvalue, 'MT')) {
710 print '<span class="amount">'.price(price2num($estimatedvalue, 'MT'), 1).'</span>';
711 } else {
712 print '';
713 }
714 $totalbuyingprice += $estimatedvalue;
715 print '</td>';
716
717 // Selling value
718 print '<td class="right"';
719 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
720 print ' title="'.dolPrintHTMLForAttribute($langs->trans("SellingPrice").' ('.$langs->trans("Currently").'): '.price(price2num($objp->price, 'MU'), 1));
721 }
722 print '">';
723 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
724 print '<span class="amount">';
725 if ($stock || (float) ($stock * $objp->price)) {
726 print price(price2num($stock * $objp->price, 'MT'), 1);
727 }
728 print '</span>';
729 $totalsellingprice += $stock * $objp->price;
730 } else {
731 $htmltext = $langs->trans("OptionMULTIPRICESIsOn");
732 print $form->textwithtooltip('<span class="opacitymedium">'.$langs->trans("Variable").'</span>', $htmltext);
733 }
734 print '</td>';
735
736 // Movements
737 print '<td class="right">';
738 if ($nbofmovement > 0) {
739 $url = DOL_URL_ROOT.'/product/stock/movement_list.php?idproduct='.$objp->rowid;
740 if (GETPOSTISSET('datemonth')) {
741 $url .= '&search_date_startday='.GETPOSTINT('dateday');
742 $url .= '&search_date_startmonth='.GETPOSTINT('datemonth');
743 $url .= '&search_date_startyear='.GETPOSTINT('dateyear');
744 }
745 if (count($search_fk_warehouse) > 1) {
746 $url = ''; // Do not show link, multi warehouse as filter not managed yet by target page
747 } else {
748 foreach ($search_fk_warehouse as $val) {
749 $url .= ($val > 0 ? '&search_warehouse='.((int) $val) : '');
750 }
751 }
752 if ($url) {
753 print '<a href="'.$url.'">';
754 }
755 print $langs->trans("Movements");
756 print '<span class="tabs paddingleft"><span class="badge">'.$nbofmovement.'</span></span>';
757 if ($url) {
758 print '</a>';
759 }
760 }
761 print '</td>';
762
763 // Current stock
764 print '<td class="right">'.($currentstock ? price(price2num($currentstock, 'MS')) : '<span class="opacitymedium">0</span>').'</td>';
765 }
766 $totalcurrentstock += $currentstock;
767
768 // Fields from hook
769 $parameters = array('objp' => $objp);
770 $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters); // Note that $action and $object may have been modified by hook
771 print $hookmanager->resPrint;
772
773 // Action column
774 if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
775 print '<td class="right"></td>';
776 }
777
778 print '</tr>'."\n";
779 }
780 }
781 $i++;
782}
783
784$parameters = array('sql' => $sql);
785$reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters); // Note that $action and $object may have been modified by hook
786if ($ext != 'csv') {
787 print $hookmanager->resPrint;
788}
789
790$colspan = 8;
791if ($mode == 'future') {
792 $colspan++;
793}
794
795if ($ext == 'csv') {
796 print implode(
797 ";",
798 ($mode == 'future') ? array(
799 '"'.$langs->trans("Totalforthispage").'"',
800 '',
801 $productid > 0 ? price2num($totalcurrentstock, 'MS') : '',
802 '',
803 price(price2num($totalvirtualstock, 'MS'))) :
804 array(
805 '"'.$langs->trans("Totalforthispage").'"',
806 '',
807 '',
808 '"'.price2num($totalbuyingprice, 'MT').'"',
809 !getDolGlobalString('PRODUIT_MULTIPRICES') ? '"'.price2num($totalsellingprice, 'MT').'"' : '',
810 '',
811 $productid > 0 ? price2num($totalcurrentstock, 'MS') : '')
812 );
813} else {
814 if (empty($date) || !$dateIsValid) {
815 print '<tr><td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("EnterADateCriteria").'</span></td></tr>';
816 } else {
817 print '<tr class="liste_total">';
818 print '<td>'.$langs->trans("Totalforthispage").'</td>';
819 print '<td></td>';
820 if ($mode == 'future') {
821 print '<td class="right">'.price(price2num($totalcurrentstock, 'MS')).'</td>';
822 print '<td></td>';
823 print '<td></td>';
824 print '<td class="right">'.price(price2num($totalvirtualstock, 'MS')).'</td>';
825 } else {
826 print '<td></td>';
827 print '<td class="right">'.price(price2num($totalbuyingprice, 'MT')).'</td>';
828 if (!getDolGlobalString('PRODUIT_MULTIPRICES')) {
829 print '<td class="right">'.price(price2num($totalsellingprice, 'MT')).'</td>';
830 } else {
831 print '<td></td>';
832 }
833 print '<td></td>';
834 print '<td class="right">'.($productid > 0 ? price(price2num($totalcurrentstock, 'MS')) : '').'</td>';
835 }
836 print '<td></td>';
837 print '</tr>';
838 }
839
840 print '</table>';
841 print '</div>';
842
843 print dol_get_fiche_end();
844
845 print '</form>';
846
847 llxFooter();
848}
849
850if (!empty($resql)) {
851 $db->free($resql);
852}
853
854$db->close();
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:87
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:71
const STATUS_OPEN_INTERNAL
Warehouse open and only operations for stock transfers/corrections allowed (not for customer shipping...
const STATUS_OPEN_ALL
Warehouse open and any operations are allowed (customer shipping, supplier dispatch,...
Class to manage generation of HTML components Only common components must be here.
Class with static methods for building HTML components related to products Only components common to ...
Class to manage products or services.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
print_barre_liste($title, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $selectlimitsuffix=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0)
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
if(!defined( 'NOREQUIREMENU')) if(!empty(GETPOST('seteventmessages', 'alpha'))) if(!function_exists("llxHeader")) top_httphead($contenttype='text/html', $forcenocache=0)
Show HTTP header.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.