reassortlot.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  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. *
  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. /**
  24. * \file htdocs/product/reassortlot.php
  25. * \ingroup produit
  26. * \brief Page to list stocks
  27. */
  28. // Load Dolibarr environment
  29. require '../main.inc.php';
  30. require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
  31. require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
  32. require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
  33. require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
  34. require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php';
  35. require_once DOL_DOCUMENT_ROOT.'/product/stock/class/productlot.class.php';
  36. // Load translation files required by the page
  37. $langs->loadLangs(array('products', 'stocks', 'productbatch'));
  38. $action = GETPOST('action', 'aZ09') ?GETPOST('action', 'aZ09') : 'view'; // The action 'add', 'create', 'edit', 'update', 'view', ...
  39. $massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists)
  40. $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'myobjectlist'; // To manage different context of search
  41. $backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page
  42. $optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print')
  43. $mode = GETPOST('mode', 'aZ');
  44. $sref = GETPOST("sref", 'alpha');
  45. $snom = GETPOST("snom", 'alpha');
  46. $sall = trim((GETPOST('search_all', 'alphanohtml') != '') ?GETPOST('search_all', 'alphanohtml') : GETPOST('sall', 'alphanohtml'));
  47. $type = GETPOSTISSET('type') ? GETPOST('type', 'int') : Product::TYPE_PRODUCT;
  48. $search_barcode = GETPOST("search_barcode", 'alpha');
  49. $search_warehouse = GETPOST('search_warehouse', 'alpha');
  50. $search_batch = GETPOST('search_batch', 'alpha');
  51. $search_toolowstock = GETPOST('search_toolowstock');
  52. $search_subjecttolotserial = GETPOST('search_subjecttolotserial');
  53. $tosell = GETPOST("tosell");
  54. $tobuy = GETPOST("tobuy");
  55. $fourn_id = GETPOST("fourn_id", 'int');
  56. $sbarcode = GETPOST("sbarcode", 'int');
  57. $search_stock_physique = GETPOST('search_stock_physique', 'alpha');
  58. // Load variable for pagination
  59. $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit;
  60. $sortfield = GETPOST('sortfield', 'aZ09comma');
  61. $sortorder = GETPOST('sortorder', 'aZ09comma');
  62. $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int');
  63. if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) {
  64. // If $page is not defined, or '' or -1 or if we click on clear filters
  65. $page = 0;
  66. }
  67. $offset = $limit * $page;
  68. $pageprev = $page - 1;
  69. $pagenext = $page + 1;
  70. // Initialize array of search criterias
  71. $object = new Product($db);
  72. $search_sale = GETPOST("search_sale");
  73. if (GETPOSTISSET('catid')) {
  74. $search_categ = GETPOST('catid', 'int');
  75. } else {
  76. $search_categ = GETPOST('search_categ', 'int');
  77. }
  78. // Fetch optionals attributes and labels
  79. $extrafields->fetch_name_optionals_label($object->table_element);
  80. //$extrafields->fetch_name_optionals_label($object->table_element_line);
  81. $search_array_options = $extrafields->getOptionalsFromPost($object->table_element, '', 'search_');
  82. // Default sort order (if not yet defined by previous GETPOST)
  83. if (!$sortfield) {
  84. reset($object->fields); // Reset is required to avoid key() to return null.
  85. $sortfield = "p.".key($object->fields); // Set here default search field. By default 1st field in definition.
  86. }
  87. if (!$sortorder) {
  88. $sortorder = "ASC";
  89. }
  90. // Initialize array of search criterias
  91. $search = array();
  92. foreach ($object->fields as $key => $val) {
  93. if (GETPOST('search_'.$key, 'alpha') !== '') {
  94. $search[$key] = GETPOST('search_'.$key, 'alpha');
  95. }
  96. if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) {
  97. $search[$key.'_dtstart'] = dol_mktime(0, 0, 0, GETPOST('search_'.$key.'_dtstartmonth', 'int'), GETPOST('search_'.$key.'_dtstartday', 'int'), GETPOST('search_'.$key.'_dtstartyear', 'int'));
  98. $search[$key.'_dtend'] = dol_mktime(23, 59, 59, GETPOST('search_'.$key.'_dtendmonth', 'int'), GETPOST('search_'.$key.'_dtendday', 'int'), GETPOST('search_'.$key.'_dtendyear', 'int'));
  99. }
  100. }
  101. $key = 'sellby';
  102. $search[$key.'_dtstart'] = dol_mktime(0, 0, 0, GETPOST('search_'.$key.'_dtstartmonth', 'int'), GETPOST('search_'.$key.'_dtstartday', 'int'), GETPOST('search_'.$key.'_dtstartyear', 'int'));
  103. $search[$key.'_dtend'] = dol_mktime(23, 59, 59, GETPOST('search_'.$key.'_dtendmonth', 'int'), GETPOST('search_'.$key.'_dtendday', 'int'), GETPOST('search_'.$key.'_dtendyear', 'int'));
  104. $key = 'eatby';
  105. $search[$key.'_dtstart'] = dol_mktime(0, 0, 0, GETPOST('search_'.$key.'_dtstartmonth', 'int'), GETPOST('search_'.$key.'_dtstartday', 'int'), GETPOST('search_'.$key.'_dtstartyear', 'int'));
  106. $search[$key.'_dtend'] = dol_mktime(23, 59, 59, GETPOST('search_'.$key.'_dtendmonth', 'int'), GETPOST('search_'.$key.'_dtendday', 'int'), GETPOST('search_'.$key.'_dtendyear', 'int'));
  107. // Get object canvas (By default, this is not defined, so standard usage of dolibarr)
  108. $canvas = GETPOST("canvas");
  109. $objcanvas = null;
  110. if (!empty($canvas)) {
  111. require_once DOL_DOCUMENT_ROOT.'/core/class/canvas.class.php';
  112. $objcanvas = new Canvas($db, $action);
  113. $objcanvas->getCanvas('product', 'list', $canvas);
  114. }
  115. // Security check
  116. if ($user->socid) {
  117. $socid = $user->socid;
  118. }
  119. $result = restrictedArea($user, 'produit|service', 0, 'product&product');
  120. /*
  121. * Actions
  122. */
  123. if (GETPOST('cancel', 'alpha')) {
  124. $action = 'list';
  125. $massaction = '';
  126. }
  127. if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') {
  128. $massaction = '';
  129. }
  130. $parameters = array();
  131. $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
  132. if ($reshook < 0) {
  133. setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
  134. }
  135. if (empty($reshook)) {
  136. // Selection of new fields
  137. include DOL_DOCUMENT_ROOT.'/core/actions_changeselectedfields.inc.php';
  138. // Purge search criteria
  139. 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
  140. foreach ($object->fields as $key => $val) {
  141. $search[$key] = '';
  142. if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) {
  143. $search[$key.'_dtstart'] = '';
  144. $search[$key.'_dtend'] = '';
  145. }
  146. }
  147. $search['sellby_dtstart'] = '';
  148. $search['eatby_dtstart'] = '';
  149. $search['sellby_dtend'] = '';
  150. $search['eatby_dtend'] = '';
  151. $sref = "";
  152. $snom = "";
  153. $sall = "";
  154. $tosell = "";
  155. $tobuy = "";
  156. $search_sale = "";
  157. $search_categ = "";
  158. $search_toolowstock = '';
  159. $search_subjecttolotserial = '';
  160. $search_batch = '';
  161. $search_warehouse = '';
  162. $fourn_id = '';
  163. $sbarcode = '';
  164. $search_stock_physique = '';
  165. $toselect = array();
  166. $search_array_options = array();
  167. }
  168. if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')
  169. || GETPOST('button_search_x', 'alpha') || GETPOST('button_search.x', 'alpha') || GETPOST('button_search', 'alpha')) {
  170. $massaction = ''; // Protection to avoid mass action if we force a new search during a mass action confirmation
  171. }
  172. // Mass actions
  173. /*$objectclass = 'MyObject';
  174. $objectlabel = 'MyObject';
  175. $uploaddir = $conf->mymodule->dir_output;
  176. include DOL_DOCUMENT_ROOT.'/core/actions_massactions.inc.php';
  177. */
  178. }
  179. /*
  180. * View
  181. */
  182. $form = new Form($db);
  183. $htmlother = new FormOther($db);
  184. $now = dol_now();
  185. $helpurl = 'EN:Module_Stocks_En|FR:Module_Stock|ES:M&oacute;dulo_Stocks';
  186. $title = $langs->trans("ProductsAndServices");
  187. $morejs = array();
  188. $morecss = array();
  189. $sql = 'SELECT p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,';
  190. $sql .= ' p.fk_product_type, p.tms as datem,';
  191. $sql .= ' p.duration, p.tosell as statut, p.tobuy, p.seuil_stock_alerte, p.desiredstock, p.stock, p.tosell, p.tobuy, p.tobatch,';
  192. $sql .= ' ps.fk_entrepot, ps.reel,';
  193. $sql .= ' e.ref as warehouse_ref, e.lieu as warehouse_lieu, e.fk_parent as warehouse_parent,';
  194. $sql .= ' pb.batch, pb.eatby as oldeatby, pb.sellby as oldsellby,';
  195. $sql .= ' pl.rowid as lotid, pl.eatby, pl.sellby,';
  196. $sql .= ' SUM(pb.qty) as stock_physique, COUNT(pb.rowid) as nbinbatchtable';
  197. $sql .= ' FROM '.MAIN_DB_PREFIX.'product as p';
  198. $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as ps on p.rowid = ps.fk_product'; // Detail for each warehouse
  199. $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'entrepot as e on ps.fk_entrepot = e.rowid'; // Link on unique key
  200. $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_batch as pb on pb.fk_product_stock = ps.rowid'; // Detail for each lot on each warehouse
  201. $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
  202. $sql .= " WHERE p.entity IN (".getEntity('product').") AND e.entity IN (".getEntity('stock').")";
  203. if (!empty($search_categ) && $search_categ != '-1') {
  204. $sql .= " AND ";
  205. if ($search_categ == -2) {
  206. $sql .= " NOT EXISTS ";
  207. } else {
  208. $sql .= " EXISTS ";
  209. }
  210. $sql .= "(";
  211. $sql .= " SELECT cp.fk_categorie, cp.fk_product";
  212. $sql .= " FROM " . MAIN_DB_PREFIX . "categorie_product as cp";
  213. $sql .= " WHERE cp.fk_product = p.rowid"; // Join for the needed table to filter by categ
  214. if ($search_categ > 0) {
  215. $sql .= " AND cp.fk_categorie = " . ((int) $search_categ);
  216. }
  217. $sql .= ")";
  218. }
  219. if ($sall) {
  220. $sql .= natural_search(array('p.ref', 'p.label', 'p.description', 'p.note'), $sall);
  221. }
  222. // if the type is not 1, we show all products (type = 0,2,3)
  223. if (dol_strlen($type)) {
  224. if ($type == 1) {
  225. $sql .= " AND p.fk_product_type = '1'";
  226. } else {
  227. $sql .= " AND p.fk_product_type <> '1'";
  228. }
  229. }
  230. if ($search_subjecttolotserial) {
  231. $sql .= " AND p.tobatch > 0";
  232. }
  233. if ($sref) {
  234. $sql .= natural_search("p.ref", $sref);
  235. }
  236. if ($search_barcode) {
  237. $sql .= natural_search("p.barcode", $search_barcode);
  238. }
  239. if ($snom) {
  240. $sql .= natural_search("p.label", $snom);
  241. }
  242. if (!empty($tosell)) {
  243. $sql .= " AND p.tosell = ".((int) $tosell);
  244. }
  245. if (!empty($tobuy)) {
  246. $sql .= " AND p.tobuy = ".((int) $tobuy);
  247. }
  248. if (!empty($canvas)) {
  249. $sql .= " AND p.canvas = '".$db->escape($canvas)."'";
  250. }
  251. if ($fourn_id > 0) {
  252. $sql .= " AND p.rowid = pf.fk_product AND pf.fk_soc = ".((int) $fourn_id);
  253. }
  254. if ($search_warehouse) {
  255. $sql .= natural_search("e.ref", $search_warehouse);
  256. }
  257. if ($search_batch) {
  258. $sql .= natural_search("pb.batch", $search_batch);
  259. }
  260. foreach ($search as $key => $val) {
  261. if (array_key_exists($key, $object->fields)) {
  262. if ($key == 'status' && $search[$key] == -1) {
  263. continue;
  264. }
  265. $mode_search = (($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key])) ? 1 : 0);
  266. if ((strpos($object->fields[$key]['type'], 'integer:') === 0) || (strpos($object->fields[$key]['type'], 'sellist:') === 0) || !empty($object->fields[$key]['arrayofkeyval'])) {
  267. if ($search[$key] == '-1' || ($search[$key] === '0' && (empty($object->fields[$key]['arrayofkeyval']) || !array_key_exists('0', $object->fields[$key]['arrayofkeyval'])))) {
  268. $search[$key] = '';
  269. }
  270. $mode_search = 2;
  271. }
  272. if ($search[$key] != '') {
  273. $sql .= natural_search("t.".$db->escape($key), $search[$key], (($key == 'status') ? 2 : $mode_search));
  274. }
  275. } else {
  276. if (preg_match('/(_dtstart|_dtend)$/', $key) && $search[$key] != '') {
  277. $columnName = preg_replace('/(_dtstart|_dtend)$/', '', $key);
  278. if ($columnName == 'eatby' || $columnName == 'sellby') {
  279. if (preg_match('/_dtstart$/', $key)) {
  280. $sql .= " AND pl.".$db->escape($columnName)." >= '".$db->idate($search[$key])."'";
  281. }
  282. if (preg_match('/_dtend$/', $key)) {
  283. $sql .= " AND pl.".$db->escape($columnName)." <= '".$db->idate($search[$key])."'";
  284. }
  285. }
  286. }
  287. }
  288. }
  289. $sql .= " GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,";
  290. $sql .= " p.fk_product_type, p.tms,";
  291. $sql .= " p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte, p.desiredstock, p.stock, p.tosell, p.tobuy, p.tobatch,";
  292. $sql .= " ps.fk_entrepot, ps.reel,";
  293. $sql .= " e.ref, e.lieu, e.fk_parent,";
  294. $sql .= " pb.batch, pb.eatby, pb.sellby,";
  295. $sql .= " pl.rowid, pl.eatby, pl.sellby";
  296. $sql_having = '';
  297. if ($search_toolowstock) {
  298. $sql_having .= " HAVING SUM(".$db->ifsql('ps.reel IS NULL', '0', 'ps.reel').") < p.seuil_stock_alerte"; // Not used yet
  299. }
  300. if ($search_stock_physique != '') {
  301. $natural_search_physique = natural_search('SUM(' . $db->ifsql('pb.qty IS NULL', $db->ifsql('ps.reel IS NULL', '0', 'ps.reel'), 'pb.qty') . ')', $search_stock_physique, 1, 1);
  302. $natural_search_physique = " " . substr($natural_search_physique, 1, -1); // remove first "(" and last ")" characters
  303. if (!empty($sql_having)) {
  304. $sql_having .= " AND";
  305. } else {
  306. $sql_having .= " HAVING";
  307. }
  308. $sql_having .= $natural_search_physique;
  309. }
  310. if (!empty($sql_having)) {
  311. $sql .= $sql_having;
  312. }
  313. //print $sql;
  314. // Count total nb of records
  315. $nbtotalofrecords = '';
  316. if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) {
  317. $resql = $db->query($sql);
  318. $nbtotalofrecords = $db->num_rows($resql);
  319. if (($page * $limit) > $nbtotalofrecords) { // if total of record found is smaller than page * limit, goto and load page 0
  320. $page = 0;
  321. $offset = 0;
  322. }
  323. $db->free($resql);
  324. }
  325. // Complete request and execute it with limit
  326. $sql .= $db->order($sortfield, $sortorder);
  327. if ($limit) {
  328. $sql .= $db->plimit($limit + 1, $offset);
  329. }
  330. $resql = $db->query($sql);
  331. if (!$resql) {
  332. dol_print_error($db);
  333. exit;
  334. }
  335. $num = $db->num_rows($resql);
  336. $i = 0;
  337. if ($num == 1 && GETPOST('autojumpifoneonly') && ($sall or $snom or $sref)) {
  338. $objp = $db->fetch_object($resql);
  339. header("Location: card.php?id=$objp->rowid");
  340. exit;
  341. }
  342. if (isset($type)) {
  343. if ($type == 1) {
  344. $texte = $langs->trans("Services");
  345. } else {
  346. $texte = $langs->trans("Products");
  347. }
  348. } else {
  349. $texte = $langs->trans("ProductsAndServices");
  350. }
  351. $texte .= ' ('.$langs->trans("StocksByLotSerial").')';
  352. $param = '';
  353. if (!empty($mode)) {
  354. $param .= '&mode='.urlencode($mode);
  355. }
  356. if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) {
  357. $param .= '&contextpage='.urlencode($contextpage);
  358. }
  359. if ($limit > 0 && $limit != $conf->liste_limit) {
  360. $param .= '&limit='.urlencode($limit);
  361. }
  362. foreach ($search as $key => $val) {
  363. if (is_array($search[$key]) && count($search[$key])) {
  364. foreach ($search[$key] as $skey) {
  365. if ($skey != '') {
  366. $param .= '&search_'.$key.'[]='.urlencode($skey);
  367. }
  368. }
  369. } elseif ($search[$key] != '') {
  370. $param .= '&search_'.$key.'='.urlencode($search[$key]);
  371. }
  372. }
  373. if ($optioncss != '') {
  374. $param .= '&optioncss='.urlencode($optioncss);
  375. }
  376. if ($sall) {
  377. $param .= "&sall=".urlencode($sall);
  378. }
  379. if ($tosell) {
  380. $param .= "&tosell=".urlencode($tosell);
  381. }
  382. if ($tobuy) {
  383. $param .= "&tobuy=".urlencode($tobuy);
  384. }
  385. if ($type != '') {
  386. $param .= "&type=".urlencode($type);
  387. }
  388. if ($fourn_id) {
  389. $param .= "&fourn_id=".urlencode($fourn_id);
  390. }
  391. if ($snom) {
  392. $param .= "&snom=".urlencode($snom);
  393. }
  394. if ($sref) {
  395. $param .= "&sref=".urlencode($sref);
  396. }
  397. if ($search_batch) {
  398. $param .= "&search_batch=".urlencode($search_batch);
  399. }
  400. if ($sbarcode) {
  401. $param .= "&sbarcode=".urlencode($sbarcode);
  402. }
  403. if ($search_warehouse) {
  404. $param .= "&search_warehouse=".urlencode($search_warehouse);
  405. }
  406. if ($search_toolowstock) {
  407. $param .= "&search_toolowstock=".urlencode($search_toolowstock);
  408. }
  409. if ($search_subjecttolotserial) {
  410. $param .= "&search_subjecttolotserial=".urlencode($search_subjecttolotserial);
  411. }
  412. if ($search_sale) {
  413. $param .= "&search_sale=".urlencode($search_sale);
  414. }
  415. if (!empty($search_categ) && $search_categ != '-1') {
  416. $param .= "&search_categ=".urlencode($search_categ);
  417. }
  418. if ($search_stock_physique) {
  419. $param .= '&search_stock_physique=' . urlencode($search_stock_physique);
  420. }
  421. /*if ($eatby) $param.="&eatby=".$eatby;
  422. if ($sellby) $param.="&sellby=".$sellby;*/
  423. llxHeader("", $title, $helpurl, $texte);
  424. print '<form id="searchFormList" action="'.$_SERVER["PHP_SELF"].'" method="POST" name="formulaire">'."\n";
  425. if ($optioncss != '') {
  426. print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
  427. }
  428. print '<input type="hidden" name="token" value="'.newToken().'">';
  429. print '<input type="hidden" name="action" value="list">';
  430. print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
  431. print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
  432. print '<input type="hidden" name="type" value="'.$type.'">';
  433. print '<input type="hidden" name="page" value="'.$page.'">';
  434. print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
  435. print '<input type="hidden" name="mode" value="'.$mode.'">';
  436. print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, $nbtotalofrecords, 'product', 0, '', '', $limit, 0, 0, 1);
  437. if ($search_categ > 0) {
  438. print "<div id='ways'>";
  439. $c = new Categorie($db);
  440. $c->fetch($search_categ);
  441. $ways = $c->print_all_ways(' &gt; ', 'product/reassortlot.php');
  442. print " &gt; ".$ways[0]."<br>\n";
  443. print "</div><br>";
  444. }
  445. // Filter on categories
  446. $moreforfilter = '';
  447. if (isModEnabled('categorie')) {
  448. $moreforfilter .= '<div class="divsearchfield">';
  449. $moreforfilter .= img_picto($langs->trans('Categories'), 'category', 'class="pictofixedwidth"');
  450. $moreforfilter .= $htmlother->select_categories(Categorie::TYPE_PRODUCT, $search_categ, 'search_categ', 1);
  451. $moreforfilter .= '</div>';
  452. }
  453. $moreforfilter.='<label for="search_subjecttolotserial">'.$langs->trans("SubjectToLotSerialOnly").' </label><input type="checkbox" id="search_subjecttolotserial" name="search_subjecttolotserial" value="1"'.($search_subjecttolotserial?' checked':'').'>';
  454. if (!empty($moreforfilter)) {
  455. print '<div class="liste_titre liste_titre_bydiv centpercent">';
  456. print $moreforfilter;
  457. $parameters = array();
  458. $reshook = $hookmanager->executeHooks('printFieldPreListTitle', $parameters); // Note that $action and $object may have been modified by hook
  459. print $hookmanager->resPrint;
  460. print '</div>';
  461. }
  462. print '<div class="div-table-responsive">';
  463. print '<table class="tagtable nobottomiftotal liste'.($moreforfilter ? " listwithfilterbefore" : "").'">';
  464. // Fields title search
  465. // --------------------------------------------------------------------
  466. print '<tr class="liste_titre_filter">';
  467. // Action column
  468. if (!empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) {
  469. print '<td class="liste_titre maxwidthsearch">';
  470. $searchpicto = $form->showFilterButtons();
  471. print $searchpicto;
  472. print '</td>';
  473. }
  474. print '<td class="liste_titre">';
  475. print '<input class="flat" type="text" name="sref" size="6" value="'.$sref.'">';
  476. print '</td>';
  477. print '<td class="liste_titre">';
  478. print '<input class="flat" type="text" name="snom" size="8" value="'.$snom.'">';
  479. print '</td>';
  480. if (isModEnabled("service") && $type == 1) {
  481. print '<td class="liste_titre">';
  482. print '&nbsp;';
  483. print '</td>';
  484. }
  485. print '<td class="liste_titre"><input class="flat" type="text" name="search_warehouse" size="6" value="'.$search_warehouse.'"></td>';
  486. print '<td class="liste_titre center"><input class="flat" type="text" name="search_batch" size="6" value="'.$search_batch.'"></td>';
  487. if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
  488. print '<td class="liste_titre center">';
  489. $key = 'sellby';
  490. print '<div class="nowrap">';
  491. print $form->selectDate($search[$key.'_dtstart'] ? $search[$key.'_dtstart'] : '', "search_".$key."_dtstart", 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('From'));
  492. print '</div>';
  493. print '<div class="nowrap">';
  494. print $form->selectDate($search[$key.'_dtend'] ? $search[$key.'_dtend'] : '', "search_".$key."_dtend", 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('to'));
  495. print '</div>';
  496. print '</td>';
  497. }
  498. if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
  499. print '<td class="liste_titre center">';
  500. $key = 'eatby';
  501. print '<div class="nowrap">';
  502. print $form->selectDate($search[$key.'_dtstart'] ? $search[$key.'_dtstart'] : '', "search_".$key."_dtstart", 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('From'));
  503. print '</div>';
  504. print '<div class="nowrap">';
  505. print $form->selectDate($search[$key.'_dtend'] ? $search[$key.'_dtend'] : '', "search_".$key."_dtend", 0, 0, 1, '', 1, 0, 0, '', '', '', '', 1, '', $langs->trans('to'));
  506. print '</div>';
  507. print '</td>';
  508. }
  509. // Physical stock
  510. print '<td class="liste_titre right">';
  511. print '<input class="flat" type="text" size="5" name="search_stock_physique" value="'.dol_escape_htmltag($search_stock_physique).'">';
  512. print '</td>';
  513. print '<td class="liste_titre">&nbsp;</td>';
  514. print '<td class="liste_titre">&nbsp;</td>';
  515. print '<td class="liste_titre">&nbsp;</td>';
  516. // Action column
  517. if (empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) {
  518. print '<td class="liste_titre maxwidthsearch">';
  519. $searchpicto = $form->showFilterButtons();
  520. print $searchpicto;
  521. print '</td>';
  522. }
  523. print '</tr>'."\n";
  524. $totalarray = array();
  525. $totalarray['nbfield'] = 0;
  526. // Fields title label
  527. // --------------------------------------------------------------------
  528. print '<tr class="liste_titre">';
  529. // Action column
  530. if (!empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) {
  531. print_liste_field_titre('');
  532. }
  533. print_liste_field_titre("Ref", $_SERVER["PHP_SELF"], "p.ref", '', $param, "", $sortfield, $sortorder);
  534. print_liste_field_titre("Label", $_SERVER["PHP_SELF"], "p.label", '', $param, "", $sortfield, $sortorder);
  535. if (isModEnabled("service") && $type == 1) {
  536. print_liste_field_titre("Duration", $_SERVER["PHP_SELF"], "p.duration", '', $param, "", $sortfield, $sortorder, 'center ');
  537. }
  538. print_liste_field_titre("Warehouse", $_SERVER["PHP_SELF"], "e.ref", '', $param, "", $sortfield, $sortorder);
  539. //print_liste_field_titre("DesiredStock", $_SERVER["PHP_SELF"], "p.desiredstock",$param,"",'',$sortfield,$sortorder, 'right );
  540. print_liste_field_titre("Batch", $_SERVER["PHP_SELF"], "pb.batch", '', $param, "", $sortfield, $sortorder, 'center ');
  541. if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
  542. print_liste_field_titre("SellByDate", $_SERVER["PHP_SELF"], "pl.sellby", '', $param, "", $sortfield, $sortorder, 'center ');
  543. }
  544. if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
  545. print_liste_field_titre("EatByDate", $_SERVER["PHP_SELF"], "pl.eatby", '', $param, "", $sortfield, $sortorder, 'center ');
  546. }
  547. print_liste_field_titre("PhysicalStock", $_SERVER["PHP_SELF"], "stock_physique", '', $param, "", $sortfield, $sortorder, 'right ');
  548. // TODO Add info of running suppliers/customers orders
  549. //print_liste_field_titre("TheoreticalStock",$_SERVER["PHP_SELF"], "stock_theorique",$param,"",'',$sortfield,$sortorder, 'right ');
  550. print_liste_field_titre('');
  551. print_liste_field_titre("ProductStatusOnSell", $_SERVER["PHP_SELF"], "p.tosell", "", $param, '', $sortfield, $sortorder, 'right ');
  552. print_liste_field_titre("ProductStatusOnBuy", $_SERVER["PHP_SELF"], "p.tobuy", "", $param, '', $sortfield, $sortorder, 'right ');
  553. if (empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) {
  554. print_liste_field_titre('');
  555. }
  556. print "</tr>\n";
  557. $product_static = new Product($db);
  558. $product_lot_static = new Productlot($db);
  559. $warehousetmp = new Entrepot($db);
  560. // Loop on record
  561. // --------------------------------------------------------------------
  562. $i = 0;
  563. $savnbfield = $totalarray['nbfield'];
  564. $totalarray['nbfield'] = 0;
  565. $imaxinloop = ($limit ? min($num, $limit) : $num);
  566. while ($i < $imaxinloop) {
  567. $objp = $db->fetch_object($resql);
  568. // Multilangs
  569. if (getDolGlobalInt('MAIN_MULTILANGS')) { // si l'option est active
  570. // TODO Use a cache
  571. $sql = "SELECT label";
  572. $sql .= " FROM ".MAIN_DB_PREFIX."product_lang";
  573. $sql .= " WHERE fk_product = ".((int) $objp->rowid);
  574. $sql .= " AND lang = '".$db->escape($langs->getDefaultLang())."'";
  575. $sql .= " LIMIT 1";
  576. $result = $db->query($sql);
  577. if ($result) {
  578. $objtp = $db->fetch_object($result);
  579. if (!empty($objtp->label)) {
  580. $objp->label = $objtp->label;
  581. }
  582. }
  583. }
  584. $product_static->ref = $objp->ref;
  585. $product_static->id = $objp->rowid;
  586. $product_static->label = $objp->label;
  587. $product_static->type = $objp->fk_product_type;
  588. $product_static->entity = $objp->entity;
  589. $product_static->status = $objp->tosell;
  590. $product_static->status_buy = $objp->tobuy;
  591. $product_static->status_batch = $objp->tobatch;
  592. $product_lot_static->batch = $objp->batch;
  593. $product_lot_static->fk_product = $objp->rowid;
  594. $product_lot_static->id = $objp->lotid;
  595. $product_lot_static->eatby = $objp->eatby;
  596. $product_lot_static->sellby = $objp->sellby;
  597. $warehousetmp->id = $objp->fk_entrepot;
  598. $warehousetmp->ref = $objp->warehouse_ref;
  599. $warehousetmp->label = $objp->warehouse_ref;
  600. $warehousetmp->fk_parent = $objp->warehouse_parent;
  601. print '<tr>';
  602. // Action column
  603. if (!empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) {
  604. print '<td></td>';
  605. }
  606. // Ref
  607. print '<td class="nowrap">';
  608. print $product_static->getNomUrl(1, '', 16);
  609. //if ($objp->stock_theorique < $objp->seuil_stock_alerte) print ' '.img_warning($langs->trans("StockTooLow"));
  610. print '</td>';
  611. // Label
  612. print '<td>'.$objp->label.'</td>';
  613. if (isModEnabled("service") && $type == 1) {
  614. print '<td class="center">';
  615. $regs = array();
  616. if (preg_match('/([0-9]+)y/i', $objp->duration, $regs)) {
  617. print $regs[1].' '.$langs->trans("DurationYear");
  618. } elseif (preg_match('/([0-9]+)m/i', $objp->duration, $regs)) {
  619. print $regs[1].' '.$langs->trans("DurationMonth");
  620. } elseif (preg_match('/([0-9]+)d/i', $objp->duration, $regs)) {
  621. print $regs[1].' '.$langs->trans("DurationDay");
  622. } else {
  623. print $objp->duration;
  624. }
  625. print '</td>';
  626. }
  627. //print '<td class="right">'.$objp->stock_theorique.'</td>';
  628. //print '<td class="right">'.$objp->seuil_stock_alerte.'</td>';
  629. //print '<td class="right">'.$objp->desiredstock.'</td>';
  630. // Warehouse
  631. print '<td class="nowrap">';
  632. if ($objp->fk_entrepot > 0) {
  633. print $warehousetmp->getNomUrl(1);
  634. }
  635. print '</td>';
  636. // Lot
  637. print '<td class="center nowrap">';
  638. if ($product_lot_static->batch) {
  639. print $product_lot_static->getNomUrl(1);
  640. }
  641. print '</td>';
  642. if (empty($conf->global->PRODUCT_DISABLE_SELLBY)) {
  643. print '<td class="center">'.dol_print_date($db->jdate($objp->sellby), 'day').'</td>';
  644. }
  645. if (empty($conf->global->PRODUCT_DISABLE_EATBY)) {
  646. print '<td class="center">'.dol_print_date($db->jdate($objp->eatby), 'day').'</td>';
  647. }
  648. print '<td class="right">';
  649. //if ($objp->seuil_stock_alerte && ($objp->stock_physique < $objp->seuil_stock_alerte)) print img_warning($langs->trans("StockTooLow")).' ';
  650. if (is_null($objp->stock_physique)) {
  651. if (!empty($objp->reel)) {
  652. print price2num($objp->reel, 'MS');
  653. }
  654. } else {
  655. if (!empty($objp->stock_physique)) {
  656. print price2num($objp->stock_physique, 'MS');
  657. }
  658. }
  659. print '</td>';
  660. print '<td class="right">';
  661. print img_picto($langs->trans("StockMovement"), 'movement', 'class="pictofixedwidth"');
  662. 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>';
  663. print '</td>';
  664. print '<td class="right nowrap">'.$product_static->LibStatut($objp->statut, 5, 0).'</td>';
  665. print '<td class="right nowrap">'.$product_static->LibStatut($objp->tobuy, 5, 1).'</td>';
  666. // Action column
  667. if (empty($conf->global->MAIN_CHECKBOX_LEFT_COLUMN)) {
  668. print '<td></td>';
  669. }
  670. print "</tr>\n";
  671. $i++;
  672. }
  673. $db->free($resql);
  674. print '</table>'."\n";
  675. print '</div>'."\n";
  676. print '</form>'."\n";
  677. // End of page
  678. llxFooter();
  679. $db->close();