dolibarr 25.0.0-alpha
reassortlot.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2001-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2016 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2005-2018 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
6 * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
7 * Copyright (C) 2016 Ferran Marcet <fmarcet@2byte.es>
8 * Copyright (C) 2019 Juanjo Menent <jmenent@2byte.es>
9 * Copyright (C) 2021 Noé Cendrier <noe.cendrier@altairis.fr>
10 * Copyright (C) 2024-2026 Frédéric France <frederic.france@free.fr>
11 * Copyright (C) 2026 MDW <mdeweerd@users.noreply.github.com>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <https://www.gnu.org/licenses/>.
25 */
26
33// Load Dolibarr environment
34require '../main.inc.php';
35require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
36require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
37require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
38require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
39require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
40require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
41
51// Load translation files required by the page
52$langs->loadLangs(array('products', 'stocks', 'productbatch', 'categories'));
53
54$action = GETPOST('action', 'aZ09') ? GETPOST('action', 'aZ09') : 'view'; // The action 'add', 'create', 'edit', 'update', 'view', ...
55$massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists)
56$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'myobjectlist'; // To manage different context of search
57$backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page
58$optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print')
59$mode = GETPOST('mode', 'aZ');
60
61$sref = GETPOST("sref", 'alpha');
62$snom = GETPOST("snom", 'alpha');
63$search_all = trim(GETPOST('search_all', 'alphanohtml'));
64$type = GETPOSTISSET('type') ? GETPOSTINT('type') : Product::TYPE_PRODUCT;
65$search_barcode = GETPOST("search_barcode", 'alpha');
66$search_warehouse = GETPOST('search_warehouse', 'alpha');
67$search_batch = GETPOST('search_batch', 'alpha');
68$search_toolowstock = GETPOST('search_toolowstock');
69$search_subjecttolotserial = GETPOST('search_subjecttolotserial');
70$tosell = GETPOST("tosell");
71$tobuy = GETPOST("tobuy");
72$fourn_id = GETPOSTINT("fourn_id");
73$sbarcode = GETPOSTINT("sbarcode");
74$search_stock_physique = GETPOST('search_stock_physique', 'alpha');
75
76// Load variable for pagination
77$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
78$sortfield = GETPOST('sortfield', 'aZ09comma');
79$sortorder = GETPOST('sortorder', 'aZ09comma');
80$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
81if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) {
82 // If $page is not defined, or '' or -1 or if we click on clear filters
83 $page = 0;
84}
85$offset = $limit * $page;
86$pageprev = $page - 1;
87$pagenext = $page + 1;
88
89// Initialize array of search criteria
90$object = new Product($db);
91$search_sale = GETPOST("search_sale");
92if (GETPOSTISSET('catid')) {
93 $search_categ = GETPOSTINT('catid');
94} else {
95 $search_categ = GETPOSTINT('search_categ');
96}
97$search_warehouse_categ = GETPOSTINT('search_warehouse_categ');
98
99// Fetch optionals attributes and labels
100$extrafields->fetch_name_optionals_label($object->table_element);
101//$extrafields->fetch_name_optionals_label($object->table_element_line);
102
103$search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
104
105// Default sort order (if not yet defined by previous GETPOST)
106if (!$sortfield) {
107 reset($object->fields); // Reset is required to avoid key() to return null.
108 $sortfield = "p.".key($object->fields); // Set here default search field. By default 1st field in definition.
109}
110if (!$sortorder) {
111 $sortorder = "ASC";
112}
113
114
115// Initialize array of search criteria
116$search = array();
117foreach ($object->fields as $key => $val) {
118 if (GETPOST('search_'.$key, 'alpha') !== '') {
119 $search[$key] = GETPOST('search_'.$key, 'alpha');
120 }
121 if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) {
122 $search[$key.'_dtstart'] = dol_mktime(0, 0, 0, GETPOSTINT('search_'.$key.'_dtstartmonth'), GETPOSTINT('search_'.$key.'_dtstartday'), GETPOSTINT('search_'.$key.'_dtstartyear'));
123 $search[$key.'_dtend'] = dol_mktime(23, 59, 59, GETPOSTINT('search_'.$key.'_dtendmonth'), GETPOSTINT('search_'.$key.'_dtendday'), GETPOSTINT('search_'.$key.'_dtendyear'));
124 }
125}
126$key = 'sellby';
127$search[$key.'_dtstart'] = dol_mktime(0, 0, 0, GETPOSTINT('search_'.$key.'_dtstartmonth'), GETPOSTINT('search_'.$key.'_dtstartday'), GETPOSTINT('search_'.$key.'_dtstartyear'));
128$search[$key.'_dtend'] = dol_mktime(23, 59, 59, GETPOSTINT('search_'.$key.'_dtendmonth'), GETPOSTINT('search_'.$key.'_dtendday'), GETPOSTINT('search_'.$key.'_dtendyear'));
129$key = 'eatby';
130$search[$key.'_dtstart'] = dol_mktime(0, 0, 0, GETPOSTINT('search_'.$key.'_dtstartmonth'), GETPOSTINT('search_'.$key.'_dtstartday'), GETPOSTINT('search_'.$key.'_dtstartyear'));
131$search[$key.'_dtend'] = dol_mktime(23, 59, 59, GETPOSTINT('search_'.$key.'_dtendmonth'), GETPOSTINT('search_'.$key.'_dtendday'), GETPOSTINT('search_'.$key.'_dtendyear'));
132
133// Get object canvas (By default, this is not defined, so standard usage of dolibarr)
134$canvas = GETPOST("canvas");
135$objcanvas = null;
136if (!empty($canvas)) {
137 require_once DOL_DOCUMENT_ROOT.'/core/class/canvas.class.php';
138 $objcanvas = new Canvas($db, $action);
139 $objcanvas->getCanvas('product', 'list', $canvas);
140}
141
142// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
143$hookmanager->initHooks(array('reassortlotlist'));
144
145// Definition of array of fields for columns
146$arrayfields = array(
147 array('type' => 'varchar', 'label' => 'Ref', 'checked' => 1, 'enabled' => 1, 'position' => 1),
148 array('type' => 'varchar', 'label' => 'Label', 'checked' => 1, 'enabled' => 1, 'position' => 1),
149 array('type' => 'int', 'label' => 'Warehouse', 'checked' => 1, 'enabled' => 1, 'position' => 1),
150 array('type' => 'varchar', 'label' => 'Lot', 'checked' => 1, 'enabled' => 1, 'position' => 1),
151 array('type' => 'varchar', 'label' => 'DLC', 'checked' => 1, 'enabled' => 1, 'position' => 1),
152 array('type' => 'varchar', 'label' => 'DLUO', 'checked' => 1, 'enabled' => 1, 'position' => 1),
153 array('type' => 'int', 'label' => 'Stock', 'checked' => 1, 'enabled' => 1, 'position' => 1),
154 array('type' => 'int', 'label' => 'StatusSell', 'checked' => 1, 'enabled' => 1, 'position' => 1),
155 array('type' => 'int', 'label' => 'StatusBuy', 'checked' => 1, 'enabled' => 1, 'position' => 1),
156);
157
158//$arrayfields['anotherfield'] = array('type'=>'integer', 'label'=>'AnotherField', 'checked'=>1, 'enabled'=>1, 'position'=>90, 'csslist'=>'right');
159$arrayfields = dol_sort_array($arrayfields, 'position');
160
161
162// Security check
163if ($user->socid) {
164 $socid = $user->socid;
165}
166$result = restrictedArea($user, 'produit|service', 0, 'product&product');
167$result = restrictedArea($user, 'stock');
168
169
170/*
171 * Actions
172 */
173
174if (GETPOST('cancel', 'alpha')) {
175 $action = 'list';
176 $massaction = '';
177}
178if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') {
179 $massaction = '';
180}
181
182$parameters = array();
183$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
184if ($reshook < 0) {
185 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
186}
187
188if (empty($reshook)) {
189 // Selection of new fields
190 include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php';
191
192 // Purge search criteria
193 if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers
194 foreach ($object->fields as $key => $val) {
195 $search[$key] = '';
196 if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) {
197 $search[$key.'_dtstart'] = '';
198 $search[$key.'_dtend'] = '';
199 }
200 }
201 $search['sellby_dtstart'] = '';
202 $search['eatby_dtstart'] = '';
203 $search['sellby_dtend'] = '';
204 $search['eatby_dtend'] = '';
205 $sref = "";
206 $snom = "";
207 $search_all = "";
208 $tosell = "";
209 $tobuy = "";
210 $search_sale = "";
211 $search_categ = "";
212 $search_warehouse_categ = "";
213 $search_toolowstock = '';
214 $search_subjecttolotserial = '';
215 $search_batch = '';
216 $search_warehouse = '';
217 $fourn_id = '';
218 $sbarcode = '';
219 $search_stock_physique = '';
220 $toselect = array();
221 $search_array_options = array();
222 }
223 if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')
224 || GETPOST('button_search_x', 'alpha') || GETPOST('button_search.x', 'alpha') || GETPOST('button_search', 'alpha')) {
225 $massaction = ''; // Protection to avoid mass action if we force a new search during a mass action confirmation
226 }
227
228 // Mass actions
229 /*$objectclass = 'MyObject';
230 $objectlabel = 'MyObject';
231 $uploaddir = $conf->mymodule->dir_output;
232 include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php';
233 */
234}
235
236
237
238/*
239 * View
240 */
241
242$form = new Form($db);
243$htmlother = new FormOther($db);
244
245$now = dol_now();
246
247$helpurl = 'EN:Module_Stocks_En|FR:Module_Stock|ES:M&oacute;dulo_Stocks';
248$title = $langs->trans("ProductsAndServices");
249$morejs = array();
250$morecss = array();
251
252$sql = 'SELECT p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,';
253$sql .= ' p.fk_product_type, p.tms as datem,';
254$sql .= ' p.duration, p.tosell as statut, p.tobuy, p.seuil_stock_alerte, p.desiredstock, p.stock, p.tosell, p.tobuy, p.tobatch,';
255$sql .= ' ps.fk_entrepot, ps.reel,';
256$sql .= ' e.ref as warehouse_ref, e.lieu as warehouse_lieu, e.fk_parent as warehouse_parent,';
257$sql .= ' pb.batch, pb.eatby as oldeatby, pb.sellby as oldsellby,';
258$sql .= ' pl.rowid as lotid, pl.eatby, pl.sellby,';
259$sql .= ' SUM(pb.qty) as stock_physique, COUNT(pb.rowid) as nbinbatchtable';
260// Add fields from hooks
261$parameters = array();
262$reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters, $object); // Note that $action and $object may have been modified by hook
263$sql .= $hookmanager->resPrint;
264$sql .= ' FROM '.MAIN_DB_PREFIX.'product as p';
265$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as ps on p.rowid = ps.fk_product'; // Detail for each warehouse
266$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'entrepot as e on ps.fk_entrepot = e.rowid'; // Link on unique key
267$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_batch as pb on pb.fk_product_stock = ps.rowid'; // Detail for each lot on each warehouse
268$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_lot as pl on pl.fk_product = p.rowid AND pl.batch = pb.batch'; // Link on unique key
269// Add table from hooks
270$parameters = array();
271$reshook = $hookmanager->executeHooks('printFieldListFrom', $parameters, $object); // Note that $action and $object may have been modified by hook
272$sql .= $hookmanager->resPrint;
273$sql .= " WHERE p.entity IN (".getEntity('product').") AND e.entity IN (".getEntity('stock').")";
274if (!empty($search_categ) && $search_categ != '-1') {
275 $sql .= " AND ";
276 if ($search_categ == -2) {
277 $sql .= " NOT EXISTS ";
278 } else {
279 $sql .= " EXISTS ";
280 }
281 $sql .= "(";
282 $sql .= " SELECT cp.fk_categorie, cp.fk_product";
283 $sql .= " FROM " . MAIN_DB_PREFIX . "categorie_product as cp";
284 $sql .= " WHERE cp.fk_product = p.rowid"; // Join for the needed table to filter by categ
285 if ($search_categ > 0) {
286 $sql .= " AND cp.fk_categorie = " . ((int) $search_categ);
287 }
288 $sql .= ")";
289}
290if (!empty($search_warehouse_categ) && $search_warehouse_categ != '-1') {
291 $sql .= " AND ";
292 if ($search_warehouse_categ == -2) {
293 $sql .= " NOT EXISTS ";
294 } else {
295 $sql .= " EXISTS ";
296 }
297 $sql .= "(";
298 $sql .= " SELECT cp.fk_categorie, cp.fk_warehouse";
299 $sql .= " FROM " . MAIN_DB_PREFIX . "categorie_warehouse as cp";
300 $sql .= " WHERE cp.fk_warehouse = e.rowid"; // Join for the needed table to filter by categ
301 if ($search_warehouse_categ > 0) {
302 $sql .= " AND cp.fk_categorie = " . ((int) $search_warehouse_categ);
303 }
304 $sql .= ")";
305}
306if ($search_all) {
307 $sql .= natural_search(array('p.ref', 'p.label', 'p.description', 'p.note'), $search_all);
308}
309// if the type is not 1, we show all products (type = 0,2,3)
310if (dol_strlen((string) $type)) {
311 if ($type == 1) {
312 $sql .= " AND p.fk_product_type = '1'";
313 } else {
314 $sql .= " AND p.fk_product_type <> '1'";
315 }
316}
317if ($search_subjecttolotserial) {
318 $sql .= " AND p.tobatch > 0";
319}
320if ($sref) {
321 $sql .= natural_search("p.ref", $sref);
322}
323if ($search_barcode) {
324 $sql .= natural_search("p.barcode", $search_barcode);
325}
326if ($snom) {
327 $sql .= natural_search("p.label", $snom);
328}
329if (!empty($tosell)) {
330 $sql .= " AND p.tosell = ".((int) $tosell);
331}
332if (!empty($tobuy)) {
333 $sql .= " AND p.tobuy = ".((int) $tobuy);
334}
335if (!empty($canvas)) {
336 $sql .= " AND p.canvas = '".$db->escape($canvas)."'";
337}
338if ($fourn_id > 0) {
339 $sql .= " AND p.rowid = pf.fk_product AND pf.fk_soc = ".((int) $fourn_id);
340}
341if ($search_warehouse) {
342 $sql .= natural_search("e.ref", $search_warehouse);
343}
344if ($search_batch) {
345 $sql .= natural_search("pb.batch", $search_batch);
346}
347
348foreach ($search as $key => $val) {
349 if (array_key_exists($key, $object->fields)) {
350 if ($key == 'status' && $search[$key] == -1) {
351 continue;
352 }
353 $mode_search = (($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key])) ? 1 : 0);
354 if ((strpos($object->fields[$key]['type'], 'integer:') === 0) || (strpos($object->fields[$key]['type'], 'sellist:') === 0) || !empty($object->fields[$key]['arrayofkeyval'])) {
355 if ($search[$key] == '-1' || ($search[$key] === '0' && (empty($object->fields[$key]['arrayofkeyval']) || !array_key_exists('0', $object->fields[$key]['arrayofkeyval'])))) {
356 $search[$key] = '';
357 }
358 $mode_search = 2;
359 }
360 if ($search[$key] != '') {
361 $sql .= natural_search("t.".$db->escape($key), $search[$key], (($key == 'status') ? 2 : $mode_search));
362 }
363 } else {
364 if (preg_match('/(_dtstart|_dtend)$/', $key) && $search[$key] != '') {
365 $columnName = preg_replace('/(_dtstart|_dtend)$/', '', $key);
366 if ($columnName == 'eatby' || $columnName == 'sellby') {
367 if (preg_match('/_dtstart$/', $key)) {
368 $sql .= " AND pl.".$db->sanitize($columnName)." >= '".$db->idate($search[$key])."'";
369 }
370 if (preg_match('/_dtend$/', $key)) {
371 $sql .= " AND pl.".$db->sanitize($columnName)." <= '".$db->idate($search[$key])."'";
372 }
373 }
374 }
375 }
376}
377// Add where from hooks
378$parameters = array();
379$reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object); // Note that $action and $object may have been modified by hook
380$sql .= $hookmanager->resPrint;
381
382$sql .= " GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,";
383$sql .= " p.fk_product_type, p.tms,";
384$sql .= " p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte, p.desiredstock, p.stock, p.tosell, p.tobuy, p.tobatch,";
385$sql .= " ps.fk_entrepot, ps.reel,";
386$sql .= " e.ref, e.lieu, e.fk_parent,";
387$sql .= " pb.batch, pb.eatby, pb.sellby,";
388$sql .= " pl.rowid, pl.eatby, pl.sellby";
389// Add GROUP BY from hooks
390$parameters = array();
391$reshook = $hookmanager->executeHooks('printFieldListGroupBy', $parameters, $object); // Note that $action and $object may have been modified by hook
392$sql .= $hookmanager->resPrint;
393$sql_having = '';
394if ($search_toolowstock) {
395 $sql_having .= " HAVING SUM(".$db->ifsql('ps.reel IS NULL', '0', 'ps.reel').") < p.seuil_stock_alerte"; // Not used yet
396}
397if ($search_stock_physique != '') {
398 $natural_search_physique = natural_search('__SEARCH_PHYSIQUE__', $search_stock_physique, 1, 1);
399 $natural_search_physique = " " . substr(str_replace('__SEARCH_PHYSIQUE__', 'SUM(COALESCE(pb.qty, ps.reel, 0))', $natural_search_physique), 1, -1); // remove first "(" and last ")" characters
400 if (!empty($sql_having)) {
401 $sql_having .= " AND";
402 } else {
403 $sql_having .= " HAVING";
404 }
405 $sql_having .= $natural_search_physique; // natural_search gives save sql @phan-suppress-current-line SqlInjection
406}
407// Add HAVING from hooks
408$parameters = array();
409$reshook = $hookmanager->executeHooks('printFieldListHaving', $parameters, $object); // Note that $action and $object may have been modified by hook
410if (empty($reshook)) {
411 if (empty($sql_having)) {
412 $sql_having .= " HAVING 1=1";
413 }
414 $sql_having .= $hookmanager->resPrint;
415} else {
416 $sql_having = $hookmanager->resPrint;
417}
418
419if (!empty($sql_having)) {
420 $sql .= $sql_having;
421}
422
423//print $sql;
424
425// Count total nb of records
426$nbtotalofrecords = '';
427if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
428 $resql = $db->query($sql);
429 $nbtotalofrecords = $db->num_rows($resql);
430
431 if (($page * $limit) > (int) $nbtotalofrecords) { // if total of record found is smaller than page * limit, goto and load page 0
432 $page = 0;
433 $offset = 0;
434 }
435 $db->free($resql);
436}
437
438// Complete request and execute it with limit
439$sql .= $db->order($sortfield, $sortorder);
440if ($limit) {
441 $sql .= $db->plimit($limit + 1, $offset);
442}
443
444$resql = $db->query($sql);
445if (!$resql) {
447 exit;
448}
449
450$num = $db->num_rows($resql);
451
452$i = 0;
453
454if ($num == 1 && GETPOST('autojumpifoneonly') && ($search_all || $snom || $sref)) {
455 $objp = $db->fetch_object($resql);
456 header("Location: card.php?id=$objp->rowid");
457 exit;
458}
459
460if ($type == 1) {
461 $texte = $langs->trans("Services");
462} else {
463 $texte = $langs->trans("Products");
464}
465$texte .= ' ('.$langs->trans("StocksByLotSerial").')';
466
467$param = '';
468if (!empty($mode)) {
469 $param .= '&mode='.urlencode($mode);
470}
471if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) {
472 $param .= '&contextpage='.urlencode($contextpage);
473}
474if ($limit > 0 && $limit != $conf->liste_limit) {
475 $param .= '&limit='.((int) $limit);
476}
477foreach ($search as $key => $val) {
478 if (is_array($search[$key]) && count($search[$key])) {
479 foreach ($search[$key] as $skey) {
480 if ($skey != '') {
481 $param .= '&search_'.$key.'[]='.urlencode($skey);
482 }
483 }
484 } elseif ($search[$key] != '') {
485 $param .= '&search_'.$key.'='.urlencode($search[$key]);
486 }
487}
488if ($optioncss != '') {
489 $param .= '&optioncss='.urlencode($optioncss);
490}
491if ($search_all) {
492 $param .= "&search_all=".urlencode($search_all);
493}
494if ($tosell) {
495 $param .= "&tosell=".urlencode($tosell);
496}
497if ($tobuy) {
498 $param .= "&tobuy=".urlencode($tobuy);
499}
500if ($type != '') {
501 $param .= "&type=".urlencode((string) ($type));
502}
503if ($fourn_id) {
504 $param .= "&fourn_id=".urlencode((string) ($fourn_id));
505}
506if ($snom) {
507 $param .= "&snom=".urlencode($snom);
508}
509if ($sref) {
510 $param .= "&sref=".urlencode($sref);
511}
512if ($search_batch) {
513 $param .= "&search_batch=".urlencode($search_batch);
514}
515if ($sbarcode) {
516 $param .= "&sbarcode=".urlencode((string) ($sbarcode));
517}
518if ($search_warehouse) {
519 $param .= "&search_warehouse=".urlencode($search_warehouse);
520}
521if ($search_toolowstock) {
522 $param .= "&search_toolowstock=".urlencode($search_toolowstock);
523}
524if ($search_subjecttolotserial) {
525 $param .= "&search_subjecttolotserial=".urlencode($search_subjecttolotserial);
526}
527if ($search_sale) {
528 $param .= "&search_sale=".urlencode($search_sale);
529}
530if (!empty($search_categ) && $search_categ != '-1') {
531 $param .= "&search_categ=".urlencode((string) ($search_categ));
532}
533if (!empty($search_warehouse_categ) && $search_warehouse_categ != '-1') {
534 $param .= "&search_warehouse_categ=".urlencode((string) ($search_warehouse_categ));
535}
536if ($search_stock_physique) {
537 $param .= '&search_stock_physique=' . urlencode($search_stock_physique);
538}
539/*if ($eatby) $param.="&eatby=".$eatby;
540if ($sellby) $param.="&sellby=".$sellby;*/
541
542llxHeader("", $title, $helpurl, $texte, 0, 0, '', '', '', 'mod-product page-reassortlot');
543
544print '<form id="searchFormList" action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formulaire">'."\n";
545if ($optioncss != '') {
546 print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
547}
548print '<input type="hidden" name="token" value="'.newToken().'">';
549print '<input type="hidden" name="action" value="list">';
550print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
551print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
552print '<input type="hidden" name="type" value="'.$type.'">';
553print '<input type="hidden" name="page" value="'.$page.'">';
554print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
555print '<input type="hidden" name="mode" value="'.$mode.'">';
556
557print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'product', 0, '', '', $limit, 0, 0, 1);
558
559/*
560if ($search_categ > 0) {
561 print "<div id='ways'>";
562 $c = new Categorie($db);
563 $c->fetch($search_categ);
564 $ways = $c->print_all_ways('auto', 'product/reassortlot.php');
565 print " &gt; ".$ways[0]."<br>\n";
566 print "</div><br>";
567}
568*/
569
570// Filter on categories
571$moreforfilter = '';
572if (isModEnabled('category')) {
573 $moreforfilter .= '<div class="divsearchfield">';
574 $moreforfilter .= img_picto($langs->trans('ProductsCategoriesShort'), 'category', 'class="pictofixedwidth"');
575 $moreforfilter .= $htmlother->select_categories(Categorie::TYPE_PRODUCT, $search_categ, 'search_categ', 1, $langs->trans("ProductsCategoryShort"), 'maxwidth400');
576 $moreforfilter .= '</div>';
577}
578// Filter on warehouse categories
579if (isModEnabled('category')) {
580 $moreforfilter .= '<div class="divsearchfield">';
581 $moreforfilter .= img_picto($langs->trans('StockCategoriesShort'), 'category', 'class="pictofixedwidth"');
582 $moreforfilter .= $htmlother->select_categories(Categorie::TYPE_WAREHOUSE, $search_warehouse_categ, 'search_warehouse_categ', 1, $langs->trans("StockCategoriesShort"), 'maxwidth400');
583 $moreforfilter .= '</div>';
584}
585
586$moreforfilter .= '<label for="search_subjecttolotserial">'.$langs->trans("SubjectToLotSerialOnly").' </label><input type="checkbox" id="search_subjecttolotserial" name="search_subjecttolotserial" value="1"'.($search_subjecttolotserial ? ' checked' : '').'>';
587
588
589print '<div class="liste_titre liste_titre_bydiv centpercent">';
590print $moreforfilter;
591$parameters = array();
592$reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
593print $hookmanager->resPrint;
594print '</div>';
595
596print '<div class="div-table-responsive">';
597print '<table class="tagtable nobottomiftotal liste'.($moreforfilter ? " listwithfilterbefore" : "").'">';
598
599// Fields title search
600// --------------------------------------------------------------------
601print '<tr class="liste_titre_filter">';
602// Action column
603if ($conf->main_checkbox_left_column) {
604 print '<td class="liste_titre maxwidthsearch">';
605 $searchpicto = $form->showFilterButtons();
606 print $searchpicto;
607 print '</td>';
608}
609print '<td class="liste_titre">';
610print '<input class="flat" type="text" name="sref" size="6" value="'.dol_escape_htmltag($sref).'">';
611print '</td>';
612print '<td class="liste_titre">';
613print '<input class="flat" type="text" name="snom" size="8" value="'.dol_escape_htmltag($snom).'">';
614print '</td>';
615if (isModEnabled("service") && $type == 1) {
616 print '<td class="liste_titre">';
617 print '&nbsp;';
618 print '</td>';
619}
620
621print '<td class="liste_titre"><input class="flat" type="text" name="search_warehouse" size="6" value="'.dol_escape_htmltag($search_warehouse).'"></td>';
622print '<td class="liste_titre center"><input class="flat" type="text" name="search_batch" size="6" value="'.dol_escape_htmltag($search_batch).'"></td>';
623if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY')) {
624 print '<td class="liste_titre center">';
625 $key = 'sellby';
626 print '<div class="nowrap">';
627 print $form->selectDate($search[$key.'_dtstart'] ? $search[$key.'_dtstart'] : '', "search_".$key."_dtstart", 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('From'));
628 print '</div>';
629 print '<div class="nowrap">';
630 print $form->selectDate($search[$key.'_dtend'] ? $search[$key.'_dtend'] : '', "search_".$key."_dtend", 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('to'));
631 print '</div>';
632 print '</td>';
633}
634if (!getDolGlobalString('PRODUCT_DISABLE_EATBY')) {
635 print '<td class="liste_titre center">';
636 $key = 'eatby';
637 print '<div class="nowrap">';
638 print $form->selectDate($search[$key.'_dtstart'] ? $search[$key.'_dtstart'] : '', "search_".$key."_dtstart", 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('From'));
639 print '</div>';
640 print '<div class="nowrap">';
641 print $form->selectDate($search[$key.'_dtend'] ? $search[$key.'_dtend'] : '', "search_".$key."_dtend", 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('to'));
642 print '</div>';
643 print '</td>';
644}
645// Physical stock
646print '<td class="liste_titre right">';
647print '<input class="flat" type="text" size="5" name="search_stock_physique" value="'.dol_escape_htmltag($search_stock_physique).'">';
648print '</td>';
649print '<td class="liste_titre">&nbsp;</td>';
650print '<td class="liste_titre">&nbsp;</td>';
651print '<td class="liste_titre">&nbsp;</td>';
652$parameters = array();
653$reshook = $hookmanager->executeHooks('printFieldListOption', $parameters); // Note that $action and $object may have been modified by hook
654print $hookmanager->resPrint;
655// Action column
656if (!$conf->main_checkbox_left_column) {
657 print '<td class="liste_titre maxwidthsearch">';
658 $searchpicto = $form->showFilterButtons();
659 print $searchpicto;
660 print '</td>';
661}
662print '</tr>'."\n";
663
664$totalarray = array();
665$totalarray['nbfield'] = 0;
666
667// Fields title label
668// --------------------------------------------------------------------
669print '<tr class="liste_titre">';
670// Action column
671if ($conf->main_checkbox_left_column) {
673}
674print_liste_field_titre("Ref", $_SERVER["PHP_SELF"], "p.ref", '', $param, "", $sortfield, $sortorder);
675print_liste_field_titre("Label", $_SERVER["PHP_SELF"], "p.label", '', $param, "", $sortfield, $sortorder);
676if (isModEnabled("service") && $type == 1) {
677 print_liste_field_titre("Duration", $_SERVER["PHP_SELF"], "p.duration", '', $param, "", $sortfield, $sortorder, 'center ');
678}
679print_liste_field_titre("Warehouse", $_SERVER["PHP_SELF"], "e.ref", '', $param, "", $sortfield, $sortorder);
680//print_liste_field_titre("DesiredStock", $_SERVER["PHP_SELF"], "p.desiredstock",$param,"",'',$sortfield,$sortorder, 'right );
681print_liste_field_titre("Batch", $_SERVER["PHP_SELF"], "pb.batch", '', $param, "", $sortfield, $sortorder, 'center ');
682if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY')) {
683 print_liste_field_titre("SellByDate", $_SERVER["PHP_SELF"], "pl.sellby", '', $param, "", $sortfield, $sortorder, 'center ');
684}
685if (!getDolGlobalString('PRODUCT_DISABLE_EATBY')) {
686 print_liste_field_titre("EatByDate", $_SERVER["PHP_SELF"], "pl.eatby", '', $param, "", $sortfield, $sortorder, 'center ');
687}
688print_liste_field_titre("PhysicalStock", $_SERVER["PHP_SELF"], "stock_physique", '', $param, "", $sortfield, $sortorder, 'right ');
689// TODO Add info of running suppliers/customers orders
690//print_liste_field_titre("TheoreticalStock",$_SERVER["PHP_SELF"], "stock_theorique",$param,"",'',$sortfield,$sortorder, 'right ');
692print_liste_field_titre("ProductStatusOnSell", $_SERVER["PHP_SELF"], "p.tosell", "", $param, '', $sortfield, $sortorder, 'right ');
693print_liste_field_titre("ProductStatusOnBuy", $_SERVER["PHP_SELF"], "p.tobuy", "", $param, '', $sortfield, $sortorder, 'right ');
694// Hook fields
695$parameters = array('param' => $param, 'sortfield' => $sortfield, 'sortorder' => $sortorder);
696$reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters); // Note that $action and $object may have been modified by hook
697print $hookmanager->resPrint;
698if (!$conf->main_checkbox_left_column) {
700}
701print "</tr>\n";
702
703$product_static = new Product($db);
704$product_lot_static = new Productlot($db);
705$warehousetmp = new Entrepot($db);
706
707// Loop on record
708// --------------------------------------------------------------------
709$i = 0;
710$savnbfield = $totalarray['nbfield'];
711$totalarray['nbfield'] = 0;
712$imaxinloop = ($limit ? min($num, $limit) : $num);
713while ($i < $imaxinloop) {
714 $objp = $db->fetch_object($resql);
715
716 // Multilangs
717 if (getDolGlobalInt('MAIN_MULTILANGS')) { // si l'option est active
718 // TODO Use a cache
719 $sql = "SELECT label";
720 $sql .= " FROM ".MAIN_DB_PREFIX."product_lang";
721 $sql .= " WHERE fk_product = ".((int) $objp->rowid);
722 $sql .= " AND lang = '".$db->escape($langs->getDefaultLang())."'";
723 $sql .= " LIMIT 1";
724
725 $result = $db->query($sql);
726 if ($result) {
727 $objtp = $db->fetch_object($result);
728 if (!empty($objtp->label)) {
729 $objp->label = $objtp->label;
730 }
731 }
732 }
733
734 $product_static->ref = $objp->ref;
735 $product_static->id = $objp->rowid;
736 $product_static->label = $objp->label;
737 $product_static->type = $objp->fk_product_type;
738 $product_static->entity = $objp->entity;
739 $product_static->status = $objp->tosell;
740 $product_static->status_buy = $objp->tobuy;
741 $product_static->status_batch = $objp->tobatch;
742
743 $product_lot_static->batch = $objp->batch;
744 $product_lot_static->fk_product = $objp->rowid;
745 $product_lot_static->id = $objp->lotid;
746 $product_lot_static->eatby = $objp->eatby;
747 $product_lot_static->sellby = $objp->sellby;
748
749
750 $warehousetmp->id = $objp->fk_entrepot;
751 $warehousetmp->ref = $objp->warehouse_ref;
752 $warehousetmp->label = $objp->warehouse_ref;
753 $warehousetmp->fk_parent = $objp->warehouse_parent;
754
755 print '<tr>';
756
757 // Action column
758 if ($conf->main_checkbox_left_column) {
759 print '<td></td>';
760 if (!$i) {
761 $totalarray['nbfield']++;
762 }
763 }
764
765 // Ref
766 print '<td class="tdoverflowmax250">';
767 print $product_static->getNomUrl(1);
768 //if ($objp->stock_theorique < $objp->seuil_stock_alerte) print ' '.img_warning($langs->trans("StockTooLow"));
769 print '</td>';
770 if (!$i) {
771 $totalarray['nbfield']++;
772 }
773
774 // Label
775 print '<td class="tdoverflowmax150">'.$objp->label.'</td>';
776 if (!$i) {
777 $totalarray['nbfield']++;
778 }
779
780 if (isModEnabled("service") && $type == 1) {
781 print '<td class="center">';
782 $regs = array();
783 if (preg_match('/([0-9]+)y/i', $objp->duration, $regs)) {
784 print $regs[1].' '.$langs->trans("DurationYear");
785 } elseif (preg_match('/([0-9]+)m/i', $objp->duration, $regs)) {
786 print $regs[1].' '.$langs->trans("DurationMonth");
787 } elseif (preg_match('/([0-9]+)d/i', $objp->duration, $regs)) {
788 print $regs[1].' '.$langs->trans("DurationDay");
789 } else {
790 print $objp->duration;
791 }
792 if (!$i) {
793 $totalarray['nbfield']++;
794 }
795 print '</td>';
796 }
797 //print '<td class="right">'.$objp->stock_theorique.'</td>';
798 //print '<td class="right">'.$objp->seuil_stock_alerte.'</td>';
799 //print '<td class="right">'.$objp->desiredstock.'</td>';
800
801 // Warehouse
802 print '<td class="nowrap">';
803 if ($objp->fk_entrepot > 0) {
804 print $warehousetmp->getNomUrl(1);
805 }
806 if (!$i) {
807 $totalarray['nbfield']++;
808 }
809 print '</td>';
810
811 // Lot
812 print '<td class="center nowrap">';
813 if ($product_lot_static->batch) {
814 print $product_lot_static->getNomUrl(1);
815 }
816 if (!$i) {
817 $totalarray['nbfield']++;
818 }
819 print '</td>';
820
821 if (!getDolGlobalString('PRODUCT_DISABLE_SELLBY')) {
822 print '<td class="center">'.dol_print_date($db->jdate($objp->sellby), 'day').'</td>';
823 if (!$i) {
824 $totalarray['nbfield']++;
825 }
826 }
827
828 if (!getDolGlobalString('PRODUCT_DISABLE_EATBY')) {
829 print '<td class="center">'.dol_print_date($db->jdate($objp->eatby), 'day').'</td>';
830 if (!$i) {
831 $totalarray['nbfield']++;
832 }
833 }
834
835 print '<td class="right">';
836 //if ($objp->seuil_stock_alerte && ($objp->stock_physique < $objp->seuil_stock_alerte)) print img_warning($langs->trans("StockTooLow")).' ';
837 if (is_null($objp->stock_physique)) {
838 if (!empty($objp->reel)) {
839 if ($objp->reel < 0) {
840 print '<span class="warning">';
841 }
842 print price2num($objp->reel, 'MS');
843 if ($objp->reel < 0) {
844 print '</span>';
845 }
846 print($objp->stock_physique < 0 ? ' '.img_warning() : '');
847 }
848 } else {
849 if (!empty($objp->stock_physique)) {
850 if ($objp->stock_physique < 0) {
851 print '<span class="warning">';
852 }
853 print price2num($objp->stock_physique, 'MS');
854 if ($objp->stock_physique < 0) {
855 print '</span>';
856 }
857 print($objp->stock_physique < 0 ? ' '.img_warning() : (($objp->stock_physique > 1 && $objp->tobatch == 2) ? ' '.img_warning($langs->trans('IlligalQtyForSerialNumbers')) : ''));
858 }
859 }
860 print '</td>';
861 if (!$i) {
862 $totalarray['nbfield']++;
863 }
864
865 print '<td class="right">';
866 print img_picto($langs->trans("StockMovement"), 'movement', 'class="pictofixedwidth"');
867 print '<a href="'.DOL_URL_ROOT.'/product/stock/movement_list.php?idproduct='.$product_static->id.'&search_warehouse='.$objp->fk_entrepot.'&search_batch='.($objp->batch != 'Undefined' ? $objp->batch : 'Undefined').'">'.$langs->trans("Movements").'</a>';
868 print '</td>';
869 if (!$i) {
870 $totalarray['nbfield']++;
871 }
872
873 print '<td class="right nowrap">'.$product_static->LibStatut($objp->statut, 5, 0).'</td>';
874 if (!$i) {
875 $totalarray['nbfield']++;
876 }
877
878 print '<td class="right nowrap">'.$product_static->LibStatut($objp->tobuy, 5, 1).'</td>';
879 if (!$i) {
880 $totalarray['nbfield']++;
881 }
882
883 // Fields values from hook
884 $parameters = array('obj' => $objp);
885 $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $product); // Note that $action and $object may have been modified by hook
886 print $hookmanager->resPrint;
887
888 // Action column
889 if (!$conf->main_checkbox_left_column) {
890 print '<td></td>';
891 if (!$i) {
892 $totalarray['nbfield']++;
893 }
894 }
895
896 print "</tr>\n";
897 $i++;
898}
899
900// If no record found
901if ($num == 0) {
902 $colspan = 2;
903 foreach ($arrayfields as $key => $val) {
904 if (!empty($val['checked'])) {
905 $colspan++;
906 }
907 }
908 print '<tr><td colspan="'.$colspan.'"><span class="opacitymedium">'.$langs->trans("NoRecordFound").'</span></td></tr>';
909}
910
911
912$db->free($resql);
913
914print '</table>'."\n";
915print '</div>'."\n";
916
917print '</form>'."\n";
918
919
920// End of page
921llxFooter();
922$db->close();
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
$totalarray
Definition list.php:501
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
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:73
Class to manage canvas.
Class to manage warehouses.
Class to manage generation of HTML components Only common components must be here.
Class to help generate other html components Only common components are here.
Class to manage products or services.
Class with list of lots and properties.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dol_now($mode='gmt')
Return date for now.
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...
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
natural_search($fields, $value, $mode=0, $nofirstand=0, $sqltoadd='')
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
print_liste_field_titre($name, $file="", $field="", $begin="", $param="", $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, $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)
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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.