reassortlot.php 34 KB

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