* Copyright (C) 2005 Davoleau Brice * Copyright (C) 2005 Rodolphe Quiedeville * Copyright (C) 2006-2007 Regis Houssin * Copyright (C) 2006-2008 Laurent Destailleur * Copyright (C) 2007 Patrick Raguin * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** \file htdocs/categories/categorie.class.php \ingroup categorie \brief Fichier de la classe des categorie \version $Id$ */ require_once(DOL_DOCUMENT_ROOT."/product.class.php"); require_once(DOL_DOCUMENT_ROOT."/fourn/fournisseur.class.php"); /** \class Categorie \brief Classe permettant la gestion des categories */ class Categorie { var $error; var $db; var $id; var $id_mere; var $label; var $description; var $statut; var $type; // 0=Produit, 1=Fournisseur, 2=Tiers var $cats=array(); // Tableau en memoire des categories var $motherof = array(); // Tableau des correspondances id_fille -> id_mere /** * Constructeur * db : accès base de données * id : id de la catégorie */ function Categorie($db, $id=-1) { $this->db = $db; $this->id = $id; if ($id != -1) $this->fetch ($this->id); } /** * Charge la catégorie * id : id de la catégorie à charger */ function fetch($id) { $sql = "SELECT rowid, label, description, visible, type"; $sql.= " FROM ".MAIN_DB_PREFIX."categorie"; $sql.= " WHERE rowid = ".$id; dolibarr_syslog("Categorie::fetch sql=".$sql); $resql = $this->db->query ($sql); if ($resql) { $res = $this->db->fetch_array($resql); $this->id = $res['rowid']; $this->label = $res['label']; $this->description = $res['description']; $this->visible = $res['visible']; $this->type = $res['type']; $this->db->free($resql); } else { dolibarr_print_error ($this->db); return -1; } $sql = "SELECT fk_categorie_mere"; $sql.= " FROM ".MAIN_DB_PREFIX."categorie_association"; $sql.= " WHERE fk_categorie_fille = '".$id."'"; dolibarr_syslog("Categorie::fetch sql=".$sql); $resql = $this->db->query ($sql); if ($resql) { $res = $this->db->fetch_array($resql); $this->id_mere = $res['fk_categorie_mere']; return $this->id; } else { dolibarr_print_error ($this->db); return -1; } } /** * Ajoute la catégorie dans la base de données * retour : -1 : erreur SQL * -2 : nouvel ID inconnu * -3 : catégorie invalide */ function create() { global $langs; $langs->load('categories'); if ($this->already_exists ()) { $this->error=$langs->trans("ImpossibleAddCat"); $this->error.=" : ".$langs->trans("CategoryExistsAtSameLevel"); return -1; } $sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie (label, description, visible,type) "; $sql .= "VALUES ('".addslashes($this->label)."', '".addslashes($this->description)."', '".$this->visible."',".$this->type.")"; $res = $this->db->query ($sql); if ($res) { $id = $this->db->last_insert_id (MAIN_DB_PREFIX."categorie"); if ($id > 0) { $this->id = $id; if($this->id_mere != "") { if($this->add_fille() < 0) { $this->error=$langs->trans("ImpossibleAssociateCategory"); return -1; } } return $id; } else { return -2; } } else { dolibarr_print_error ($this->db); return -1; } } /** * Mise à jour de la catégorie * retour : 1 : OK * -1 : erreur SQL * -2 : catégorie invalide */ function update() { // Clean parameters $this->label=trim($this->label); $this->description=trim($this->description); $this->db->begin(); $sql = 'delete from '.MAIN_DB_PREFIX.'categorie_association'; $sql .= ' WHERE fk_categorie_fille = "'.$this->id.'"'; dolibarr_syslog("Categorie::update sql=".$sql); if (! $this->db->query($sql)) { $this->db->rollback(); dolibarr_print_error($this->db); return -1; } if($this->id_mere !="" && $this->id_mere!=$this->id) { $sql = 'insert into '.MAIN_DB_PREFIX.'categorie_association(fk_categorie_mere,fk_categorie_fille)'; $sql .= ' VALUES ("'.$this->id_mere.'","'.$this->id.'")'; dolibarr_syslog("Categorie::update sql=".$sql); if (! $this->db->query($sql)) { $this->db->rollback(); dolibarr_print_error($this->db); return -1; } } $sql = "UPDATE ".MAIN_DB_PREFIX."categorie"; $sql.= " SET label = '".addslashes($this->label)."'"; if ($this->description) { $sql .= ", description = '".addslashes($this->description)."'"; } $sql .= ", visible = '".$this->visible."'"; $sql .= " WHERE rowid = ".$this->id; dolibarr_syslog("Categorie::update sql=".$sql); if ($this->db->query($sql)) { $this->db->commit(); return 1; } else { $this->db->rollback(); dolibarr_print_error($this->db); return -1; } } /** * Supprime la catégorie * Les produits et sous-catégories deviennent orphelins * si $all = false, et sont (seront :) supprimés sinon */ function remove ($all = false) { $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_product "; $sql .= "WHERE fk_categorie = ".$this->id; if (!$this->db->query($sql)) { dolibarr_print_error($this->db); return -1; } $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_association "; $sql .= "WHERE fk_categorie_mere = ".$this->id; $sql .= " OR fk_categorie_fille = ".$this->id; if (!$this->db->query($sql)) { dolibarr_print_error($this->db); return -1; } $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie "; $sql .= "WHERE rowid = ".$this->id; if (!$this->db->query($sql)) { dolibarr_print_error($this->db); return -1; } else { return 1; } } /** * Ajout d'une sous-catégorie * $fille : objet catégorie * retour : 1 : OK * -2 : $fille est déjà dans la famille de $this * -3 : catégorie ($this ou $fille) invalide */ function add_fille() { $sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie_association (fk_categorie_mere, fk_categorie_fille)"; $sql.= " VALUES (".$this->id_mere.", ".$this->id.")"; if ($this->db->query($sql)) { return 1; } else { dolibarr_print_error($this->db); return -1; } } /** * Suppression d'une sous-catégorie (seulement "désassociation") * $fille : objet catégorie * retour : 1 : OK * -3 : catégorie ($this ou $fille) invalide */ function del_fille($fille) { if (!$this->check() || !$fille->check()) { return -3; } $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_association"; $sql .= " WHERE fk_categorie_mere = ".$this->id." and fk_categorie_fille = ".$fille->id; if ($this->db->query($sql)) { return 1; } else { $this->error=$this->db->error().' sql='.$sql; return -1; } } /** * \brief Link an object to the category * \param obj Object to link to category * \param type Type of category * \return int 1 : OK, -1 : erreur SQL, -2 : id non renseign, -3 : Already linked */ function add_type($obj,$type) { if ($this->id == -1) { return -2; } $sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie_".$type." (fk_categorie, fk_".($type=='fournisseur'?'societe':$type).")"; $sql .= " VALUES (".$this->id.", ".$obj->id.")"; if ($this->db->query($sql)) { return 1; } else { if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') { $this->error=$this->db->lasterrno(); return -3; } else { $this->error=$this->db->error().' sql='.$sql; } return -1; } } /** * Suppresion d'un produit de la catégorie * @param $prod est un objet de type produit * retour : 1 : OK * -1 : erreur SQL */ function del_type($obj,$type) { $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_".$type; $sql .= " WHERE fk_categorie = ".$this->id; $sql .= " AND fk_".($type=='fournisseur'?'societe':$type)." = ".$obj->id; if ($this->db->query($sql)) { return 1; } else { $this->error=$this->db->error().' sql='.$sql; return -1; } } /** * Retourne les produits de la catégorie */ function get_type($type,$class) { $sql = "SELECT fk_".$type." FROM ".MAIN_DB_PREFIX."categorie_".$type." "; $sql .= "WHERE fk_categorie = ".$this->id; $res = $this->db->query($sql); if ($res) { $obj = array(); while ($rec = $this->db->fetch_array ($res)) { $obj = new $class ($this->db, $rec['fk_'.$type]); $obj->fetch ($obj->id); $objs[] = $obj; } return $objs; } else { $this->error=$this->db->error().' sql='.$sql; return -1; } } /** * Retourne les filles de la catégorie */ function get_filles () { $sql = "SELECT fk_categorie_fille FROM ".MAIN_DB_PREFIX."categorie_association "; $sql .= "WHERE fk_categorie_mere = ".$this->id; $res = $this->db->query ($sql); if ($res) { $cats = array (); while ($rec = $this->db->fetch_array ($res)) { $cat = new Categorie ($this->db, $rec['fk_categorie_fille']); $cats[] = $cat; } return $cats; } else { dolibarr_print_error ($this->db); return -1; } } /** * retourne la description d'une catégorie */ function get_desc ($cate) { $sql = "SELECT description FROM ".MAIN_DB_PREFIX."categorie "; $sql .= "WHERE rowid = '".$cate."'"; $res = $this->db->query ($sql); $n = $this->db->fetch_array ($res); return ($n[0]); } /** * La catégorie $fille est-elle une fille de cette catégorie ? */ function is_fille ($fille) { $sql = "SELECT count(fk_categorie_fille) FROM ".MAIN_DB_PREFIX."categorie_association "; $sql .= "WHERE fk_categorie_mere = ".$this->id." AND fk_categorie_fille = ".$fille->id; $res = $this->db->query ($sql); $n = $this->db->fetch_array ($res); return ($n[0] > 0); } /** * \brief Reconstruit l'arborescence des catégories sous la forme d'un tableau * Renvoi un tableau de tableau('id','id_mere',...) trié selon * arbre et avec: * id = id de la categorie * id_mere = id de la categorie mere * id_children = tableau des id enfant * label = nom de la categorie * fulllabel = nom avec chemin complet de la categorie * fullpath = chemin complet compose des id * \param type Type de categories (0=produit, 1=fournisseur, 2=client) * \return array Tableau de array */ function get_full_arbo($type) { // Charge tableau des meres $sql = "SELECT fk_categorie_mere as id_mere, fk_categorie_fille as id_fille"; $sql.= " FROM ".MAIN_DB_PREFIX."categorie_association"; dolibarr_syslog("Categorie::get_full_arbo sql=".$sql); $resql = $this->db->query ($sql); if ($resql) { while ($obj= $this->db->fetch_object($resql)) { $this->motherof[$obj->id_fille]=$obj->id_mere; } } else { dolibarr_print_error ($this->db); return -1; } // Charge tableau des categories $sql = "SELECT c.rowid, c.label as label, ca.fk_categorie_fille as rowid_fille"; $sql.= " FROM ".MAIN_DB_PREFIX."categorie as c"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."categorie_association as ca"; $sql.= " ON c.rowid=ca.fk_categorie_mere"; $sql.= " WHERE c.type = ".$type; $sql.= " ORDER BY c.label, c.rowid"; dolibarr_syslog("Categorie::get_full_arbo sql=".$sql); $resql = $this->db->query($sql); if ($resql) { $this->cats = array(); $i=0; while ($obj = $this->db->fetch_object($resql)) { $this->cats[$obj->rowid]['id'] = $obj->rowid; $this->cats[$obj->rowid]['id_mere'] = $this->motherof[$obj->rowid]; $this->cats[$obj->rowid]['label'] = $obj->label; if ($obj->rowid_fille) { if (is_array($this->cats[$obj->rowid]['id_children'])) { $newelempos=sizeof($this->cats[$obj->rowid]['id_children']); //print "this->cats[$i]['id_children'] est deja un tableau de $newelem elements
"; $this->cats[$obj->rowid]['id_children'][$newelempos]=$obj->rowid_fille; } else { //print "this->cats[".$obj->rowid."]['id_children'] n'est pas encore un tableau
"; $this->cats[$obj->rowid]['id_children']=array($obj->rowid_fille); } } $i++; } } else { dolibarr_print_error ($this->db); return -1; } // On ajoute la propriete fullpath a tous les éléments foreach($this->cats as $key => $val) { if (isset($motherof[$key])) continue; $this->build_path_from_id_categ($key,0); } $this->cats=dol_sort_array($this->cats, 'fulllabel', 'asc', true, false); //$this->debug_cats(); return $this->cats; } /** * \brief Calcule les propriétés fullpath et fulllabel d'une categorie * du tableau this->cats et de toutes ces enfants * \param id_categ id_categ entry to update * \param protection Deep counter to avoid infinite loop */ function build_path_from_id_categ($id_categ,$protection=0) { // Defini fullpath if (isset($this->cats[$id_categ]['id_mere'])) { $this->cats[$id_categ]['fullpath'] =$this->cats[$this->cats[$id_categ]['id_mere']]['fullpath']; $this->cats[$id_categ]['fullpath'].='_'.$id_categ; $this->cats[$id_categ]['fulllabel'] =$this->cats[$this->cats[$id_categ]['id_mere']]['fulllabel']; $this->cats[$id_categ]['fulllabel'].=' >> '.$this->cats[$id_categ]['label']; } else { $this->cats[$id_categ]['fullpath']='_'.$id_categ; $this->cats[$id_categ]['fulllabel']=$this->cats[$id_categ]['label']; } // We count number of _ to have level $this->cats[$id_categ]['level']=strlen(eregi_replace('[^_]','',$this->cats[$id_categ]['fullpath'])); // Traite ces enfants $protection++; if ($protection > 20) return; // On ne traite pas plus de 20 niveaux if (! is_array($this->cats[$id_categ]['id_children'])) return; foreach($this->cats[$id_categ]['id_children'] as $key => $val) { $this->build_path_from_id_categ($val,$protection); } return; } /** * \brief Affiche contenu de $this->cats */ function debug_cats() { // Affiche $this->cats foreach($this->cats as $key => $val) { print 'id: '.$this->cats[$key]['id']; print ' label: '.$this->cats[$key]['label']; print ' mother: '.$this->cats[$key]['id_mere']; print ' children: '.(is_array($this->cats[$key]['id_children'])?join(',',$this->cats[$key]['id_children']):''); print ' fullpath: '.$this->cats[$key]['fullpath']; print ' fulllabel: '.$this->cats[$key]['fulllabel']; print "
\n"; } } /** * \brief Retourne toutes les catégories * \return array Tableau d'objet Categorie */ function get_all_categories () { $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie"; $res = $this->db->query ($sql); if ($res) { $cats = array (); while ($record = $this->db->fetch_array ($res)) { $cat = new Categorie ($this->db, $record['rowid']); $cats[$record['rowid']] = $cat; } return $cats; } else { dolibarr_print_error ($this->db); return -1; } } /** * \brief Retourne le nombre total de catégories * \return int Nombre de categories */ function get_nb_categories () { $sql = "SELECT count(rowid)"; $sql.= " FROM ".MAIN_DB_PREFIX."categorie"; $res = $this->db->query ($sql); if ($res) { $res = $this->db->fetch_array($res); return $res[0]; } else { dolibarr_print_error ($this->db); return -1; } } /** * Vérifie si une catégorie porte le label $label */ function already_exists() { $sql = "SELECT count(c.rowid)"; $sql.= " FROM ".MAIN_DB_PREFIX."categorie as c, ".MAIN_DB_PREFIX."categorie_association as ca"; $sql.= " WHERE c.label = '".addslashes($this -> label)."' AND type=".$this->type; if($this -> id_mere != "") { $sql.= " AND c.rowid = ca.fk_categorie_fille"; $sql.= " AND ca.fk_categorie_mere = '".$this -> id_mere."'"; } $res = $this->db->query ($sql); if($res) { $res = $this->db->fetch_array ($res); if($res[0] > 0) return true; else return false; } else { dolibarr_print_error ($this->db); return -1; } } /** * \brief Retourne les catégories de premier niveau (qui ne sont pas filles) */ function get_main_categories () { $allcats = $this->get_all_categories (); $maincats = array (); $filles = array (); $sql = "SELECT fk_categorie_fille FROM ".MAIN_DB_PREFIX."categorie_association"; $res = $this->db->query ($sql); while ($res = $this->db->fetch_array ($res)) { $filles[] = $res['fk_categorie_fille']; } foreach ($allcats as $cat) { if (!in_array ($cat->id, $filles)) { $maincats[] = $cat; } else { } } return $maincats; } /** * Retourne les chemin de la catégorie, avec les noms des catégories * séparés par $sep (" >> " par défaut) */ function print_all_ways ($sep = " >> ", $url='') { $ways = array (); foreach ($this->get_all_ways () as $way) { $w = array (); foreach ($way as $cat) { if ($url == '') { $w[] = "".$cat->label.""; } else { $w[] = "".$cat->label.""; } } $ways[] = implode ($sep, $w); } return $ways; } /** * get_primary_way() affiche le chemin le plus court pour se rendre à un produit */ function get_primary_way($id, $type="") { $primary_way = Array("taille"=>-1,"chemin"=>Array()); $meres = $this->containing($id,$type); foreach ($meres as $mere) { foreach ($mere->get_all_ways() as $way) { if(sizeof($way)<$primary_way["taille"] || $primary_way["taille"]<0) { $primary_way["taille"] = sizeOf($way); $primary_way["chemin"] = $way; } } } return $primary_way["chemin"]; } /** * print_primary_way() affiche le chemin le plus court pour se rendre à un produit */ function print_primary_way($id, $sep= " >> ", $url, $type="") { $primary_way = Array(); $way = $this->get_primary_way($id,$type); $w = array(); foreach ($way as $cat) { if ($url == '') { $w[] = "".$cat->label.""; } else { $w[] = "".$cat->label.""; } } return implode($sep, $w); } /** * Retourne un tableau contenant la liste des catégories mères */ function get_meres () { $meres = array (); $sql = "SELECT fk_categorie_mere FROM ".MAIN_DB_PREFIX."categorie_association "; $sql .= "WHERE fk_categorie_fille = ".$this->id; $res = $this->db->query ($sql); while ($cat = $this->db->fetch_array ($res)) { $meres[] = new Categorie ($this->db, $cat['fk_categorie_mere']); } return $meres; } /** * Retourne dans un tableau tous les chemins possibles pour arriver à la catégorie * en partant des catégories principales, représentés par des tableaux de catégories */ function get_all_ways () { $ways = array (); foreach ($this->get_meres () as $mere) { foreach ($mere->get_all_ways () as $way) { $w = $way; $w[] = $this; $ways[] = $w; } } if (sizeof ($ways) == 0) $ways[0][0] = $this; return $ways; } /** * Retourne les catégories contenant le produit $id */ function containing ($id,$type,$typeid) { $cats = array (); $sql = "SELECT ct.fk_categorie"; $sql.= " FROM ".MAIN_DB_PREFIX."categorie_".$type." as ct"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."categorie as c ON ct.fk_categorie = c.rowid"; $sql.= " WHERE ct.fk_".($type=='fournisseur'?'societe':$type)." = ".$id." AND c.type = ".$typeid; $res = $this->db->query ($sql); if ($res) { while ($cat = $this->db->fetch_array ($res)) { $cats[] = new Categorie ($this->db, $cat['fk_categorie']); } return $cats; } else { dolibarr_print_error ($this->db); return -1; } } /** * Retourne les catégories contenant le produit $ref */ function containing_ref ($ref,$type) { $cats = array (); $sql = "SELECT c.fk_categorie, c.fk_".$type.", p.rowid, p.ref"; $sql.= " FROM ".MAIN_DB_PREFIX."categorie_".$type." as c, ".MAIN_DB_PREFIX.$type." as p"; $sql.= " WHERE p.ref = '".$ref."' AND c.fk_".$type." = p.rowid"; $res = $this->db->query ($sql); if ($res) { while ($cat = $this->db->fetch_array ($res)) { $cats[] = new Categorie ($this->db, $cat['fk_categorie']); } return $cats; } else { dolibarr_print_error ($this->db); return -1; } } /** * \brief Vérifie le type de la catégorie * */ function verify_type($id) { $sql = "SELECT type FROM ".MAIN_DB_PREFIX."categorie "; $sql .= "WHERE rowid = ".$id." "; $result = $this->db->query ($sql); if ($result) { $obj = $this->db->fetch_object($result); return $obj->type; } else { $this->error=$this->db->error().' sql='.$sql; dolibarr_print_error('',$this->error); return -1; } } /** * \brief Retourne les catégories dont l'id ou le nom correspond * ajoute des wildcards au nom sauf si $exact = true */ function rechercher($id, $nom, $type, $exact = false) { $cats = array (); // Generation requete recherche $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie "; $sql .= "WHERE type = ".$type." "; if ($nom) { if (! $exact) { $nom = '%'.str_replace ('*', '%', $nom).'%'; } $sql.= "AND label LIKE '".$nom."'"; } if ($id) { $sql.="AND rowid = '".$id."'"; } $res = $this->db->query ($sql); if ($res) { while ($id = $this->db->fetch_array ($res)) { $cats[] = new Categorie ($this->db, $id['rowid']); } return $cats; } else { $this->error=$this->db->error().' sql='.$sql; //dolibarr_syslog($this->error); dolibarr_print_error('',$this->error); return -1; } } } ?>