29 require
'../main.inc.php';
30 require_once DOL_DOCUMENT_ROOT.
'/product/class/product.class.php';
31 require_once DOL_DOCUMENT_ROOT.
'/core/class/html.formother.class.php';
32 require_once DOL_DOCUMENT_ROOT.
'/categories/class/categorie.class.php';
33 require_once DOL_DOCUMENT_ROOT.
'/product/class/html.formproduct.class.php';
36 $langs->loadLangs(array(
'products',
'stocks'));
38 $action =
GETPOST(
'action',
'aZ09');
39 $sref =
GETPOST(
"sref",
'alpha');
40 $snom =
GETPOST(
"snom",
'alpha');
41 $sall = trim((
GETPOST(
'search_all',
'alphanohtml') !=
'') ?
GETPOST(
'search_all',
'alphanohtml') :
GETPOST(
'sall',
'alphanohtml'));
43 $search_barcode =
GETPOST(
"search_barcode",
'alpha');
44 $toolowstock =
GETPOST(
'toolowstock');
47 $fourn_id =
GETPOST(
"fourn_id",
'int');
48 $sbarcode =
GETPOST(
"sbarcode",
'int');
49 $search_stock_physique =
GETPOST(
'search_stock_physique',
'alpha');
51 $sortfield =
GETPOST(
'sortfield',
'aZ09comma');
52 $sortorder =
GETPOST(
'sortorder',
'aZ09comma');
54 if (empty($page) || $page < 0) {
63 $limit =
GETPOST(
'limit',
'int') ?
GETPOST(
'limit',
'int') : $conf->liste_limit;
64 if (empty($page) || $page == -1) {
67 $offset = $limit * $page;
70 $search_sale =
GETPOST(
"search_sale");
72 $search_categ =
GETPOST(
'catid',
'int');
74 $search_categ =
GETPOST(
'search_categ',
'int');
80 if (!empty($canvas)) {
81 require_once DOL_DOCUMENT_ROOT.
'/core/class/canvas.class.php';
82 $objcanvas =
new Canvas($db, $action);
83 $objcanvas->getCanvas(
'product',
'list', $canvas);
87 $virtualdiffersfromphysical = 0;
88 if (!empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT)
89 || !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)
90 || !empty($conf->global->STOCK_CALCULATE_ON_SHIPMENT_CLOSE)
91 || !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION)
92 || !empty($conf->global->STOCK_CALCULATE_ON_RECEPTION_CLOSE)
93 || !empty($conf->mrp->enabled)) {
94 $virtualdiffersfromphysical = 1;
98 $hookmanager->initHooks(array(
'productreassortlist'));
101 $socid = $user->socid;
103 $result =
restrictedArea($user,
'produit|service', 0,
'product&product');
111 if (
GETPOST(
'button_removefilter_x',
'alpha') ||
GETPOST(
'button_removefilter.x',
'alpha') ||
GETPOST(
'button_removefilter',
'alpha')) {
122 $search_stock_physique =
'';
131 $helpurl =
'EN:Module_Stocks_En|FR:Module_Stock|ES:Módulo_Stocks';
136 $sql =
'SELECT p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,';
137 $sql .=
' p.fk_product_type, p.tms as datem,';
138 $sql .=
' p.duration, p.tosell as statut, p.tobuy, p.seuil_stock_alerte, p.desiredstock,';
139 $sql .=
' SUM(s.reel) as stock_physique';
140 if (!empty($conf->global->PRODUCT_USE_UNITS)) {
141 $sql .=
', u.short_label as unit_short';
144 $parameters = array();
145 $reshook = $hookmanager->executeHooks(
'printFieldListSelect', $parameters, $object);
146 $sql .= $hookmanager->resPrint;
147 $sql .=
' FROM '.MAIN_DB_PREFIX.
'product as p';
148 $sql .=
' LEFT JOIN '.MAIN_DB_PREFIX.
'product_stock as s ON p.rowid = s.fk_product';
149 if (!empty($conf->global->PRODUCT_USE_UNITS)) {
150 $sql .=
' LEFT JOIN '.MAIN_DB_PREFIX.
'c_units as u on p.fk_unit = u.rowid';
152 $sql .=
" WHERE p.entity IN (".getEntity(
'product').
")";
153 if (!empty($search_categ) && $search_categ !=
'-1') {
155 if ($search_categ == -2) {
156 $sql .=
" NOT EXISTS ";
161 $sql .=
" SELECT cp.fk_categorie, cp.fk_product";
162 $sql .=
" FROM " . MAIN_DB_PREFIX .
"categorie_product as cp";
163 $sql .=
" WHERE cp.fk_product = p.rowid";
164 if ($search_categ > 0) {
165 $sql .=
" AND cp.fk_categorie = " . ((int) $search_categ);
169 $sql .=
" AND EXISTS (SELECT e.rowid FROM ".MAIN_DB_PREFIX.
"entrepot as e WHERE e.rowid = s.fk_entrepot AND e.entity IN (".
getEntity(
'stock').
"))";
171 $sql .=
natural_search(array(
'p.ref',
'p.label',
'p.description',
'p.note'), $sall);
176 $sql .=
" AND p.fk_product_type = '1'";
178 $sql .=
" AND p.fk_product_type <> '1'";
184 if ($search_barcode) {
190 if (!empty($tosell)) {
191 $sql .=
" AND p.tosell = ".((int) $tosell);
193 if (!empty($tobuy)) {
194 $sql .=
" AND p.tobuy = ".((int) $tobuy);
196 if (!empty($canvas)) {
197 $sql .=
" AND p.canvas = '".$db->escape($canvas).
"'";
200 $sql .=
" AND p.rowid = pf.fk_product AND pf.fk_soc = ".((int) $fourn_id);
203 $parameters = array();
204 $reshook = $hookmanager->executeHooks(
'printFieldListWhere', $parameters, $object);
205 $sql .= $hookmanager->resPrint;
206 $sql .=
" GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type, p.entity,";
207 $sql .=
" p.fk_product_type, p.tms, p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte, p.desiredstock";
209 $parameters = array();
210 $reshook = $hookmanager->executeHooks(
'printFieldSelect', $parameters, $object);
211 $sql .= $hookmanager->resPrint;
214 $sql_having .=
" HAVING SUM(".$db->ifsql(
's.reel IS NULL',
'0',
's.reel').
") < p.seuil_stock_alerte";
216 if ($search_stock_physique !=
'') {
218 $natural_search_physique =
natural_search(
'SUM(' . $db->ifsql(
's.reel IS NULL',
'0',
's.reel') .
')', $search_stock_physique, 1, 1);
219 $natural_search_physique =
" " . substr($natural_search_physique, 1, -1);
220 if (!empty($sql_having)) {
221 $sql_having .=
" AND";
223 $sql_having .=
" HAVING";
225 $sql_having .= $natural_search_physique;
227 if (!empty($sql_having)) {
230 $sql .= $db->order($sortfield, $sortorder);
233 $nbtotalofrecords =
'';
234 if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) {
235 $result = $db->query($sql);
236 $nbtotalofrecords = $db->num_rows($result);
237 if (($page * $limit) > $nbtotalofrecords) {
243 $sql .= $db->plimit($limit + 1, $offset);
245 $resql = $db->query($sql);
247 $num = $db->num_rows(
$resql);
251 if ($num == 1 &&
GETPOST(
'autojumpifoneonly') && ($sall || $snom || $sref)) {
252 $objp = $db->fetch_object(
$resql);
253 header(
"Location: card.php?id=$objp->rowid");
259 $texte = $langs->trans(
"Services");
261 $texte = $langs->trans(
"Products");
264 $texte = $langs->trans(
"ProductsAndServices");
266 $texte .=
' ('.$langs->trans(
"MenuStocks").
')';
269 if ($limit > 0 && $limit != $conf->liste_limit) {
270 $param .=
'&limit='.urlencode($limit);
273 $param .=
"&sall=".urlencode($sall);
276 $param .=
"&tosell=".urlencode($tosell);
279 $param .=
"&tobuy=".urlencode($tobuy);
282 $param .=
"&type=".urlencode($type);
285 $param .=
"&fourn_id=".urlencode($fourn_id);
288 $param .=
"&snom=".urlencode($snom);
291 $param .=
"&sref=".urlencode($sref);
294 $param .=
"&search_sale=".urlencode($search_sale);
296 if ($search_categ > 0) {
297 $param .=
"&search_categ=".urlencode($search_categ);
300 $param .=
"&toolowstock=".urlencode($toolowstock);
303 $param .=
"&sbarcode=".urlencode($sbarcode);
305 if ($search_stock_physique) {
306 $param .=
'&search_stock_physique=' . urlencode($search_stock_physique);
311 print
'<form action="'.$_SERVER[
"PHP_SELF"].
'" method="post" name="formulaire">';
312 print
'<input type="hidden" name="token" value="'.newToken().
'">';
313 print
'<input type="hidden" name="sortfield" value="'.$sortfield.
'">';
314 print
'<input type="hidden" name="sortorder" value="'.$sortorder.
'">';
315 print
'<input type="hidden" name="page" value="'.$page.
'">';
316 print
'<input type="hidden" name="type" value="'.$type.
'">';
318 print_barre_liste($texte, $page, $_SERVER[
"PHP_SELF"], $param, $sortfield, $sortorder,
'', $num, $nbtotalofrecords,
'product', 0,
'',
'', $limit);
320 if ($search_categ > 0) {
321 print
"<div id='ways'>";
323 $c->fetch($search_categ);
324 $ways = $c->print_all_ways(
' > ',
'product/reassort.php');
325 print
" > ".$ways[0].
"<br>\n";
331 if (!empty($conf->categorie->enabled)) {
332 $moreforfilter .=
'<div class="divsearchfield">';
333 $moreforfilter .=
img_picto($langs->trans(
'Categories'),
'category',
'class="pictofixedwidth"');
334 $moreforfilter .= $htmlother->select_categories(Categorie::TYPE_PRODUCT, $search_categ,
'search_categ', 1);
335 $moreforfilter .=
'</div>';
338 $moreforfilter .=
'<div class="divsearchfield">';
339 $moreforfilter .= $langs->trans(
"StockTooLow").
' <input type="checkbox" name="toolowstock" value="1"'.($toolowstock ?
' checked' :
'').
'>';
340 $moreforfilter .=
'</div>';
342 if (!empty($moreforfilter)) {
343 print
'<div class="liste_titre liste_titre_bydiv centpercent">';
344 print $moreforfilter;
345 $parameters = array();
346 $reshook = $hookmanager->executeHooks(
'printFieldPreListTitle', $parameters);
347 print $hookmanager->resPrint;
352 $formProduct->loadWarehouses();
353 $warehouses_list = $formProduct->cache_warehouses;
354 $nb_warehouse = count($warehouses_list);
355 $colspan_warehouse = 1;
356 if (!empty($conf->global->STOCK_DETAIL_ON_WAREHOUSE)) {
357 $colspan_warehouse = $nb_warehouse > 1 ? $nb_warehouse + 1 : 1;
360 print
'<div class="div-table-responsive">';
361 print
'<table class="tagtable liste'.($moreforfilter ?
" listwithfilterbefore" :
"").
'">';
364 print
'<tr class="liste_titre_filter">';
365 print
'<td class="liste_titre">';
366 print
'<input class="flat" type="text" name="sref" size="6" value="'.$sref.
'">';
368 print
'<td class="liste_titre">';
369 print
'<input class="flat" type="text" name="snom" size="8" value="'.$snom.
'">';
372 if (!empty($conf->service->enabled) && $type == 1) {
373 print
'<td class="liste_titre">';
378 print
'<td class="liste_titre"> </td>';
379 print
'<td class="liste_titre right"> </td>';
381 print
'<td class="liste_titre right">';
382 print
'<input class="flat" type="text" size="5" name="search_stock_physique" value="'.dol_escape_htmltag($search_stock_physique).
'">';
384 if ($virtualdiffersfromphysical) {
385 print
'<td class="liste_titre"> </td>';
387 print
'<td class="liste_titre"> </td>';
388 print
'<td class="liste_titre" colspan="'.$colspan_warehouse.
'"> </td>';
389 print
'<td class="liste_titre"></td>';
390 $parameters = array();
391 $reshook = $hookmanager->executeHooks(
'printFieldListOption', $parameters);
392 print $hookmanager->resPrint;
393 print
'<td class="liste_titre maxwidthsearch">';
394 $searchpicto =
$form->showFilterAndCheckAddButtons(0);
400 print
"<tr class=\"liste_titre\">";
403 if (!empty($conf->service->enabled) && $type == 1) {
404 print_liste_field_titre(
"Duration", $_SERVER[
"PHP_SELF"],
"p.duration",
'', $param,
"", $sortfield, $sortorder,
'center ');
406 print_liste_field_titre(
"StockLimit", $_SERVER[
"PHP_SELF"],
"p.seuil_stock_alerte",
'', $param,
"", $sortfield, $sortorder,
'right ');
407 print_liste_field_titre(
"DesiredStock", $_SERVER[
"PHP_SELF"],
"p.desiredstock",
'', $param,
"", $sortfield, $sortorder,
'right ');
408 print_liste_field_titre(
"PhysicalStock", $_SERVER[
"PHP_SELF"],
"stock_physique",
'', $param,
"", $sortfield, $sortorder,
'right ');
410 if (!empty($conf->global->STOCK_DETAIL_ON_WAREHOUSE)) {
411 if ($nb_warehouse > 1) {
412 foreach ($warehouses_list as &$wh) {
417 if ($virtualdiffersfromphysical) {
418 print_liste_field_titre(
"VirtualStock", $_SERVER[
"PHP_SELF"],
"",
'', $param,
"", $sortfield, $sortorder,
'right ',
'VirtualStockDesc');
421 if (!empty($conf->global->PRODUCT_USE_UNITS)) {
422 print_liste_field_titre(
"Unit", $_SERVER[
"PHP_SELF"],
"unit_short",
'', $param,
'align="right"', $sortfield, $sortorder);
425 print_liste_field_titre(
"ProductStatusOnSell", $_SERVER[
"PHP_SELF"],
"p.tosell",
'', $param,
"", $sortfield, $sortorder,
'right ');
426 print_liste_field_titre(
"ProductStatusOnBuy", $_SERVER[
"PHP_SELF"],
"p.tobuy",
'', $param,
"", $sortfield, $sortorder,
'right ');
428 $parameters = array(
'param'=>$param,
'sortfield'=>$sortfield,
'sortorder'=>$sortorder);
429 $reshook = $hookmanager->executeHooks(
'printFieldListTitle', $parameters);
430 print $hookmanager->resPrint;
434 while ($i < min($num, $limit)) {
435 $objp = $db->fetch_object(
$resql);
438 $product->fetch($objp->rowid);
439 $product->load_stock();
442 print
'<td class="nowrap">';
443 print $product->getNomUrl(1,
'', 16);
446 print
'<td>'.$product->label.
'</td>';
448 if (!empty($conf->service->enabled) && $type == 1) {
449 print
'<td class="center">';
450 if (preg_match(
'/([0-9]+)y/i', $objp->duration, $regs)) {
451 print $regs[1].
' '.$langs->trans(
"DurationYear");
452 } elseif (preg_match(
'/([0-9]+)m/i', $objp->duration, $regs)) {
453 print $regs[1].
' '.$langs->trans(
"DurationMonth");
454 } elseif (preg_match(
'/([0-9]+)d/i', $objp->duration, $regs)) {
455 print $regs[1].
' '.$langs->trans(
"DurationDay");
457 print $objp->duration;
462 print
'<td class="right">'.$objp->seuil_stock_alerte.
'</td>';
463 print
'<td class="right">'.$objp->desiredstock.
'</td>';
465 print
'<td class="right">';
466 if ($objp->seuil_stock_alerte !=
'' && ($objp->stock_physique < $objp->seuil_stock_alerte)) {
467 print
img_warning($langs->trans(
"StockTooLow")).
' ';
469 print
price2num($objp->stock_physique,
'MS');
473 if (!empty($conf->global->STOCK_DETAIL_ON_WAREHOUSE)) {
474 if ($nb_warehouse > 1) {
475 foreach ($warehouses_list as &$wh) {
476 print
'<td class="right">';
477 print empty($product->stock_warehouse[$wh[
'id']]->real) ?
'0' : $product->stock_warehouse[$wh[
'id']]->real;
484 if ($virtualdiffersfromphysical) {
485 print
'<td class="right">';
486 if ($objp->seuil_stock_alerte !=
'' && ($product->stock_theorique < $objp->seuil_stock_alerte)) {
487 print
img_warning($langs->trans(
"StockTooLow")).
' ';
489 print
price2num($product->stock_theorique,
'MS');
493 if (!empty($conf->global->PRODUCT_USE_UNITS)) {
494 print
'<td class="left">'.$objp->unit_short.
'</td>';
496 print
'<td class="center">';
497 print
img_picto($langs->trans(
"StockMovement"),
'movement',
'class="pictofixedwidth"');
498 print
'<a href="'.DOL_URL_ROOT.
'/product/stock/movement_list.php?idproduct='.$product->id.
'">'.$langs->trans(
"Movements").
'</a>';
500 print
'<td class="right nowrap">'.$product->LibStatut($objp->statut, 5, 0).
'</td>';
501 print
'<td class="right nowrap">'.$product->LibStatut($objp->tobuy, 5, 1).
'</td>';
503 $parameters = array(
'obj'=>$objp);
504 $reshook = $hookmanager->executeHooks(
'printFieldListValue', $parameters, $product);
505 print $hookmanager->resPrint;