From a7813c37a1dee3b5ba8d2e4da451e0f16e96f519 Mon Sep 17 00:00:00 2001 From: Alexis Algoud Date: Mon, 12 Dec 2016 12:36:00 +0100 Subject: [PATCH] init inventory integration --- htdocs/core/class/coreobject.class.php | 214 ++++++++++ htdocs/inventory/ajax/ajax.inventory.php | 50 +++ htdocs/inventory/class/inventory.class.php | 466 +++++++++++++++++++++ 3 files changed, 730 insertions(+) create mode 100644 htdocs/core/class/coreobject.class.php create mode 100644 htdocs/inventory/ajax/ajax.inventory.php create mode 100644 htdocs/inventory/class/inventory.class.php diff --git a/htdocs/core/class/coreobject.class.php b/htdocs/core/class/coreobject.class.php new file mode 100644 index 00000000000..1cc68f38cb7 --- /dev/null +++ b/htdocs/core/class/coreobject.class.php @@ -0,0 +1,214 @@ + + * + * 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/core/class/coreobject.inventory.php + * \ingroup core + * \brief File of class to manage all object. Might be replace or merge into commonobject + */ + +require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; + +class CoreObject extends CommonObject { + + + /** + * Constructor + * + * @param DoliDB $db Database handler + */ + function __construct(DoliDB &$db) { + + $this->db = &$db; + + $this->date_0 = '1001-01-01 00:00:00'; //TODO there is a solution for this ? + } + + private function init() { + + $this->id = 0; + $this->datec = time(); + $this->tms = time(); + + if(!empty($this->__fields)) { + foreach ($this->__fields as $field=>$info) { + + if($this->is_date($info)){ + $this->{$field} = time(); + } + elseif($this->is_array($info)){ + $this->{$field} = array(); + } + elseif($this->is_int($info)){ + $this->{$field} = (int)0; + } + elseif($this->is_float($info)) { + $this->{$field} = (double)0; + } + else{ + $this->{$field} = ''; + } + } + + $this->to_delete=false; + + return true; + } + else{ + return false; + } + + } + + + private function is_date(Array &$info){ + + if(is_array($info)) { + if(isset($info['type']) && $info['type']=='date') return true; + else return false; + } + else return false; + } + + private function is_array($info) { + + if(is_array($info)) { + if(isset($info['type']) && $info['type']=='array') return true; + else return false; + } + else return false; + } + + + private function is_null($info){ + if(is_array($info)) { + if(isset($info['type']) && $info['type']=='null') return true; + else return false; + } + else return false; + } + + private function is_int($info){ + + if(is_array($info)) { + if(isset($info['type']) && ($info['type']=='int' || $info['type']=='integer' )) return true; + else return false; + } + else return false; + } + private function _is_float($info){ + if(is_array($info)) { + if(isset($info['type']) && $info['type']=='float') return true; + else return false; + } else return false; + } + + private function _is_text($info){ + if(is_array($info)) { + if(isset($info['type']) && $info['type']=='text') return true; + else return false; + } else return false; + } + private function _is_index($info){ + if(is_array($info)) { + if(isset($info['index']) && $info['index']==true) return true; + else return false; + } else return false; + } + + private function set_save_query(){ + + $query=array( + 'rowid'=>$this->id + ,'datec'=>($this->id>0 ? $this->db->jdate($this->datec) : time()) + ,'tms'=>time() + ); + + foreach ($this->__fields as $field=>$info) { + + if($this->_is_date($info)){ + if(empty($this->{$field})){ + $query[$field] = $this->date_0; + } + else{ + $query[$field] = $this->db->jdate($this->{$field}); + } + } + else if($this->is_array($info)){ + $query[$field] = serialize($this->{$field}); + } + + else if($this->is_int($info)){ + $query[$field] = (int)price2num($this->{$field}); + } + + else if($this->_is_float($info)){ + $query[$field] = (double)price2num($this->{$field}); + } + + elseif($this->_is_null($info)) { + $query[$field] = (is_null($this->{$field}) || (empty($this->{$field}) && $this->{$field}!==0 && $this->{$field}!=='0')?null:$this->{$field}); + } + else{ + $query[$field] = $this->{$field}; + } + + } + + return $query; + } + + + public function fetch($id, $loadChild = true) { + + if(empty($id)) return false; + + $res = $db->query( 'SELECT '.$this->get_field_list().'datec,tms + FROM '.$this->table_element.' + WHERE rowid='.$id ); + if($obj = $db->fetch_object($res)) { + $this->rowid=$id; + + $this->set_vars_by_db($db); + + $this->datec=$this->db->idate($obj->datec); + $this->tms=$this->db->idate($obj->tms); + + if($loadChild) $this->loadChild($db); + + $this->run_trigger($db, 'load'); + + return $this->id; + } + else { + return false; + } + + } + public function update() { + + + + } + public function create() { + + + + } + + +} diff --git a/htdocs/inventory/ajax/ajax.inventory.php b/htdocs/inventory/ajax/ajax.inventory.php new file mode 100644 index 00000000000..34efecfab8f --- /dev/null +++ b/htdocs/inventory/ajax/ajax.inventory.php @@ -0,0 +1,50 @@ +rights->inventory->write) { echo -1; exit; } + + $fk_det_inventory = GETPOST('fk_det_inventory'); + + $det = new Inventorydet; + if( $det->load($PDOdb, $fk_det_inventory)) { + $det->qty_view+=GETPOST('qty'); + $det->save($PDOdb); + + echo $det->qty_view; + } + else { + echo -2; + } + + break; + + case 'pmp': + if (!$user->rights->inventory->write || !$user->rights->inventory->changePMP) { echo -1; exit; } + + $fk_det_inventory = GETPOST('fk_det_inventory'); + + $det = new Inventorydet; + if( $det->load($PDOdb, $fk_det_inventory)) { + $det->new_pmp=price2num(GETPOST('pmp')); + $det->save($PDOdb); + + echo $det->new_pmp; + } + else { + echo -2; + } + + break; + + } + diff --git a/htdocs/inventory/class/inventory.class.php b/htdocs/inventory/class/inventory.class.php new file mode 100644 index 00000000000..808dd7933be --- /dev/null +++ b/htdocs/inventory/class/inventory.class.php @@ -0,0 +1,466 @@ + + * + * 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/inventory/class/product.inventory.php + * \ingroup product + * \brief File of class to manage predefined products stock + */ + +require_once DOL_DOCUMENT_ROOT.'/core/class/coreobject.class.php'; +require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; + +class Inventory extends CoreObject +{ + + public $element='inventory'; + public $table_element='inventory'; + public $fk_element='fk_inventory'; + protected $childtables=array('inventorydet'); // To test if we can delete object + protected $isnolinkedbythird = 1; // No field fk_soc + protected $ismultientitymanaged = 1; // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe + + /** + * Warehouse Id + * @var int + */ + public $fk_warehouse; + /** + * Entity Id + * @var int + */ + public $entity; + + /** + * Status + * @var int + */ + public $status; + /** + * Inventory Date + * @var date + */ + public $date_inventory; + /** + * Inventory Title + * @var string + */ + public $title; + + private $__fields=array( + 'fk_warehouse'=>array('type'=>'integer','index'=>true) + ,'entity'=>array('type'=>'integer','index'=>true) + ,'status'=>array('type'=>'integer','index'=>true) + ,'date_inventory'=>array('type'=>'date') + ,'title'=>array('type'=>'string') + ); + + function __construct() + { + + parent::init(); + + $this->_init_vars(); + + $this->start(); + + $this->setChild('Inventorydet','fk_inventory'); + + $this->status = 0; + $this->entity = $conf->entity; + $this->errors = array(); + $this->amount = 0; + + } + + function sort_det() + { + usort($this->Inventorydet, array('Inventory', 'customSort')); + } + + function load(&$PDOdb, $id,$annexe = true) + { + + if(!$annexe) $this->withChild = false; + + $res = parent::load($PDOdb, $id); + $this->sort_det(); + + $this->amount = 0; + foreach($this->Inventorydet as &$det){ + $this->amount+=$det->qty_view * $det->pmp; + } + + return $res; + } + + + function customSort(&$objA, &$objB) + { + global $db; + + $r = strcmp(strtoupper(trim($objA->product->ref)), strtoupper(trim($objB->product->ref))); + + if ($r < 0) $r = -1; + elseif ($r > 0) $r = 1; + else $r = 0; + + return $r; + } + + function changePMP(&$PDOdb) { + + foreach ($this->Inventorydet as $k => &$Inventorydet) + { + + if($Inventorydet->new_pmp>0) { + $Inventorydet->pmp = $Inventorydet->new_pmp; + $Inventorydet->new_pmp = 0; + + $PDOdb->Execute("UPDATE ".MAIN_DB_PREFIX."product as p SET pmp = ".$Inventorydet->pmp." + WHERE rowid = ".$Inventorydet->fk_product ); + + if((float)DOL_VERSION<4.0) { + + $PDOdb->Execute("UPDATE ".MAIN_DB_PREFIX."product_stock SET pmp=".$Inventorydet->pmp." + WHERE fk_entrepot = ".$this->fk_warehouse." AND fk_product = ".$Inventorydet->fk_product) ; + + } + + } + } + + parent::save($PDOdb); + + } + + function save(&$PDOdb) + { + //si on valide l'inventaire on sauvegarde le stock à cette instant + if ($this->status) + { + $this->regulate($PDOdb); + } + + parent::save($PDOdb); + } + + function set_values($Tab) + { + global $db,$langs; + + if (isset($Tab['qty_to_add'])) + { + foreach ($Tab['qty_to_add'] as $k => $qty) + { + $qty = (float) price2num($qty); + + if ($qty < 0) + { + $this->errors[] = $langs->trans('inventoryErrorQtyAdd'); + return 0; + } + + $product = new Product($db); + $product->fetch($this->Inventorydet[$k]->fk_product); + + $this->Inventorydet[$k]->pmp = $product->pmp; + $this->Inventorydet[$k]->qty_view += $qty; + } + } + + return parent::set_values($Tab); + } + + function deleteAllLine(&$PDOdb) { + + foreach($this->Inventorydet as &$det) { + $det->to_delete = true; + } + + $this->save($PDOdb); + + $this->Inventorydet=array(); + + } + + function add_product(&$PDOdb, $fk_product, $fk_entrepot='') { + + $k = $this->addChild($PDOdb, 'Inventorydet'); + $det = &$this->Inventorydet[$k]; + + $det->fk_inventory = $this->getId(); + $det->fk_product = $fk_product; + $det->fk_warehouse = empty($fk_entrepot) ? $this->fk_warehouse : $fk_entrepot; + + $det->load_product(); + + $date = $this->get_date('date_inventory', 'Y-m-d'); + if(empty($date))$date = $this->get_date('date_cre', 'Y-m-d'); + $det->setStockDate($PDOdb, $date , $fk_entrepot); + + } + + function correct_stock($fk_product, $id_entrepot, $nbpiece, $movement, $label='', $price=0, $inventorycode='') + { + global $conf, $db, $langs, $user; + + /* duplication method product to add datem */ + if ($id_entrepot) + { + $db->begin(); + + require_once DOL_DOCUMENT_ROOT .'/product/stock/class/mouvementstock.class.php'; + + $op[0] = "+".trim($nbpiece); + $op[1] = "-".trim($nbpiece); + + $datem = empty($conf->global->INVENTORY_USE_INVENTORY_DATE_FROM_DATEMVT) ? dol_now() : $this->date_inventory; + + $movementstock=new MouvementStock($db); + $result=$movementstock->_create($user,$fk_product,$id_entrepot,$op[$movement],$movement,$price,$label,$inventorycode, $datem); + + if ($result >= 0) + { + $db->commit(); + return 1; + } + else + { + $this->error=$movementstock->error; + $this->errors=$movementstock->errors; + + $db->rollback(); + return -1; + } + } + } + + function regulate(&$PDOdb) + { + global $db,$user,$langs,$conf; + + if($conf->global->INVENTORY_DISABLE_VIRTUAL){ + $pdt_virtuel = false; + // Test si pdt virtuel est activé + if($conf->global->PRODUIT_SOUSPRODUITS) + { + $pdt_virtuel = true; + $conf->global->PRODUIT_SOUSPRODUITS = 0; + } + } + + foreach ($this->Inventorydet as $k => $Inventorydet) + { + $product = new Product($db); + $product->fetch($Inventorydet->fk_product); + + /* + * Ancien code qui était pourri et qui modifié la valeur du stock théorique si le parent était déstocké le même jour que l'enfant + * + * $product->load_stock(); + $Inventorydet->qty_stock = $product->stock_warehouse[$this->fk_warehouse]->real; + + if(date('Y-m-d', $this->date_inventory) < date('Y-m-d')) { + $TRes = $Inventorydet->getPmpStockFromDate($PDOdb, date('Y-m-d', $this->date_inventory), $this->fk_warehouse); + $Inventorydet->qty_stock = $TRes[1]; + } + */ + if ($Inventorydet->qty_view != $Inventorydet->qty_stock) + { + $Inventorydet->qty_regulated = $Inventorydet->qty_view - $Inventorydet->qty_stock; + $nbpiece = abs($Inventorydet->qty_regulated); + $movement = (int) ($Inventorydet->qty_view < $Inventorydet->qty_stock); // 0 = add ; 1 = remove + + $href = dol_buildpath('/inventory/inventory.php?id='.$this->getId().'&action=view', 1); + + if(empty($this->title)) + $this->correct_stock($product->id, $Inventorydet->fk_warehouse, $nbpiece, $movement, $langs->trans('inventoryMvtStock', $href, $this->getId())); + else + $this->correct_stock($product->id, $Inventorydet->fk_warehouse, $nbpiece, $movement, $langs->trans('inventoryMvtStockWithNomInventaire', $href, $this->title)); + } + } + + if($conf->global->INVENTORY_DISABLE_VIRTUAL){ + // Test si pdt virtuel était activé avant la régule + if($pdt_virtuel) $conf->global->PRODUIT_SOUSPRODUITS = 1; + } + + return 1; + } + + static function getLink($id) { + global $langs; + + $PDOdb=new TPDOdb; + + $i = new Inventory; + $i->load($PDOdb, $id, false); + + $title = !empty($i->title) ? $i->title : $langs->trans('inventoryTitle').' '.$i->getId(); + + return ''.img_picto('','object_list.png','',0).' '.$title.''; + + } +} + +class Inventorydet extends TObjetStd +{ + function __construct() + { + global $conf; + + $this->set_table(MAIN_DB_PREFIX.'inventorydet'); + $this->TChamps = array(); + $this->add_champs('fk_inventory,fk_warehouse,fk_product,entity', 'type=entier;'); + $this->add_champs('qty_view,qty_stock,qty_regulated,pmp,pa,new_pmp', 'type=float;'); + + $this->_init_vars(); + $this->start(); + + $this->entity = $conf->entity; + $this->errors = array(); + + $this->product = null; + + $this->current_pa = 0; + + } + + function load(&$PDOdb, $id) + { + global $conf; + + $res = parent::load($PDOdb, $id); + $this->load_product(); + $this->fetch_current_pa(); + + return $res; + } + + function fetch_current_pa() { + global $db,$conf; + + if(empty($conf->global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA)) return false; + + if($this->pa>0){ + $this->current_pa = $this->pa; + } + else { + + dol_include_once('/fourn/class/fournisseur.product.class.php'); + $p= new ProductFournisseur($db); + $p->find_min_price_product_fournisseur($this->fk_product); + + if($p->fourn_qty>0) $this->current_pa = $p->fourn_price / $p->fourn_qty; + } + return true; + } + + function setStockDate(&$PDOdb, $date, $fk_warehouse) { + + list($pmp,$stock) = $this->getPmpStockFromDate($PDOdb, $date, $fk_warehouse); + + $this->qty_stock = $stock; + $this->pmp = $pmp; + + $last_pa = 0; + $sql = "SELECT price FROM ".MAIN_DB_PREFIX."stock_mouvement + WHERE fk_entrepot=".$fk_warehouse." + AND fk_product=".$this->fk_product." + AND (origintype='order_supplier' || origintype='invoice_supplier') + AND price>0 + AND datem<='".$date." 23:59:59' + ORDER BY datem DESC LIMIT 1"; + + $PDOdb->Execute($sql); + + if($obj = $PDOdb->Get_line()) { + $last_pa = $obj->price; + } + + $this->pa = $last_pa; + /* var_dump($fk_warehouse,$this->product->stock_warehouse,$this->pmp, $this->pa, $this->qty_stock); + exit;*/ + } + + function getPmpStockFromDate(&$PDOdb, $date, $fk_warehouse){ + + $res = $this->product->load_stock(); + + if($res>0) { + $stock = isset($this->product->stock_warehouse[$fk_warehouse]->real) ? $this->product->stock_warehouse[$fk_warehouse]->real : 0; + + if((float)DOL_VERSION<4.0) { + $pmp = isset($this->product->stock_warehouse[$fk_warehouse]->pmp) ? $this->product->stock_warehouse[$fk_warehouse]->pmp : 0; + } + else{ + $pmp = $this->product->pmp; + } + + } + + //On récupère tous les mouvements de stocks du produit entre aujourd'hui et la date de l'inventaire + $sql = "SELECT value, price + FROM ".MAIN_DB_PREFIX."stock_mouvement + WHERE fk_product = ".$this->product->id." + AND fk_entrepot = ".$fk_warehouse." + AND datem > '".date('Y-m-d 23:59:59',strtotime($date))."' + ORDER BY datem DESC"; + + //echo $sql.'
'; + $PDOdb->Execute($sql); + $TMouvementStock = $PDOdb->Get_All(); + $laststock = $stock; + $lastpmp = $pmp; + //Pour chacun des mouvements on recalcule le PMP et le stock physique + foreach($TMouvementStock as $mouvement){ + + //150 + //if($this->product->id==394) echo 'laststock = '.$stock.'
'; + + //9.33 + //if($this->product->id==394) echo 'lastpmp = '.$pmp.'
'; + $price = ($mouvement->price>0 && $mouvement->value>0) ? $mouvement->price : $lastpmp; + + $stock_value = $laststock * $lastpmp; + + $laststock -= $mouvement->value; + + $last_stock_value = $stock_value - ($mouvement->value * $price); + + $lastpmp = ($laststock != 0) ? $last_stock_value / $laststock : $lastpmp; + + + + } + + return array($lastpmp,$laststock); + } + + function load_product() + { + global $db; + + if($this->fk_product>0) { + $this->product = new Product($db); + $this->product->fetch($this->fk_product); + } + + } + +}