From 8ebad7a3957feca485fe6a7b6304887069be4da5 Mon Sep 17 00:00:00 2001 From: Laurent Destailleur Date: Sun, 12 Apr 2015 17:08:14 +0200 Subject: [PATCH] A serious rewrite of a lot of code to have the module batch number stable. --- htdocs/compta/facture/class/facture.class.php | 12 +- htdocs/core/menus/init_menu_auguria.sql | 3 +- htdocs/core/menus/standard/eldy.lib.php | 13 +- htdocs/langs/en_US/stocks.lang | 1 + htdocs/product/card.php | 6 +- htdocs/product/class/product.class.php | 46 ++- htdocs/product/index.php | 2 +- htdocs/product/reassort.php | 22 +- htdocs/product/reassortlot.php | 388 ++++++++++++++++++ .../stock/class/mouvementstock.class.php | 31 +- htdocs/product/stock/index.php | 19 +- htdocs/product/stock/list.php | 19 +- htdocs/product/stock/massstockmove.php | 44 +- htdocs/product/stock/product.php | 4 +- 14 files changed, 551 insertions(+), 59 deletions(-) create mode 100644 htdocs/product/reassortlot.php diff --git a/htdocs/compta/facture/class/facture.class.php b/htdocs/compta/facture/class/facture.class.php index 358f91bfcf2..c0d32275ec9 100644 --- a/htdocs/compta/facture/class/facture.class.php +++ b/htdocs/compta/facture/class/facture.class.php @@ -136,7 +136,7 @@ class Facture extends CommonInvoice var $specimen; var $fac_rec; - + //Incoterms var $fk_incoterms; var $location_incoterms; @@ -559,7 +559,7 @@ class Facture extends CommonInvoice $action='create'; // Actions on extra fields (by external module or standard code) - // FIXME le hook fait double emploi avec le trigger !! + // TODO le hook fait double emploi avec le trigger !! $hookmanager->initHooks(array('invoicedao')); $parameters=array('invoiceid'=>$this->id); $reshook=$hookmanager->executeHooks('insertExtraFields',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks @@ -1040,9 +1040,9 @@ class Facture extends CommonInvoice //Incoterms $this->fk_incoterms = $obj->fk_incoterms; - $this->location_incoterms = $obj->location_incoterms; + $this->location_incoterms = $obj->location_incoterms; $this->libelle_incoterms = $obj->libelle_incoterms; - + if ($this->statut == self::STATUS_DRAFT) $this->brouillon = 1; // Retreive all extrafield for invoice @@ -3607,7 +3607,7 @@ class Facture extends CommonInvoice $this->db->commit(); return 1; } else { - $this->error = $this->db->error(); + $this->error = $this->db->lasterror(); dol_syslog(get_class($this) . "::update Error setFinal " . $sql, LOG_ERR); $this->db->rollback(); return -1; @@ -3630,7 +3630,7 @@ class Facture extends CommonInvoice $last = $res['max(situation_counter)']; return ($last == $this->situation_counter); } else { - $this->error = $this->db->error(); + $this->error = $this->db->lasterror(); dol_syslog(get_class($this) . "::select Error " . $this->error, LOG_ERR); $this->db->rollback(); return -1; diff --git a/htdocs/core/menus/init_menu_auguria.sql b/htdocs/core/menus/init_menu_auguria.sql index f1db486bfc0..fd4d41ce6a7 100644 --- a/htdocs/core/menus/init_menu_auguria.sql +++ b/htdocs/core/menus/init_menu_auguria.sql @@ -93,7 +93,8 @@ insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, left insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->product->enabled', __HANDLER__, 'left', 2801__+MAX_llx_menu__, 'products', '', 2800__+MAX_llx_menu__, '/product/card.php?leftmenu=product&action=create&type=0', 'NewProduct', 1, 'products', '$user->rights->produit->creer', '', 2, 0, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->product->enabled', __HANDLER__, 'left', 2802__+MAX_llx_menu__, 'products', '', 2800__+MAX_llx_menu__, '/product/list.php?leftmenu=product&type=0', 'List', 1, 'products', '$user->rights->produit->lire', '', 2, 1, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->product->enabled', __HANDLER__, 'left', 2803__+MAX_llx_menu__, 'products', '', 2800__+MAX_llx_menu__, '/product/reassort.php?type=0', 'Stocks', 1, 'products', '$user->rights->produit->lire && $user->rights->stock->lire', '', 2, 4, __ENTITY__); -insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->propal->enabled', __HANDLER__, 'left', 2804__+MAX_llx_menu__, 'products', '', 2800__+MAX_llx_menu__, '/product/popuprop.php?leftmenu=stats&type=0', 'Statistics', 1, 'main', '$user->rights->produit->lire', '', 2, 5, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->productbatch->enabled', __HANDLER__, 'left', 2805__+MAX_llx_menu__, 'products', '', 2800__+MAX_llx_menu__, '/product/reassortlot.php?type=0', 'StocksByLotSerial', 1, 'products', '$user->rights->produit->lire && $user->rights->stock->lire', '', 2, 5, __ENTITY__); +insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->propal->enabled', __HANDLER__, 'left', 2804__+MAX_llx_menu__, 'products', '', 2800__+MAX_llx_menu__, '/product/popuprop.php?leftmenu=stats&type=0', 'Statistics', 1, 'main', '$user->rights->produit->lire', '', 2, 6, __ENTITY__); -- Product - Services insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->service->enabled', __HANDLER__, 'left', 2900__+MAX_llx_menu__, 'products', 'service', 3__+MAX_llx_menu__, '/product/index.php?leftmenu=service&type=1', 'Services', 0, 'products', '$user->rights->service->lire', '', 2, 1, __ENTITY__); insert into llx_menu (module, enabled, menu_handler, type, rowid, mainmenu, leftmenu, fk_menu, url, titre, level, langs, perms, target, usertype, position, entity) values ('', '$conf->service->enabled', __HANDLER__, 'left', 2901__+MAX_llx_menu__, 'products', '', 2900__+MAX_llx_menu__, '/product/card.php?leftmenu=service&action=create&type=1', 'NewService', 1, 'products', '$user->rights->service->creer', '', 2, 0, __ENTITY__); diff --git a/htdocs/core/menus/standard/eldy.lib.php b/htdocs/core/menus/standard/eldy.lib.php index 748f7111e54..fc193f2ecda 100644 --- a/htdocs/core/menus/standard/eldy.lib.php +++ b/htdocs/core/menus/standard/eldy.lib.php @@ -1055,14 +1055,19 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu $newmenu->add("/product/index.php?leftmenu=product&type=0", $langs->trans("Products"), 0, $user->rights->produit->lire, '', $mainmenu, 'product'); $newmenu->add("/product/card.php?leftmenu=product&action=create&type=0", $langs->trans("NewProduct"), 1, $user->rights->produit->creer); $newmenu->add("/product/list.php?leftmenu=product&type=0", $langs->trans("List"), 1, $user->rights->produit->lire); - if (! empty($conf->propal->enabled)) - { - $newmenu->add("/product/popuprop.php?leftmenu=stats&type=0", $langs->trans("Statistics"), 1, $user->rights->produit->lire && $user->rights->propale->lire); - } if (! empty($conf->stock->enabled)) { $newmenu->add("/product/reassort.php?type=0", $langs->trans("Stocks"), 1, $user->rights->produit->lire && $user->rights->stock->lire); } + if (! empty($conf->stock->enabled)) + { + $langs->load("stocks"); + $newmenu->add("/product/reassortlot.php?type=0", $langs->trans("StocksByLotSerial"), 1, $user->rights->produit->lire && $user->rights->stock->lire); + } + if (! empty($conf->propal->enabled)) + { + $newmenu->add("/product/popuprop.php?leftmenu=stats&type=0", $langs->trans("Statistics"), 1, $user->rights->produit->lire && $user->rights->propale->lire); + } } // Services diff --git a/htdocs/langs/en_US/stocks.lang b/htdocs/langs/en_US/stocks.lang index 7b963497d82..55e2914719d 100644 --- a/htdocs/langs/en_US/stocks.lang +++ b/htdocs/langs/en_US/stocks.lang @@ -16,6 +16,7 @@ CancelSending=Cancel sending DeleteSending=Delete sending Stock=Stock Stocks=Stocks +StocksByLotSerial=Stock by lot/serial Movement=Movement Movements=Movements ErrorWarehouseRefRequired=Warehouse reference name is required diff --git a/htdocs/product/card.php b/htdocs/product/card.php index 1803c35629b..102b86299eb 100644 --- a/htdocs/product/card.php +++ b/htdocs/product/card.php @@ -1,6 +1,6 @@ - * Copyright (C) 2004-2014 Laurent Destailleur + * Copyright (C) 2004-2015 Laurent Destailleur * Copyright (C) 2005 Eric Seigne * Copyright (C) 2005-2014 Regis Houssin * Copyright (C) 2006 Andre Cianfarani @@ -840,6 +840,8 @@ else else $title=$langs->trans("NewProduct"); print_fiche_titre($title); + dol_fiche_head(''); + print ''; print ''; $tmpcode=''; @@ -1058,6 +1060,8 @@ else print '
'; //} + dol_fiche_end(); + print '
'; print ''; diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 8e861056ce4..a18ef715d9d 100755 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -3202,7 +3202,8 @@ class Product extends CommonObject /** * Load information about stock of a product into stock_reel, stock_warehouse[] (including stock_warehouse[idwarehouse]->detail_batch for batch products) * - * @return int < 0 if KO, > 0 if OK + * @return int < 0 if KO, > 0 if OK + * @see load_virtual_stock, getBatchInfo */ function load_stock() { @@ -3250,9 +3251,10 @@ class Product extends CommonObject } /** - * Load information about virtual stock of a product + * Load information about objects that are delat between physical and virtual stock of a product * - * @return int < 0 if KO, > 0 if OK + * @return int < 0 if KO, > 0 if OK + * @see load_stock, getBatchInfo */ function load_virtual_stock() { @@ -3308,6 +3310,44 @@ class Product extends CommonObject } } + + /** + * Load existing information about a serial + * + * @param string $batch + * @return array Array with record into product_batch + * @see load_stock, load_virtual_stock + */ + function loadBatchInfo($batch) + { + $result=array(); + + $sql = "SELECT pb.batch, pb.eatby, pb.sellby, SUM(pb.qty) FROM ".MAIN_DB_PREFIX."product_batch as pb, ".MAIN_DB_PREFIX."product_stock as ps"; + $sql.= " WHERE pb.fk_product_stock = ps.rowid AND ps.fk_product = ".$this->id." AND pb.batch = '".$this->db->escape($batch)."'"; + $sql.= " GROUP BY pb.batch, pb.eatby, pb.sellby"; + dol_syslog(get_class($this)."::loadBatchInfo load first entry found for lot/serial = ".$batch, LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) + { + $num = $this->db->num_rows($resql); + $i=0; + while ($i < $num) + { + $obj = $this->db->fetch_object($resql); + $result[]=array('batch'=>$batch, 'eatby'=>$this->db->jdate($obj->eatby), 'sellby'=>$this->db->jdate($obj->sellby), 'qty'=>$obj->qty); + $i++; + } + return $result; + } + else + { + dol_print_error($this->db); + $this->db->rollback(); + return array(); + } + } + + /** * Move an uploaded file described into $file array into target directory $sdir. * diff --git a/htdocs/product/index.php b/htdocs/product/index.php index 3142e041262..b98470b272d 100644 --- a/htdocs/product/index.php +++ b/htdocs/product/index.php @@ -1,6 +1,6 @@ - * Copyright (C) 2004-2011 Laurent Destailleur + * Copyright (C) 2004-2015 Laurent Destailleur * Copyright (C) 2005-2014 Regis Houssin * Copyright (C) 2014 Charles-Fr BENKE * diff --git a/htdocs/product/reassort.php b/htdocs/product/reassort.php index 88b2e4721a0..be38eda6b2d 100644 --- a/htdocs/product/reassort.php +++ b/htdocs/product/reassort.php @@ -102,13 +102,12 @@ $title=$langs->trans("ProductsAndServices"); $sql = 'SELECT p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type,'; $sql.= ' p.fk_product_type, p.tms as datem,'; -$sql.= ' p.duration, p.tosell as statut, p.tobuy, p.seuil_stock_alerte,'; +$sql.= ' p.duration, p.tosell as statut, p.tobuy, p.seuil_stock_alerte, p.desiredstock,'; $sql.= ' SUM(s.reel) as stock_physique'; -$sql .= ', p.desiredstock'; -$sql.= ' FROM ('.MAIN_DB_PREFIX.'product as p'; +$sql.= ' FROM '.MAIN_DB_PREFIX.'product as p'; +$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as s on p.rowid = s.fk_product'; // We'll need this table joined to the select in order to filter by categ if ($search_categ) $sql.= ", ".MAIN_DB_PREFIX."categorie_product as cp"; -$sql.= ') LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as s on p.rowid = s.fk_product'; $sql.= " WHERE p.entity IN (".getEntity('product', 1).")"; if ($search_categ) $sql.= " AND p.rowid = cp.fk_product"; // Join for the needed table to filter by categ if ($sall) @@ -156,9 +155,7 @@ if ($search_categ) $sql .= " AND cp.fk_categorie = ".$db->escape($search_categ); } $sql.= " GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type,"; -$sql.= " p.fk_product_type, p.tms,"; -$sql.= " p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte"; -$sql .= ", p.desiredstock"; +$sql.= " p.fk_product_type, p.tms, p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte, p.desiredstock"; if ($toolowstock) $sql.= " HAVING SUM(".$db->ifsql('s.reel IS NULL', '0', 's.reel').") < p.seuil_stock_alerte"; // Not used yet $sql.= $db->order($sortfield,$sortorder); $sql.= $db->plimit($limit + 1, $offset); @@ -247,19 +244,19 @@ if ($resql) print_liste_field_titre($langs->trans("DesiredStock"), $_SERVER["PHP_SELF"], "p.desiredstock",$param,"",'align="right"',$sortfield,$sortorder); print_liste_field_titre($langs->trans("PhysicalStock"), $_SERVER["PHP_SELF"], "stock_physique",$param,"",'align="right"',$sortfield,$sortorder); // TODO Add info of running suppliers/customers orders - //print_liste_field_titre($langs->trans("TheoreticalStock"),"reassort.php", "stock_theorique",$param,"",'align="right"',$sortfield,$sortorder); + //print_liste_field_titre($langs->trans("TheoreticalStock"),$_SERVER["PHP_SELF"], "stock_theorique",$param,"",'align="right"',$sortfield,$sortorder); print ''; - print_liste_field_titre($langs->trans("Sell"),"reassort.php", "p.tosell",$param,"",'align="right"',$sortfield,$sortorder); - print_liste_field_titre($langs->trans("Buy"),"reassort.php", "p.tobuy",$param,"",'align="right"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("Sell"),$_SERVER["PHP_SELF"], "p.tosell",$param,"",'align="right"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("Buy"),$_SERVER["PHP_SELF"], "p.tobuy",$param,"",'align="right"',$sortfield,$sortorder); print "\n"; // Lignes des champs de filtre print ''; print ''; print ''; if (! empty($conf->service->enabled) && $type == 1) { @@ -267,6 +264,7 @@ if ($resql) print ' '; print ''; } + // Lot/Serial print ''; print ''; print ''; diff --git a/htdocs/product/reassortlot.php b/htdocs/product/reassortlot.php new file mode 100644 index 00000000000..451e4302f05 --- /dev/null +++ b/htdocs/product/reassortlot.php @@ -0,0 +1,388 @@ + + * Copyright (C) 2004-2015 Laurent Destailleur + * Copyright (C) 2005-2012 Regis Houssin + * Copyright (C) 2013 Cédric Salvador + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/product/reassortlot.php + * \ingroup produit + * \brief Page to list stocks + */ + +require '../main.inc.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; +require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; +require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php'; + +$langs->load("products"); +$langs->load("stocks"); +$langs->load("productbatch"); + +// Security check +if ($user->societe_id) $socid=$user->societe_id; +$result=restrictedArea($user,'produit|service'); + + +$action=GETPOST('action','alpha'); +$sref=GETPOST("sref"); +$snom=GETPOST("snom"); +$sall=GETPOST("sall"); +$type=GETPOST("type","int"); +$sbarcode=GETPOST("sbarcode"); +$search_batch=GETPOST('search_batch'); +$catid=GETPOST('catid','int'); +$toolowstock=GETPOST('toolowstock'); +$tosell = GETPOST("tosell"); +$tobuy = GETPOST("tobuy"); +$fourn_id = GETPOST("fourn_id",'int'); + +$sortfield = GETPOST("sortfield",'alpha'); +$sortorder = GETPOST("sortorder",'alpha'); +$page = GETPOST("page",'int'); +if (! $sortfield) $sortfield="stock_physique"; +if (! $sortorder) $sortorder="ASC"; +$limit = $conf->liste_limit; +$offset = $limit * $page ; + +// Load sale and categ filters +$search_sale = GETPOST("search_sale"); +$search_categ = GETPOST("search_categ"); + +// Get object canvas (By default, this is not defined, so standard usage of dolibarr) +$canvas=GETPOST("canvas"); +$objcanvas=null; +if (! empty($canvas)) +{ + require_once DOL_DOCUMENT_ROOT.'/core/class/canvas.class.php'; + $objcanvas = new Canvas($db,$action); + $objcanvas->getCanvas('product','list',$canvas); +} + +if (! empty($_POST["button_removefilter_x"])) +{ + $sref=""; + $snom=""; + $sall=""; + $search_sale=""; + $search_categ=""; + $type=""; + $catid=''; + $toolowstock=''; +} + + + +/* + * Actions + */ + +// None + + +/* + * View + */ + +$htmlother=new FormOther($db); + +$title=$langs->trans("ProductsAndServices"); + +$sql = 'SELECT p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type,'; +$sql.= ' p.fk_product_type, p.tms as datem,'; +$sql.= ' p.duration, p.tosell as statut, p.tobuy, p.seuil_stock_alerte, p.desiredstock,'; +$sql.= ' s.fk_entrepot,'; +$sql.= ' pb.batch, pb.eatby, pb.sellby,'; +$sql.= ' SUM(pb.qty) as stock_physique'; +$sql.= ' FROM '.MAIN_DB_PREFIX.'product as p'; +$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_stock as s on p.rowid = s.fk_product'; +$sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'product_batch as pb on pb.fk_product_stock = s.rowid'; +// We'll need this table joined to the select in order to filter by categ +if ($search_categ) $sql.= ", ".MAIN_DB_PREFIX."categorie_product as cp"; +$sql.= " WHERE p.entity IN (".getEntity('product', 1).")"; +if ($search_categ) $sql.= " AND p.rowid = cp.fk_product"; // Join for the needed table to filter by categ +if ($sall) +{ + $sql.= " AND (p.ref LIKE '%".$db->escape($sall)."%' OR p.label LIKE '%".$db->escape($sall)."%' OR p.description LIKE '%".$db->escape($sall)."%' OR p.note LIKE '%".$db->escape($sall)."%')"; +} +// if the type is not 1, we show all products (type = 0,2,3) +if (dol_strlen($type)) +{ + if ($type==1) + { + $sql.= " AND p.fk_product_type = '1'"; + } + else + { + $sql.= " AND p.fk_product_type <> '1'"; + } +} +if ($sref) $sql.= " AND p.ref LIKE '%".$sref."%'"; +if ($sbarcode) $sql.= " AND p.barcode LIKE '%".$sbarcode."%'"; +if ($snom) $sql.= " AND p.label LIKE '%".$db->escape($snom)."%'"; +if (! empty($tosell)) +{ + $sql.= " AND p.tosell = ".$tosell; +} +if (! empty($tobuy)) +{ + $sql.= " AND p.tobuy = ".$tobuy; +} +if (! empty($canvas)) +{ + $sql.= " AND p.canvas = '".$db->escape($canvas)."'"; +} +if($catid) +{ + $sql.= " AND cp.fk_categorie = ".$catid; +} +if ($fourn_id > 0) +{ + $sql.= " AND p.rowid = pf.fk_product AND pf.fk_soc = ".$fourn_id; +} +// Insert categ filter +if ($search_categ) +{ + $sql .= " AND cp.fk_categorie = ".$db->escape($search_categ); +} +if ($search_batch) +{ + $sql .= " AND pb.batch LIKE '%".$db->escape($search_batch)."%'"; +} +$sql.= " GROUP BY p.rowid, p.ref, p.label, p.barcode, p.price, p.price_ttc, p.price_base_type,"; +$sql.= " p.fk_product_type, p.tms, p.duration, p.tosell, p.tobuy, p.seuil_stock_alerte, p.desiredstock,"; +$sql.= " s.fk_entrepot,"; +$sql.= " pb.batch, pb.eatby, pb.sellby"; +if ($toolowstock) $sql.= " HAVING SUM(".$db->ifsql('s.reel IS NULL', '0', 's.reel').") < p.seuil_stock_alerte"; // Not used yet +$sql.= $db->order($sortfield,$sortorder); +$sql.= $db->plimit($limit + 1, $offset); +$resql = $db->query($sql); + +if ($resql) +{ + $num = $db->num_rows($resql); + + $i = 0; + + if ($num == 1 && ($sall or $snom or $sref)) + { + $objp = $db->fetch_object($resql); + header("Location: card.php?id=$objp->rowid"); + exit; + } + + $helpurl=''; + $helpurl='EN:Module_Stocks_En|FR:Module_Stock|ES:Módulo_Stocks'; + + if (isset($type)) + { + if ($type==1) { $texte = $langs->trans("Services"); } + else { $texte = $langs->trans("Products"); } + } else { + $texte = $langs->trans("ProductsAndServices"); + } + $texte.=' ('.$langs->trans("StocksByLotSerial").')'; + + + llxHeader("",$title,$helpurl,$texte); + + if ($sref || $snom || $sall || GETPOST('search')) + { + print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], "&sref=".$sref."&snom=".$snom."&sall=".$sall."&tosell=".$tosell."&tobuy=".$tobuy, $sortfield, $sortorder,'',$num); + } + else + { + print_barre_liste($texte, $page, $_SERVER["PHP_SELF"], "&sref=$sref&snom=$snom&fourn_id=$fourn_id".(isset($type)?"&type=$type":""), $sortfield, $sortorder,'',$num); + } + + if (! empty($catid)) + { + print "
"; + $c = new Categorie($db); + $c->fetch($catid); + $ways = $c->print_all_ways(' > ','product/reassortlot.php'); + print " > ".$ways[0]."
\n"; + print "

"; + } + + print '
'; + print ''; + print ''; + print ''; + print ''; + + print '
 
'; - print ''; + print ''; print ''; - print ''; + print ''; print '   
'; + + // Filter on categories + $moreforfilter=''; + if (! empty($conf->categorie->enabled)) + { + $moreforfilter.=$langs->trans('Categories'). ': '; + $moreforfilter.=$htmlother->select_categories(0,$search_categ,'search_categ'); + $moreforfilter.='           '; + } + //$moreforfilter.=$langs->trans("StockTooLow").' '; + if ($moreforfilter) + { + print ''; + print ''; + } + + $param="&tosell=$tosell&tobuy=$tobuy".(isset($type)?"&type=$type":"")."&fourn_id=$fourn_id&snom=$snom&sref=$sref&batch=$batch&eatby=$eatby&sellby=$sellby"; + + // Lignes des titres + print ""; + print_liste_field_titre($langs->trans("Ref"), $_SERVER["PHP_SELF"], "p.ref",$param,"","",$sortfield,$sortorder); + print_liste_field_titre($langs->trans("Label"), $_SERVER["PHP_SELF"], "p.label",$param,"","",$sortfield,$sortorder); + if (! empty($conf->service->enabled) && $type == 1) print_liste_field_titre($langs->trans("Duration"), $_SERVER["PHP_SELF"], "p.duration",$param,"",'align="center"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("Warehouse"), $_SERVER["PHP_SELF"], "",$param,"",'',$sortfield,$sortorder); + //print_liste_field_titre($langs->trans("DesiredStock"), $_SERVER["PHP_SELF"], "p.desiredstock",$param,"",'align="right"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("Batch"), $_SERVER["PHP_SELF"], "pb.batch",$param,"",'align="center"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("l_eatby"), $_SERVER["PHP_SELF"], "pb.eatby",$param,"",'align="center"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("l_sellby"), $_SERVER["PHP_SELF"], "pb.sellby",$param,"",'align="center"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("PhysicalStock"), $_SERVER["PHP_SELF"], "stock_physique",$param,"",'align="right"',$sortfield,$sortorder); + // TODO Add info of running suppliers/customers orders + //print_liste_field_titre($langs->trans("TheoreticalStock"),$_SERVER["PHP_SELF"], "stock_theorique",$param,"",'align="right"',$sortfield,$sortorder); + print ''; + print_liste_field_titre($langs->trans("Sell"),$_SERVER["PHP_SELF"], "p.tosell",$param,"",'align="right"',$sortfield,$sortorder); + print_liste_field_titre($langs->trans("Buy"),$_SERVER["PHP_SELF"], "p.tobuy",$param,"",'align="right"',$sortfield,$sortorder); + print "\n"; + + // Lignes des champs de filtre + print ''; + print ''; + print ''; + if (! empty($conf->service->enabled) && $type == 1) + { + print ''; + } + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + + $product_static=new Product($db); + $warehousetmp=new Entrepot($db); + + $var=True; + while ($i < min($num,$limit)) + { + $objp = $db->fetch_object($resql); + + // Multilangs + if (! empty($conf->global->MAIN_MULTILANGS)) // si l'option est active + { + $sql = "SELECT label"; + $sql.= " FROM ".MAIN_DB_PREFIX."product_lang"; + $sql.= " WHERE fk_product=".$objp->rowid; + $sql.= " AND lang='". $langs->getDefaultLang() ."'"; + $sql.= " LIMIT 1"; + + $result = $db->query($sql); + if ($result) + { + $objtp = $db->fetch_object($result); + if (! empty($objtp->label)) $objp->label = $objtp->label; + } + } + + $var=!$var; + print ''; + print ''; + + if (! empty($conf->service->enabled) && $type == 1) + { + print ''; + } + //print ''; + //print ''; + //print ''; + + // Warehouse + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print "\n"; + $i++; + } + + print "
'; + print $moreforfilter; + print '
 
'; + print ''; + print ''; + print ''; + print ''; + print ' '; + print '      '; + print ''; + print ''; + print '
'; + $product_static->ref=$objp->ref; + $product_static->id=$objp->rowid; + $product_static->label = $objp->label; + $product_static->type=$objp->fk_product_type; + print $product_static->getNomUrl(1,'',16); + //if ($objp->stock_theorique < $objp->seuil_stock_alerte) print ' '.img_warning($langs->trans("StockTooLow")); + print ''.$objp->label.''; + if (preg_match('/([0-9]+)y/i',$objp->duration,$regs)) print $regs[1].' '.$langs->trans("DurationYear"); + elseif (preg_match('/([0-9]+)m/i',$objp->duration,$regs)) print $regs[1].' '.$langs->trans("DurationMonth"); + elseif (preg_match('/([0-9]+)d/i',$objp->duration,$regs)) print $regs[1].' '.$langs->trans("DurationDay"); + else print $objp->duration; + print ''.$objp->stock_theorique.''.$objp->seuil_stock_alerte.''.$objp->desiredstock.''; + $warehousetmp->fetch($obj->fk_entrepot); + print $warehousetmp->getNomUrl(1); + print ''.$objp->batch.''.dol_print_date($db->jdate($objp->eatby), 'day').''.dol_print_date($db->jdate($objp->sellby), 'day').''; + //if ($objp->seuil_stock_alerte && ($objp->stock_physique < $objp->seuil_stock_alerte)) print img_warning($langs->trans("StockTooLow")).' '; + print $objp->stock_physique; + print ''.$langs->trans("Movements").''.$product_static->LibStatut($objp->statut,5,0).''.$product_static->LibStatut($objp->tobuy,5,1).'
"; + print ''; + + if ($num > $conf->liste_limit) + { + if ($sref || $snom || $sall || GETPOST('search')) + { + print_barre_liste('', $page, "reassort.php", "&sref=".$sref."&snom=".$snom."&sall=".$sall."&tosell=".$tosell."&tobuy=".$tobuy, $sortfield, $sortorder,'',$num, 0, ''); + } + else + { + print_barre_liste('', $page, "reassort.php", "&sref=$sref&snom=$snom&fourn_id=$fourn_id".(isset($type)?"&type=$type":"")."&tosell=".$tosell."&tobuy=".$tobuy, $sortfield, $sortorder,'',$num, 0, ''); + } + } + + $db->free($resql); + +} +else +{ + dol_print_error($db); +} + + +llxFooter(); +$db->close(); diff --git a/htdocs/product/stock/class/mouvementstock.class.php b/htdocs/product/stock/class/mouvementstock.class.php index 3f8b2362386..2e9fcfab68e 100644 --- a/htdocs/product/stock/class/mouvementstock.class.php +++ b/htdocs/product/stock/class/mouvementstock.class.php @@ -76,7 +76,7 @@ class MouvementStock extends CommonObject require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; $error = 0; - dol_syslog(get_class($this)."::_create start userid=$user->id, fk_product=$fk_product, warehouse=$entrepot_id, qty=$qty, type=$type, price=$price, label=$label, inventorycode=$inventorycode"); + dol_syslog(get_class($this)."::_create start userid=$user->id, fk_product=$fk_product, warehouse=$entrepot_id, qty=$qty, type=$type, price=$price, label=$label, inventorycode=$inventorycode, datem=".$datem.", eatby=".$eatby.", sellby=".$sellby.", batch=".$batch.", skip_batch=".$skip_batch); // Clean parameters if (empty($price)) $price=0; @@ -101,7 +101,7 @@ class MouvementStock extends CommonObject $this->qty = $qty; $this->type = $type; - $this->db->begin(); + $mvid = 0; $product = new Product($this->db); $result=$product->fetch($fk_product); @@ -110,6 +110,9 @@ class MouvementStock extends CommonObject dol_print_error('',"Failed to fetch product"); return -1; } + + $this->db->begin(); + $product->load_stock(); // Test if product require batch data. If yes, and there is not, we throw an error. @@ -137,17 +140,17 @@ class MouvementStock extends CommonObject while ($i < $num) { $obj = $this->db->fetch_object($resql); - if ($obj->eatby != $eatby) + if ($this->db->jdate($obj->eatby) != $eatby) { - $this->errors[]=$langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $obj->eatby, $eatby); - dol_syslog($langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $obj->eatby, $eatby)); + $this->errors[]=$langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $this->db->jdate($obj->eatby), $eatby); + dol_syslog($langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $this->db->jdate($obj->eatby), $eatby)); $this->db->rollback(); return -3; } - if ($obj->sellby != $sellby) + if ($this->db->jdate($obj->sellby) != $sellby) { - $this->errors[]=$langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $obj->sellby, $sellby); - dol_syslog($langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $obj->sellby, $sellby)); + $this->errors[]=$langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $this->db->jdate($obj->sellby), $sellby); + dol_syslog($langs->trans("ThisSerialAlreadyExistWithDifferentDate", $batch, $this->db->jdate($obj->sellby), $sellby)); $this->db->rollback(); return -3; } @@ -162,6 +165,16 @@ class MouvementStock extends CommonObject } } + // TODO Check qty is ok for stock move. + if (! empty($conf->productbatch->enabled) && $product->hasbatch() && ! $skip_batch) + { + + } + else + { + + } + // Define if we must make the stock change (If product type is a service or if stock is used also for services) $movestock=0; if ($product->type != Product::TYPE_SERVICE || ! empty($conf->global->STOCK_SUPPORTS_SERVICES)) $movestock=1; @@ -176,8 +189,6 @@ class MouvementStock extends CommonObject $fk_origin = 0; } - $mvid = 0; - $sql = "INSERT INTO ".MAIN_DB_PREFIX."stock_mouvement("; $sql.= " datem, fk_product, batch, eatby, sellby,"; $sql.= " fk_entrepot, value, type_mouvement, fk_user_author, label, inventorycode, price, fk_origin, origintype"; diff --git a/htdocs/product/stock/index.php b/htdocs/product/stock/index.php index dc394f898dc..aacdedc3668 100644 --- a/htdocs/product/stock/index.php +++ b/htdocs/product/stock/index.php @@ -27,6 +27,7 @@ require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/product/stock/class/entrepot.class.php'; $langs->load("stocks"); +$langs->load("productbatch"); // Security check $result=restrictedArea($user,'stock'); @@ -112,7 +113,7 @@ print '
'; $max=10; $sql = "SELECT p.rowid, p.label as produit,"; $sql.= " e.label as stock, e.rowid as entrepot_id,"; -$sql.= " m.value, m.datem"; +$sql.= " m.value as qty, m.datem, m.batch, m.eatby, m.sellby"; $sql.= " FROM ".MAIN_DB_PREFIX."entrepot as e"; $sql.= ", ".MAIN_DB_PREFIX."stock_mouvement as m"; $sql.= ", ".MAIN_DB_PREFIX."product as p"; @@ -133,6 +134,12 @@ if ($resql) print ""; print ''.$langs->trans("LastMovements",min($num,$max)).''; print ''.$langs->trans("Product").''; + if (! empty($conf->productbatch->enabled)) + { + print ''.$langs->trans("Batch").''; + print ''.$langs->trans("l_eatby").''; + print ''.$langs->trans("l_sellby").''; + } print ''.$langs->trans("Warehouse").''; print ''.$langs->trans("FullList").''; print "\n"; @@ -148,12 +155,18 @@ if ($resql) print "rowid\">"; print img_object($langs->trans("ShowProduct"),"product").' '.$objp->produit; print "\n"; + if (! empty($conf->productbatch->enabled)) + { + print ''.$objp->batch.''; + print ''.dol_print_date($db->jdate($objp->eatby),'day').''; + print ''.dol_print_date($db->jdate($objp->sellby),'day').''; + } print ''; print img_object($langs->trans("ShowWarehouse"),"stock").' '.$objp->stock; print "\n"; print ''; - if ($objp->value > 0) print '+'; - print $objp->value.''; + if ($objp->qty > 0) print '+'; + print $objp->qty.''; print "\n"; $i++; } diff --git a/htdocs/product/stock/list.php b/htdocs/product/stock/list.php index f6417b389e1..fcc7412c061 100644 --- a/htdocs/product/stock/list.php +++ b/htdocs/product/stock/list.php @@ -1,6 +1,6 @@ - * Copyright (C) 2004-2014 Laurent Destailleur + * Copyright (C) 2004-2015 Laurent Destailleur * Copyright (C) 2005-2014 Regis Houssin * * This program is free software; you can redistribute it and/or modify @@ -51,6 +51,8 @@ $year = strftime("%Y",time()); * View */ +$form=new Form($db); + $sql = "SELECT e.rowid, e.label as ref, e.statut, e.lieu, e.address, e.zip, e.town, e.fk_pays,"; $sql.= " SUM(p.pmp * ps.reel) as estimatedvalue, SUM(p.price * ps.reel) as sellvalue"; $sql.= " FROM ".MAIN_DB_PREFIX."entrepot as e"; @@ -118,7 +120,11 @@ if ($result) // Selling value print ''; if (empty($conf->global->PRODUIT_MULTIPRICES)) print price(price2num($objp->sellvalue,'MT'),1); - else print $langs->trans("Variable"); + else + { + $htmltext=$langs->trans("OptionMULTIPRICESIsOn"); + print $form->textwithtooltip($langs->trans("Variable"),$htmltext); + } print ''; // Status print ''.$entrepot->LibStatut($objp->statut,5).''; @@ -134,7 +140,14 @@ if ($result) print ''; print ''.$langs->trans("Total").''; print ''.price(price2num($total,'MT'),1,$langs,0,0,-1,$conf->currency).''; - print ''.price(price2num($totalsell,'MT'),1,$langs,0,0,-1,$conf->currency).''; + print ''; + if (empty($conf->global->PRODUIT_MULTIPRICES)) print price(price2num($totalsell,'MT'),1,$langs,0,0,-1,$conf->currency); + else + { + $htmltext=$langs->trans("OptionMULTIPRICESIsOn"); + print $form->textwithtooltip($langs->trans("Variable"),$htmltext); + } + print ''; print ' '; print "\n"; } diff --git a/htdocs/product/stock/massstockmove.php b/htdocs/product/stock/massstockmove.php index 386f8ef2a2b..7b3d41c9911 100644 --- a/htdocs/product/stock/massstockmove.php +++ b/htdocs/product/stock/massstockmove.php @@ -107,7 +107,7 @@ if ($action == 'addline') { $producttmp=new Product($db); $producttmp->fetch($id_product); - if ($producttmp->status_batch) + if ($producttmp->hasbatch()) { if (empty($batch)) { @@ -117,6 +117,21 @@ if ($action == 'addline') } } + // TODO Check qty is ok for stock move. Note qty may not be enough yet, but we make a check now to report a warning. + // What is important is to have qty when doing action 'createmovements' + if (! $error) + { + // Warning, don't forget lines already added into the $_SESSION['massstockmove'] + if ($producttmp->hasbatch()) + { + + } + else + { + + } + } + if (! $error) { if (count(array_keys($listofdata)) > 0) $id=max(array_keys($listofdata)) + 1; @@ -214,12 +229,19 @@ if ($action == 'createmovements') } else { - // FIXME Seach record into product_batch table from serial to use same value for dlc and dluo - // FIXME MAke field batch lot required. - - /*var_dump($batch); - var_dump($product->stock_warehouse); - exit;*/ + $arraybatchinfo=$product->loadBatchInfo($batch); + if (count($arraybatchinfo) > 0) + { + $firstrecord = array_shift($arraybatchinfo); + $dlc=$firstrecord['eatby']; + $dluo=$firstrecord['sellby']; + //var_dump($batch); var_dump($arraybatchinfo); var_dump($firstrecord); var_dump($dlc); var_dump($dluo); exit; + } + else + { + $dlc=''; + $dluo=''; + } // Remove stock $result1=$product->correct_stock_batch( @@ -325,7 +347,6 @@ $param=''; print ''; print getTitleFieldOfList($langs->trans('ProductRef'),0,$_SERVER["PHP_SELF"],'',$param,'','class="tagtd"',$sortfield,$sortorder); -print getTitleFieldOfList(''); if ($conf->productbatch->enabled) { print getTitleFieldOfList($langs->trans('Batch'),0,$_SERVER["PHP_SELF"],'',$param,'','class="tagtd"',$sortfield,$sortorder); @@ -352,8 +373,6 @@ else } print $form->select_produits($id_product,'productid',$filtertype,$limit); print ''; -print ''; -print ''; // Batch number if ($conf->productbatch->enabled) { @@ -386,12 +405,9 @@ foreach($listofdata as $key => $val) $warehousestatict->fetch($val['id_tw']); print ''; - print ''.$productstatic->getNomUrl(1).''; print ''; - $oldref=$productstatic->ref; - $productstatic->ref=$productstatic->label; print $productstatic->getNomUrl(1); - $productstatic->ref=$oldref; + print ' - '.$productstatic->label; print ''; if ($conf->productbatch->enabled) { diff --git a/htdocs/product/stock/product.php b/htdocs/product/stock/product.php index 39a81abe86d..40d9ef7e060 100644 --- a/htdocs/product/stock/product.php +++ b/htdocs/product/stock/product.php @@ -432,7 +432,9 @@ if ($id > 0 || $ref) // PMP print ''.$langs->trans("AverageUnitPricePMP").''; - print ''.price($product->pmp).' '.$langs->trans("HT").''; + print ''; + if ($product->pmp > 0) print price($product->pmp).' '.$langs->trans("HT"); + print ''; print ''; // Minimum Price