Passed
Branch develop (30d2c8)
by
unknown
26:49
created

Categorie::get_all_ways()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 12
nc 4
nop 0
dl 0
loc 21
rs 9.5555
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) 2005       Matthieu Valleton       <[email protected]>
3
 * Copyright (C) 2005       Davoleau Brice          <[email protected]>
4
 * Copyright (C) 2005       Rodolphe Quiedeville    <[email protected]>
5
 * Copyright (C) 2006-2012  Regis Houssin           <[email protected]>
6
 * Copyright (C) 2006-2012  Laurent Destailleur     <[email protected]>
7
 * Copyright (C) 2007       Patrick Raguin          <[email protected]>
8
 * Copyright (C) 2013-2016  Juanjo Menent           <[email protected]>
9
 * Copyright (C) 2013-2018  Philippe Grand          <[email protected]>
10
 * Copyright (C) 2015       Marcos García           <[email protected]>
11
 * Copyright (C) 2015       Raphaël Doursenaud      <[email protected]>
12
 * Copyright (C) 2016       Charlie Benke           <[email protected]>
13
 * Copyright (C) 2018-2019  Frédéric France         <[email protected]>
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 3 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
27
 */
28
29
/**
30
 *	\file       htdocs/categories/class/categorie.class.php
31
 *	\ingroup    categorie
32
 *	\brief      File of class to manage categories
33
 */
34
35
require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
36
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
37
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.class.php';
38
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
39
40
41
/**
42
 *	Class to manage categories
43
 */
44
class Categorie extends CommonObject
45
{
46
	// Categories types (we use string because we want to accept any modules/types in a future)
47
	const TYPE_PRODUCT   = 'product';
48
	const TYPE_SUPPLIER  = 'supplier';
49
	const TYPE_CUSTOMER  = 'customer';
50
	const TYPE_MEMBER    = 'member';
51
	const TYPE_CONTACT   = 'contact';
52
	const TYPE_USER      = 'user';
53
	const TYPE_PROJECT   = 'project';
54
	const TYPE_ACCOUNT   = 'bank_account';
55
	const TYPE_BANK_LINE = 'bank_line';
56
	const TYPE_WAREHOUSE = 'warehouse';
57
	const TYPE_ACTIONCOMM = 'actioncomm';
58
	const TYPE_WEBSITE_PAGE = 'website_page';
59
60
	/**
61
	 * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
62
	 */
63
	public $picto = 'category';
64
65
66
	/**
67
	 * @var array Table of mapping between type string and ID used for field 'type' in table llx_categories
68
	 */
69
	protected $MAP_ID = array(
70
		'product'      => 0,
71
		'supplier'     => 1,
72
		'customer'     => 2,
73
		'member'       => 3,
74
		'contact'      => 4,
75
		'bank_account' => 5,
76
		'project'      => 6,
77
		'user'         => 7,
78
		'bank_line'    => 8,
79
		'warehouse'    => 9,
80
		'actioncomm'   => 10,
81
		'website_page' => 11
82
	);
83
84
	/**
85
	 * @var array Code mapping from ID
86
	 *
87
	 * @note This array should be removed in future, once previous constants are moved to the string value. Deprecated
88
	 */
89
	public static $MAP_ID_TO_CODE = array(
90
		0 => 'product',
91
		1 => 'supplier',
92
		2 => 'customer',
93
		3 => 'member',
94
		4 => 'contact',
95
		5 => 'bank_account',
96
		6 => 'project',
97
		7 => 'user',
98
		8 => 'bank_line',
99
		9 => 'warehouse',
100
		10 => 'actioncomm',
101
        11 => 'website_page'
102
	);
103
104
	/**
105
	 * @var array Foreign keys mapping from type string when value does not match
106
	 *
107
	 * @todo Move to const array when PHP 5.6 will be our minimum target
108
	 */
109
	protected $MAP_CAT_FK = array(
110
		'customer' => 'soc',
111
		'supplier' => 'soc',
112
		'contact'  => 'socpeople',
113
		'bank_account' => 'account',
114
	);
115
116
	/**
117
	 * @var array Category tables mapping from type string (llx_categorie_...) when value does not match
118
	 *
119
	 * @note Move to const array when PHP 5.6 will be our minimum target
120
	 */
121
	protected $MAP_CAT_TABLE = array(
122
		'customer' => 'societe',
123
		'supplier' => 'fournisseur',
124
		'bank_account'=> 'account',
125
	);
126
127
	/**
128
	 * @var array Object class mapping from type string
129
	 *
130
	 * @note Move to const array when PHP 5.6 will be our minimum target
131
	 */
132
	protected $MAP_OBJ_CLASS = array(
133
		'product'  => 'Product',
134
		'customer' => 'Societe',
135
		'supplier' => 'Fournisseur',
136
		'member'   => 'Adherent',
137
		'contact'  => 'Contact',
138
		'user'     => 'User',
139
		'account'  => 'Account', // old for bank account
140
		'bank_account'  => 'Account',
141
		'project'  => 'Project',
142
		'warehouse'=> 'Entrepot',
143
		'actioncomm' => 'ActionComm',
144
		'website_page' => 'WebsitePage'
145
	);
146
147
	/**
148
	 * @var array Title Area mapping from type string
149
	 *
150
	 * @note Move to const array when PHP 5.6 will be our minimum target
151
	 */
152
	public static $MAP_TYPE_TITLE_AREA = array(
153
		'product' => 'ProductsCategoriesArea',
154
		'customer' => 'CustomersCategoriesArea',
155
		'supplier' => 'SuppliersCategoriesArea',
156
		'member' => 'MembersCategoriesArea',
157
		'contact' => 'ContactsCategoriesArea',
158
		'user' => 'UsersCategoriesArea',
159
		'account' => 'AccountsCategoriesArea', // old for bank account
160
		'bank_account' => 'AccountsCategoriesArea',
161
		'project' => 'ProjectsCategoriesArea',
162
		'warehouse'=> 'StocksCategoriesArea',
163
		'actioncomm' => 'ActioncommCategoriesArea',
164
		'website_page' => 'WebsitePageCategoriesArea'
165
	);
166
167
	/**
168
	 * @var array Object table mapping from type string (table llx_...) when value of key does not match table name.
169
	 *
170
	 * @note Move to const array when PHP 5.6 will be our minimum target
171
	 */
172
	protected $MAP_OBJ_TABLE = array(
173
		'customer' => 'societe',
174
		'supplier' => 'societe',
175
		'member'   => 'adherent',
176
		'contact'  => 'socpeople',
177
		'account'  => 'bank_account', // old for bank account
178
		'project'  => 'projet',
179
		'warehouse'=> 'entrepot'
180
	);
181
182
	/**
183
	 * @var string ID to identify managed object
184
	 */
185
	public $element = 'category';
186
187
	/**
188
	 * @var string Name of table without prefix where object is stored
189
	 */
190
	public $table_element = 'categorie';
191
192
	/**
193
	 * @var int ID
194
	 */
195
	public $fk_parent;
196
197
	/**
198
	 * @var string Category label
199
	 */
200
   	public $label;
201
202
	/**
203
	 * @var string description
204
	 */
205
	public $description;
206
207
	/**
208
	 * @var string     Color
209
	 */
210
	public $color;
211
212
	/**
213
	 * @var int Visible
214
	 */
215
	public $visible;
216
217
	/**
218
	 * @var int		  Id of thirdparty when CATEGORY_ASSIGNED_TO_A_CUSTOMER is set
219
	 */
220
	public $socid;
221
222
	/**
223
	 * @var string	Category type
224
	 *
225
	 * @see Categorie::TYPE_PRODUCT
226
	 * @see Categorie::TYPE_SUPPLIER
227
	 * @see Categorie::TYPE_CUSTOMER
228
	 * @see Categorie::TYPE_MEMBER
229
	 * @see Categorie::TYPE_CONTACT
230
	 * @see Categorie::TYPE_USER
231
	 * @see Categorie::TYPE_PROJECT
232
	 * @see Categorie::TYPE_ACCOUNT
233
	 * @see Categorie::TYPE_BANK_LINE
234
     * @see Categorie::TYPE_WAREHOUSE
235
     * @see Categorie::TYPE_ACTIONCOMM
236
     * @see Categorie::TYPE_WEBSITE_PAGE
237
	 */
238
	public $type;
239
240
	/**
241
	 * @var array Categories table in memory
242
	 */
243
	public $cats = array();
244
245
	/**
246
	 * @var array Mother of table
247
	 */
248
	public $motherof = array();
249
250
	/**
251
	 *	Constructor
252
	 *
253
	 *  @param		DoliDB		$db     Database handler
254
	 */
255
	public function __construct($db)
256
	{
257
		global $hookmanager;
258
259
		$this->db = $db;
260
261
		if (is_object($hookmanager)) {
262
			$hookmanager->initHooks(array('category'));
263
			$parameters = array();
264
			$reshook = $hookmanager->executeHooks('constructCategory', $parameters, $this); // Note that $action and $object may have been modified by some hooks
265
			if ($reshook >= 0 && !empty($hookmanager->resArray)) {
266
				$mapList = $hookmanager->resArray;
267
				$mapId   = $mapList['id'];
268
				$mapCode = $mapList['code'];
269
				self::$MAP_ID_TO_CODE[$mapId]  = $mapCode;
270
				$this->MAP_ID[$mapCode]        = $mapId;
271
				$this->MAP_CAT_FK[$mapCode]    = $mapList['cat_fk'];
272
				$this->MAP_CAT_TABLE[$mapCode] = $mapList['cat_table'];
273
				$this->MAP_OBJ_CLASS[$mapCode] = $mapList['obj_class'];
274
				$this->MAP_OBJ_TABLE[$mapCode] = $mapList['obj_table'];
275
			}
276
		}
277
	}
278
279
	/**
280
	 * Get map list
281
	 *
282
	 * @return	array
283
	 */
284
	public function getMapList()
285
	{
286
		$mapList = array();
287
288
		foreach ($this->MAP_ID as $mapCode => $mapId) {
289
			$mapList[] = array(
290
				'id'        => $mapId,
291
				'code'      => $mapCode,
292
				'cat_fk'    => (empty($this->MAP_CAT_FK[$mapCode]) ? $mapCode : $this->MAP_CAT_FK[$mapCode]),
293
				'cat_table' => (empty($this->MAP_CAT_TABLE[$mapCode]) ? $mapCode : $this->MAP_CAT_TABLE[$mapCode]),
294
				'obj_class' => (empty($this->MAP_OBJ_CLASS[$mapCode]) ? $mapCode : $this->MAP_OBJ_CLASS[$mapCode]),
295
				'obj_table' => (empty($this->MAP_OBJ_TABLE[$mapCode]) ? $mapCode : $this->MAP_OBJ_TABLE[$mapCode])
296
			);
297
		}
298
299
		return $mapList;
300
	}
301
302
	/**
303
	 * 	Load category into memory from database
304
	 *
305
	 * 	@param		int		$id      Id of category
306
	 *  @param		string	$label   Label of category
307
	 *  @param		string	$type    Type of category ('product', '...') or (0, 1, ...)
308
	 *  @param		string	$ref_ext External reference of object
309
	 * 	@return		int				<0 if KO, >0 if OK
310
	 */
311
	public function fetch($id, $label = '', $type = null, $ref_ext = '')
312
	{
313
		global $conf;
314
315
		// Check parameters
316
		if (empty($id) && empty($label) && empty($ref_ext)) return -1;
317
		if (!is_null($type) && !is_numeric($type)) $type = $this->MAP_ID[$type];
318
319
		$sql = "SELECT rowid, fk_parent, entity, label, description, color, fk_soc, visible, type, ref_ext";
320
		$sql .= ", date_creation, tms, fk_user_creat, fk_user_modif";
321
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie";
322
		if ($id > 0) {
323
			$sql .= " WHERE rowid = ".$id;
324
		} elseif (!empty($ref_ext)) {
325
			$sql .= " WHERE ref_ext LIKE '".$this->db->escape($ref_ext)."'";
326
		} else {
327
			$sql .= " WHERE label = '".$this->db->escape($label)."' AND entity IN (".getEntity('category').")";
328
			if (!is_null($type)) $sql .= " AND type = ".((int) $type);
329
		}
330
331
		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
332
		$resql = $this->db->query($sql);
333
		if ($resql) {
334
			if ($this->db->num_rows($resql) > 0) {
335
				$res = $this->db->fetch_array($resql);
336
337
				$this->id = $res['rowid'];
338
				//$this->ref = $res['rowid'];
339
				$this->fk_parent	= (int) $res['fk_parent'];
340
				$this->label		= $res['label'];
341
				$this->description = $res['description'];
342
				$this->color    	= $res['color'];
343
				$this->socid		= (int) $res['fk_soc'];
344
				$this->visible = (int) $res['visible'];
345
				$this->type = (int) $res['type'];
346
				$this->ref_ext = $res['ref_ext'];
347
				$this->entity = (int) $res['entity'];
348
				$this->date_creation = $this->db->jdate($res['date_creation']);
349
				$this->date_modification = $this->db->jdate($res['tms']);
350
				$this->user_creation = (int) $res['fk_user_creat'];
351
				$this->user_modification = (int) $res['fk_user_modif'];
352
353
				// Retrieve all extrafield
354
				// fetch optionals attributes and labels
355
				$this->fetch_optionals();
356
357
				$this->db->free($resql);
358
359
				// multilangs
360
				if (!empty($conf->global->MAIN_MULTILANGS)) $this->getMultiLangs();
361
362
				return 1;
363
			} else {
364
				return 0;
365
			}
366
		} else {
367
			dol_print_error($this->db);
368
			return -1;
369
		}
370
	}
371
372
	/**
373
	 *  Add category into database
374
	 *
375
	 *  @param	User	$user		Object user
376
	 *  @return	int 				-1 : SQL error
377
	 *          					-2 : new ID unknown
378
	 *          					-3 : Invalid category
379
	 * 								-4 : category already exists
380
	 */
381
	public function create($user)
382
	{
383
		global $conf, $langs, $hookmanager;
384
		$langs->load('categories');
385
386
		$type = $this->type;
387
388
		if (!is_numeric($type)) $type = $this->MAP_ID[$type];
389
390
		$error = 0;
391
392
		dol_syslog(get_class($this).'::create', LOG_DEBUG);
393
394
		// Clean parameters
395
		$this->label = trim($this->label);
396
		$this->description = trim($this->description);
397
		$this->color = trim($this->color);
398
		$this->import_key = trim($this->import_key);
399
		$this->ref_ext = trim($this->ref_ext);
400
		if (empty($this->visible)) $this->visible = 0;
401
		$this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
402
403
		if ($this->already_exists()) {
404
			$this->error = $langs->trans("ImpossibleAddCat", $this->label);
405
			$this->error .= " : ".$langs->trans("CategoryExistsAtSameLevel");
406
			dol_syslog($this->error, LOG_WARNING);
407
			return -4;
408
		}
409
410
		$this->db->begin();
411
		$now = dol_now();
412
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie (";
413
		$sql .= "fk_parent,";
414
		$sql .= " label,";
415
		$sql .= " description,";
416
		$sql .= " color,";
417
		if (!empty($conf->global->CATEGORY_ASSIGNED_TO_A_CUSTOMER)) {
418
			$sql .= "fk_soc,";
419
		}
420
		$sql .= " visible,";
421
		$sql .= " type,";
422
		$sql .= " import_key,";
423
		$sql .= " ref_ext,";
424
		$sql .= " entity,";
425
		$sql .= " date_creation,";
426
		$sql .= " fk_user_creat";
427
		$sql .= ") VALUES (";
428
		$sql .= (int) $this->fk_parent.",";
429
		$sql .= "'".$this->db->escape($this->label)."', ";
430
		$sql .= "'".$this->db->escape($this->description)."', ";
431
		$sql .= "'".$this->db->escape($this->color)."', ";
432
		if (!empty($conf->global->CATEGORY_ASSIGNED_TO_A_CUSTOMER)) {
433
			$sql .= ($this->socid > 0 ? $this->socid : 'null').", ";
434
		}
435
		$sql .= "'".$this->db->escape($this->visible)."', ";
436
		$sql .= $this->db->escape($type).", ";
437
		$sql .= (!empty($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : 'null').", ";
438
		$sql .= (!empty($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : 'null').", ";
439
		$sql .= (int) $conf->entity.", ";
440
		$sql .= "'".$this->db->idate($now)."', ";
441
		$sql .= (int) $user->id;
442
		$sql .= ")";
443
444
		$res = $this->db->query($sql);
445
		if ($res) {
446
			$id = $this->db->last_insert_id(MAIN_DB_PREFIX."categorie");
447
448
			if ($id > 0) {
449
				$this->id = $id;
450
451
				$action = 'create';
452
453
				// Actions on extra fields
454
				if (!$error) {
455
					$result = $this->insertExtraFields();
456
					if ($result < 0) {
457
						$error++;
458
					}
459
				}
460
461
				if (!$error) {
462
					// Call trigger
463
					$result = $this->call_trigger('CATEGORY_CREATE', $user);
464
					if ($result < 0) { $error++; }
465
					// End call triggers
466
				}
467
468
				if (!$error) {
469
					$this->db->commit();
470
					return $id;
471
				} else {
472
					$this->db->rollback();
473
					return -3;
474
				}
475
			} else {
476
				$this->db->rollback();
477
				return -2;
478
			}
479
		} else {
480
			$this->error = $this->db->error();
481
			$this->db->rollback();
482
			return -1;
483
		}
484
	}
485
486
	/**
487
	 * 	Update category
488
	 *
489
	 *	@param	User	$user		Object user
490
	 * 	@return	int		 			1 : OK
491
	 *          					-1 : SQL error
492
	 *          					-2 : invalid category
493
	 */
494
	public function update(User $user)
495
	{
496
		global $conf, $langs, $hookmanager;
497
498
		$error = 0;
499
500
		// Clean parameters
501
		$this->label = trim($this->label);
502
		$this->description = trim($this->description);
503
		$this->ref_ext = trim($this->ref_ext);
504
		$this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
505
		$this->visible = ($this->visible != "" ? intval($this->visible) : 0);
506
507
		if ($this->already_exists()) {
508
			$this->error = $langs->trans("ImpossibleUpdateCat");
509
			$this->error .= " : ".$langs->trans("CategoryExistsAtSameLevel");
510
			return -1;
511
		}
512
513
		$this->db->begin();
514
515
		$sql = "UPDATE ".MAIN_DB_PREFIX."categorie";
516
		$sql .= " SET label = '".$this->db->escape($this->label)."',";
517
		$sql .= " description = '".$this->db->escape($this->description)."',";
518
		$sql .= " ref_ext = '".$this->db->escape($this->ref_ext)."',";
519
		$sql .= " color = '".$this->db->escape($this->color)."'";
520
		if (!empty($conf->global->CATEGORY_ASSIGNED_TO_A_CUSTOMER)) {
521
			$sql .= ", fk_soc = ".($this->socid > 0 ? $this->socid : 'null');
522
		}
523
		$sql .= ", visible = ".(int) $this->visible;
524
		$sql .= ", fk_parent = ".(int) $this->fk_parent;
525
		$sql .= ", fk_user_modif = ".(int) $user->id;
526
		$sql .= " WHERE rowid = ".$this->id;
527
528
		dol_syslog(get_class($this)."::update", LOG_DEBUG);
529
		if ($this->db->query($sql)) {
530
			$action = 'update';
531
532
			// Actions on extra fields
533
			if (!$error) {
534
				$result = $this->insertExtraFields();
535
				if ($result < 0) {
536
					$error++;
537
				}
538
			}
539
540
			if (!$error) {
541
				// Call trigger
542
				$result = $this->call_trigger('CATEGORY_MODIFY', $user);
543
				if ($result < 0) { $error++; $this->db->rollback(); return -1; }
544
				// End call triggers
545
			}
546
547
			$this->db->commit();
548
549
			return 1;
550
		} else {
551
			$this->db->rollback();
552
			dol_print_error($this->db);
553
			return -1;
554
		}
555
	}
556
557
	/**
558
	 * 	Delete a category from database
559
	 *
560
	 * 	@param	User	$user		Object user that ask to delete
561
	 *	@param	int		$notrigger	1=Does not execute triggers, 0= execute triggers
562
	 *	@return	int                 <0 KO >0 OK
563
	 */
564
	public function delete($user, $notrigger = 0)
565
	{
566
		global $conf, $langs;
567
568
		$error = 0;
569
570
		// Clean parameters
571
		$this->fk_parent = ($this->fk_parent != "" ? intval($this->fk_parent) : 0);
572
573
		dol_syslog(get_class($this)."::remove");
574
575
		$this->db->begin();
576
577
		if (!$error && !$notrigger) {
578
			// Call trigger
579
			$result = $this->call_trigger('CATEGORY_DELETE', $user);
580
			if ($result < 0) $error++;
581
			// End call triggers
582
		}
583
584
		/* FIX #1317 : Check for child category and move up 1 level*/
585
		if (!$error) {
586
			$sql = "UPDATE ".MAIN_DB_PREFIX."categorie";
587
			$sql .= " SET fk_parent = ".$this->fk_parent;
588
			$sql .= " WHERE fk_parent = ".$this->id;
589
590
			if (!$this->db->query($sql)) {
591
				$this->error = $this->db->lasterror();
592
				$error++;
593
			}
594
		}
595
596
		$arraydelete = array(
597
			'categorie_societe' => 'fk_categorie',
598
			'categorie_fournisseur' => 'fk_categorie',
599
			'categorie_product' => 'fk_categorie',
600
			'categorie_member' => 'fk_categorie',
601
			'categorie_contact' => 'fk_categorie',
602
			'categorie_account' => 'fk_categorie',
603
			'bank_class' => 'fk_categ',
604
			'categorie_lang' => 'fk_category',
605
			'categorie' => 'rowid',
606
		);
607
		foreach ($arraydelete as $key => $value) {
608
			$sql  = "DELETE FROM ".MAIN_DB_PREFIX.$key;
609
			$sql .= " WHERE ".$value." = ".$this->id;
610
			if (!$this->db->query($sql)) {
611
				$this->errors[] = $this->db->lasterror();
612
				dol_syslog("Error sql=".$sql." ".$this->error, LOG_ERR);
613
				$error++;
614
			}
615
		}
616
617
		// Removed extrafields
618
		if (!$error) {
619
			$result = $this->deleteExtraFields();
620
			if ($result < 0) {
621
				$error++;
622
				dol_syslog(get_class($this)."::delete erreur ".$this->error, LOG_ERR);
623
			}
624
		}
625
626
		if (!$error) {
627
			$this->db->commit();
628
			return 1;
629
		} else {
630
			$this->db->rollback();
631
			return -1;
632
		}
633
	}
634
635
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
636
	/**
637
	 * Link an object to the category
638
	 *
639
	 * @param   CommonObject 	$obj  	Object to link to category
640
	 * @param   string     		$type 	Type of category ('product', ...). Use '' to take $obj->element.
641
	 * @return  int                		1 : OK, -1 : erreur SQL, -2 : id not defined, -3 : Already linked
642
	 */
643
	public function add_type($obj, $type = '')
644
	{
645
		// phpcs:enable
646
		global $user, $langs, $conf;
647
648
		$error = 0;
649
650
		if ($this->id == -1) return -2;
651
652
		if (empty($type)) $type = $obj->element;
653
654
		$this->db->begin();
655
656
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type]);
657
		$sql .= " (fk_categorie, fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type]).")";
658
		$sql .= " VALUES (".$this->id.", ".$obj->id.")";
659
660
		dol_syslog(get_class($this).'::add_type', LOG_DEBUG);
661
		if ($this->db->query($sql)) {
662
			if (!empty($conf->global->CATEGORIE_RECURSIV_ADD)) {
663
				$sql = 'SELECT fk_parent FROM '.MAIN_DB_PREFIX.'categorie';
664
				$sql .= " WHERE rowid = ".$this->id;
665
666
				dol_syslog(get_class($this)."::add_type", LOG_DEBUG);
667
				$resql = $this->db->query($sql);
668
				if ($resql) {
669
					if ($this->db->num_rows($resql) > 0) {
670
						$objparent = $this->db->fetch_object($resql);
671
672
						if (!empty($objparent->fk_parent)) {
673
							$cat = new Categorie($this->db);
674
							$cat->id = $objparent->fk_parent;
675
							if (!$cat->containsObject($type, $obj->id)) {
676
								$result = $cat->add_type($obj, $type);
677
								if ($result < 0) {
678
									$this->error = $cat->error;
679
									$error++;
680
								}
681
							}
682
						}
683
					}
684
				} else {
685
					$error++;
686
					$this->error = $this->db->lasterror();
687
				}
688
689
				if ($error) {
690
					$this->db->rollback();
691
					return -1;
692
				}
693
			}
694
695
696
697
			// Call trigger
698
			$this->context = array('linkto'=>$obj); // Save object we want to link category to into category instance to provide information to trigger
699
			$result = $this->call_trigger('CATEGORY_LINK', $user);
700
			if ($result < 0) { $error++; }
701
			// End call triggers
702
703
			if (!$error) {
704
				$this->db->commit();
705
				return 1;
706
			} else {
707
				$this->db->rollback();
708
				return -2;
709
			}
710
		} else {
711
			$this->db->rollback();
712
			if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
713
				$this->error = $this->db->lasterrno();
714
				return -3;
715
			} else {
716
				$this->error = $this->db->lasterror();
717
			}
718
			return -1;
719
		}
720
	}
721
722
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
723
	/**
724
	 * Delete object from category
725
	 *
726
	 * @param   CommonObject $obj  Object
727
	 * @param   string       $type Type of category ('customer', 'supplier', 'contact', 'product', 'member')
728
	 *
729
	 * @return  int          1 if OK, -1 if KO
730
	 */
731
	public function del_type($obj, $type)
732
	{
733
		// phpcs:enable
734
		global $user, $langs, $conf;
735
736
		$error = 0;
737
738
		// For backward compatibility
739
		if ($type == 'societe') {
740
			$type = 'customer';
741
			dol_syslog(get_class($this)."::del_type(): type 'societe' is deprecated, please use 'customer' instead", LOG_WARNING);
742
		} elseif ($type == 'fournisseur') {
743
			$type = 'supplier';
744
			dol_syslog(get_class($this)."::del_type(): type 'fournisseur' is deprecated, please use 'supplier' instead", LOG_WARNING);
745
		}
746
747
		$this->db->begin();
748
749
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type]);
750
		$sql .= " WHERE fk_categorie = ".$this->id;
751
		$sql .= " AND fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = ".$obj->id;
752
753
		dol_syslog(get_class($this).'::del_type', LOG_DEBUG);
754
		if ($this->db->query($sql)) {
755
			// Call trigger
756
			$this->context = array('unlinkoff'=>$obj); // Save object we want to link category to into category instance to provide information to trigger
757
			$result = $this->call_trigger('CATEGORY_UNLINK', $user);
758
			if ($result < 0) { $error++; }
759
			// End call triggers
760
761
			if (!$error) {
762
				$this->db->commit();
763
				return 1;
764
			} else {
765
				$this->db->rollback();
766
				return -2;
767
			}
768
		} else {
769
			$this->db->rollback();
770
			$this->error = $this->db->lasterror();
771
			return -1;
772
		}
773
	}
774
775
	/**
776
	 * Return list of fetched instance of elements having this category
777
	 *
778
	 * @param   string     	$type       Type of category ('customer', 'supplier', 'contact', 'product', 'member', ...)
779
	 * @param   int        	$onlyids    Return only ids of objects (consume less memory)
780
	 * @param	int			$limit		Limit
781
	 * @param	int			$offset		Offset
782
	 * @param	string		$sortfield	Sort fields
783
	 * @param	string		$sortorder	Sort order ('ASC' or 'DESC');
784
	 * @return  array|int              	-1 if KO, array of instance of object if OK
785
	 * @see containsObject()
786
	 */
787
	public function getObjectsInCateg($type, $onlyids = 0, $limit = 0, $offset = 0, $sortfield = '', $sortorder = 'ASC')
788
	{
789
		global $user;
790
791
		$objs = array();
792
793
		$classnameforobj = $this->MAP_OBJ_CLASS[$type];
794
		$obj = new $classnameforobj($this->db);
795
796
		$sql = "SELECT c.fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type]);
797
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type])." as c";
798
		$sql .= ", ".MAIN_DB_PREFIX.(empty($this->MAP_OBJ_TABLE[$type]) ? $type : $this->MAP_OBJ_TABLE[$type])." as o";
799
		$sql .= " WHERE o.entity IN (".getEntity($obj->element).")";
800
		$sql .= " AND c.fk_categorie = ".$this->id;
801
		$sql .= " AND c.fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = o.rowid";
802
		// Protection for external users
803
		if (($type == 'customer' || $type == 'supplier') && $user->socid > 0) {
804
			$sql .= " AND o.rowid = ".$user->socid;
805
		}
806
		if ($limit > 0 || $offset > 0)  $sql .= $this->db->plimit($limit + 1, $offset);
807
		$sql .= $this->db->order($sortfield, $sortorder);
808
809
		dol_syslog(get_class($this)."::getObjectsInCateg", LOG_DEBUG);
810
		$resql = $this->db->query($sql);
811
		if ($resql) {
812
			while ($rec = $this->db->fetch_array($resql)) {
813
				if ($onlyids) {
814
					$objs[] = $rec['fk_'.(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])];
815
				} else {
816
					$classnameforobj = $this->MAP_OBJ_CLASS[$type];
817
818
					$obj = new $classnameforobj($this->db);
819
					$obj->fetch($rec['fk_'.(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])]);
820
821
					$objs[] = $obj;
822
				}
823
			}
824
			return $objs;
825
		} else {
826
			$this->error = $this->db->error().' sql='.$sql;
827
			return -1;
828
		}
829
	}
830
831
	/**
832
	 * Check for the presence of an object in a category
833
	 *
834
	 * @param   string $type      		Type of category ('customer', 'supplier', 'contact', 'product', 'member')
835
	 * @param   int    $object_id 		Id of the object to search
836
	 * @return  int                     Number of occurrences
837
	 * @see getObjectsInCateg()
838
	 */
839
	public function containsObject($type, $object_id)
840
	{
841
		$sql = "SELECT COUNT(*) as nb FROM ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type]);
842
		$sql .= " WHERE fk_categorie = ".$this->id." AND fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = ".$object_id;
843
		dol_syslog(get_class($this)."::containsObject", LOG_DEBUG);
844
		$resql = $this->db->query($sql);
845
		if ($resql) {
846
			return $this->db->fetch_object($resql)->nb;
847
		} else {
848
			$this->error = $this->db->error().' sql='.$sql;
849
			return -1;
850
		}
851
	}
852
853
	/**
854
	 * List categories of an element id
855
	 *
856
	 * @param	int		$id			Id of element
857
	 * @param	string	$type		Type of category ('member', 'customer', 'supplier', 'product', 'contact')
858
	 * @param	string	$sortfield	Sort field
859
	 * @param	string	$sortorder	Sort order
860
	 * @param	int		$limit		Limit for list
861
	 * @param	int		$page		Page number
862
	 * @return	array|int			Array of categories, 0 if no cat, -1 on error
863
	 */
864
	public function getListForItem($id, $type = 'customer', $sortfield = "s.rowid", $sortorder = 'ASC', $limit = 0, $page = 0)
865
	{
866
		global $conf;
867
868
		$categories = array();
869
870
		$sub_type = $type;
871
		$subcol_name = "fk_".$type;
872
		if ($type == "customer") {
873
			$sub_type = "societe";
874
			$subcol_name = "fk_soc";
875
		}
876
		if ($type == "supplier") {
877
			$sub_type = "fournisseur";
878
			$subcol_name = "fk_soc";
879
		}
880
		if ($type == "contact") {
881
			$subcol_name = "fk_socpeople";
882
		}
883
884
		$idoftype = array_search($type, self::$MAP_ID_TO_CODE);
885
886
		$sql = "SELECT s.rowid";
887
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie as s";
888
		$sql .= " , ".MAIN_DB_PREFIX."categorie_".$sub_type." as sub ";
889
		$sql .= ' WHERE s.entity IN ('.getEntity('category').')';
890
		$sql .= ' AND s.type='.$idoftype;
891
		$sql .= ' AND s.rowid = sub.fk_categorie';
892
		$sql .= ' AND sub.'.$subcol_name.' = '.$id;
893
894
		$sql .= $this->db->order($sortfield, $sortorder);
895
896
		$offset = 0;
897
		$nbtotalofrecords = '';
898
		if (empty($conf->global->MAIN_DISABLE_FULL_SCANLIST)) {
899
			$result = $this->db->query($sql);
900
			$nbtotalofrecords = $this->db->num_rows($result);
901
			if (($page * $limit) > $nbtotalofrecords) {	// if total resultset is smaller then paging size (filtering), goto and load page 0
902
				$page = 0;
903
				$offset = 0;
904
			}
905
		}
906
907
		if ($limit) {
908
			if ($page < 0) {
909
				$page = 0;
910
			}
911
			$offset = $limit * $page;
912
913
			$sql .= $this->db->plimit($limit + 1, $offset);
914
		}
915
916
		$result = $this->db->query($sql);
917
		if ($result) {
918
			$i = 0;
919
			$num = $this->db->num_rows($result);
920
			$min = min($num, ($limit <= 0 ? $num : $limit));
921
			while ($i < $min) {
922
				$obj = $this->db->fetch_object($result);
923
				$category_static = new Categorie($this->db);
924
				if ($category_static->fetch($obj->rowid)) {
925
					$categories[$i]['id'] = $category_static->id;
926
					$categories[$i]['fk_parent']		= $category_static->fk_parent;
927
					$categories[$i]['label']			= $category_static->label;
928
					$categories[$i]['description'] = $category_static->description;
929
					$categories[$i]['color']    		= $category_static->color;
930
					$categories[$i]['socid']			= $category_static->socid;
931
					$categories[$i]['ref_ext'] = $category_static->ref_ext;
932
					$categories[$i]['visible'] = $category_static->visible;
933
					$categories[$i]['type'] = $category_static->type;
934
					$categories[$i]['entity'] = $category_static->entity;
935
					$categories[$i]['array_options'] = $category_static->array_options;
936
937
					// multilangs
938
					if (!empty($conf->global->MAIN_MULTILANGS)) {
939
						$categories[$i]['multilangs'] = $category_static->multilangs;
940
					}
941
				}
942
				$i++;
943
			}
944
		} else {
945
			$this->error = $this->db->lasterror();
946
			return -1;
947
		}
948
		if (!count($categories)) {
949
			return 0;
950
		}
951
952
		return $categories;
953
	}
954
955
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
956
	/**
957
	 * Return direct childs id of a category into an array
958
	 *
959
	 * @return	array|int   <0 KO, array ok
960
	 */
961
	public function get_filles()
962
	{
963
		// phpcs:enable
964
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
965
		$sql .= " WHERE fk_parent = ".$this->id;
966
		$sql .= " AND entity IN (".getEntity('category').")";
967
968
		$res = $this->db->query($sql);
969
		if ($res) {
970
			$cats = array();
971
			while ($rec = $this->db->fetch_array($res)) {
972
				$cat = new Categorie($this->db);
973
				$cat->fetch($rec['rowid']);
974
				$cats[] = $cat;
975
			}
976
			return $cats;
977
		} else {
978
			dol_print_error($this->db);
979
			return -1;
980
		}
981
	}
982
983
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
984
	/**
985
	 * 	Load the array this->motherof that is array(id_son=>id_parent, ...)
986
	 *
987
	 *	@return		int		<0 if KO, >0 if OK
988
	 */
989
	protected function load_motherof()
990
	{
991
		// phpcs:enable
992
		$this->motherof = array();
993
994
		// Load array[child]=parent
995
		$sql = "SELECT fk_parent as id_parent, rowid as id_son";
996
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie";
997
		$sql .= " WHERE fk_parent != 0";
998
		$sql .= " AND entity IN (".getEntity('category').")";
999
1000
		dol_syslog(get_class($this)."::load_motherof", LOG_DEBUG);
1001
		$resql = $this->db->query($sql);
1002
		if ($resql) {
1003
			while ($obj = $this->db->fetch_object($resql)) {
1004
				$this->motherof[$obj->id_son] = $obj->id_parent;
1005
			}
1006
			return 1;
1007
		} else {
1008
			dol_print_error($this->db);
1009
			return -1;
1010
		}
1011
	}
1012
1013
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1014
	/**
1015
	 * Rebuilding the category tree as an array
1016
	 * Return an array of table('id','id_mere',...) trie selon arbre et avec:
1017
	 *                id = id de la categorie
1018
	 *                id_mere = id de la categorie mere
1019
	 *                id_children = tableau des id enfant
1020
	 *                label = nom de la categorie
1021
	 *                fulllabel = nom avec chemin complet de la categorie
1022
	 *                fullpath = chemin complet compose des id
1023
	 *
1024
	 * @param   string                  $type                   Type of categories ('customer', 'supplier', 'contact', 'product', 'member', ...)
1025
	 * @param   int|string|array        $markafterid            Keep only or removed all categories including the leaf $markafterid in category tree (exclude) or Keep only of category is inside the leaf starting with this id.
1026
	 *                                                          $markafterid can be an :
1027
	 *                                                          - int (id of category)
1028
	 *                                                          - string (categories ids separated by comma)
1029
	 *                                                          - array (list of categories ids)
1030
	 * @param   int                     $include                [=0] Removed or 1=Keep only
1031
	 * @return  array|int               Array of categories. this->cats and this->motherof are set, -1 on error
1032
	 */
1033
	public function get_full_arbo($type, $markafterid = 0, $include = 0)
1034
	{
1035
		// phpcs:enable
1036
		global $conf, $langs;
1037
1038
		if (!is_numeric($type)) $type = $this->MAP_ID[$type];
1039
		if (is_null($type)) {
1040
			$this->error = 'BadValueForParameterType';
1041
			return -1;
1042
		}
1043
1044
		if (is_string($markafterid)) {
1045
			$markafterid = explode(',', $markafterid);
1046
		} elseif (is_numeric($markafterid)) {
1047
			if ($markafterid > 0) {
1048
				$markafterid = array($markafterid);
1049
			} else {
1050
				$markafterid = array();
1051
			}
1052
		} elseif (!is_array($markafterid)) {
1053
			$markafterid = array();
1054
		}
1055
1056
		$this->cats = array();
1057
1058
		// Init this->motherof that is array(id_son=>id_parent, ...)
1059
		$this->load_motherof();
1060
		$current_lang = $langs->getDefaultLang();
1061
1062
		// Init $this->cats array
1063
		$sql = "SELECT DISTINCT c.rowid, c.label, c.ref_ext, c.description, c.color, c.fk_parent, c.visible"; // Distinct reduce pb with old tables with duplicates
1064
		if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= ", t.label as label_trans, t.description as description_trans";
1065
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie as c";
1066
		if (!empty($conf->global->MAIN_MULTILANGS)) $sql .= " LEFT  JOIN ".MAIN_DB_PREFIX."categorie_lang as t ON t.fk_category=c.rowid AND t.lang='".$this->db->escape($current_lang)."'";
1067
		$sql .= " WHERE c.entity IN (".getEntity('category').")";
1068
		$sql .= " AND c.type = ".(int) $type;
1069
1070
		dol_syslog(get_class($this)."::get_full_arbo get category list", LOG_DEBUG);
1071
		$resql = $this->db->query($sql);
1072
		if ($resql) {
1073
			$i = 0;
1074
			while ($obj = $this->db->fetch_object($resql)) {
1075
				$this->cats[$obj->rowid]['rowid'] = $obj->rowid;
1076
				$this->cats[$obj->rowid]['id'] = $obj->rowid;
1077
				$this->cats[$obj->rowid]['fk_parent'] = $obj->fk_parent;
1078
				$this->cats[$obj->rowid]['label'] = !empty($obj->label_trans) ? $obj->label_trans : $obj->label;
1079
				$this->cats[$obj->rowid]['description'] = !empty($obj->description_trans) ? $obj->description_trans : $obj->description;
1080
				$this->cats[$obj->rowid]['color'] = $obj->color;
1081
				$this->cats[$obj->rowid]['visible'] = $obj->visible;
1082
				$this->cats[$obj->rowid]['ref_ext'] = $obj->ref_ext;
1083
				$i++;
1084
			}
1085
		} else {
1086
			dol_print_error($this->db);
1087
			return -1;
1088
		}
1089
1090
		// We add the fullpath property to each elements of first level (no parent exists)
1091
		dol_syslog(get_class($this)."::get_full_arbo call to build_path_from_id_categ", LOG_DEBUG);
1092
		foreach ($this->cats as $key => $val) {
1093
			//print 'key='.$key.'<br>'."\n";
1094
			$this->build_path_from_id_categ($key, 0); // Process a branch from the root category key (this category has no parent)
1095
		}
1096
1097
		// Include or exclude leaf including $markafterid from tree
1098
		if (count($markafterid) > 0) {
1099
			$keyfiltercatid = '('.implode('|', $markafterid).')';
1100
1101
			//print "Look to discard category ".$markafterid."\n";
1102
			$keyfilter1 = '^'.$keyfiltercatid.'$';
1103
			$keyfilter2 = '_'.$keyfiltercatid.'$';
1104
			$keyfilter3 = '^'.$keyfiltercatid.'_';
1105
			$keyfilter4 = '_'.$keyfiltercatid.'_';
1106
			foreach ($this->cats as $key => $val) {
1107
				$test = (preg_match('/'.$keyfilter1.'/', $val['fullpath']) || preg_match('/'.$keyfilter2.'/', $val['fullpath'])
1108
					|| preg_match('/'.$keyfilter3.'/', $val['fullpath']) || preg_match('/'.$keyfilter4.'/', $val['fullpath']));
1109
1110
				if (($test && !$include) || (!$test && $include)) {
1111
					unset($this->cats[$key]);
1112
				}
1113
			}
1114
		}
1115
1116
		dol_syslog(get_class($this)."::get_full_arbo dol_sort_array", LOG_DEBUG);
1117
		$this->cats = dol_sort_array($this->cats, 'fulllabel', 'asc', true, false);
1118
1119
		//$this->debug_cats();
1120
1121
		return $this->cats;
1122
	}
1123
1124
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1125
	/**
1126
	 *	For category id_categ and its childs available in this->cats, define property fullpath and fulllabel.
1127
	 *  It is called by get_full_arbo()
1128
	 *  This function is a memory scan only from $this->cats and $this->motherof, no database access must be done here.
1129
	 *
1130
	 * 	@param		int		$id_categ		id_categ entry to update
1131
	 * 	@param		int		$protection		Deep counter to avoid infinite loop
1132
	 *	@return		void
1133
	 *  @see get_full_arbo()
1134
	 */
1135
	public function build_path_from_id_categ($id_categ, $protection = 1000)
1136
	{
1137
		// phpcs:enable
1138
		dol_syslog(get_class($this)."::build_path_from_id_categ id_categ=".$id_categ." protection=".$protection, LOG_DEBUG);
1139
1140
		if (!empty($this->cats[$id_categ]['fullpath'])) {
1141
			// Already defined
1142
			dol_syslog(get_class($this)."::build_path_from_id_categ fullpath and fulllabel already defined", LOG_WARNING);
1143
			return;
1144
		}
1145
1146
		// First build full array $motherof
1147
		//$this->load_motherof();	// Disabled because already done by caller of build_path_from_id_categ
1148
1149
		// Define fullpath and fulllabel
1150
		$this->cats[$id_categ]['fullpath'] = '_'.$id_categ;
1151
		$this->cats[$id_categ]['fulllabel'] = $this->cats[$id_categ]['label'];
1152
		$i = 0; $cursor_categ = $id_categ;
1153
		//print 'Work for id_categ='.$id_categ.'<br>'."\n";
1154
		while ((empty($protection) || $i < $protection) && !empty($this->motherof[$cursor_categ])) {
1155
			//print '&nbsp; cursor_categ='.$cursor_categ.' i='.$i.' '.$this->motherof[$cursor_categ].'<br>'."\n";
1156
			$this->cats[$id_categ]['fullpath'] = '_'.$this->motherof[$cursor_categ].$this->cats[$id_categ]['fullpath'];
1157
			$this->cats[$id_categ]['fulllabel'] = $this->cats[$this->motherof[$cursor_categ]]['label'].' >> '.$this->cats[$id_categ]['fulllabel'];
1158
			//print '&nbsp; Result for id_categ='.$id_categ.' : '.$this->cats[$id_categ]['fullpath'].' '.$this->cats[$id_categ]['fulllabel'].'<br>'."\n";
1159
			$i++; $cursor_categ = $this->motherof[$cursor_categ];
1160
		}
1161
		//print 'Result for id_categ='.$id_categ.' : '.$this->cats[$id_categ]['fullpath'].'<br>'."\n";
1162
1163
		// We count number of _ to have level
1164
		$nbunderscore = substr_count($this->cats[$id_categ]['fullpath'], '_');
1165
		$this->cats[$id_categ]['level'] = ($nbunderscore ? $nbunderscore : null);
1166
1167
		return;
1168
	}
1169
1170
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1171
	/**
1172
	 *	Display content of $this->cats
1173
	 *
1174
	 *	@return	void
1175
	 */
1176
	public function debug_cats()
1177
	{
1178
		// phpcs:enable
1179
		// Display $this->cats
1180
		foreach ($this->cats as $key => $val) {
1181
			print 'id: '.$this->cats[$key]['id'];
1182
			print ' label: '.$this->cats[$key]['label'];
1183
			print ' mother: '.$this->cats[$key]['fk_parent'];
1184
			//print ' children: '.(is_array($this->cats[$key]['id_children'])?join(',',$this->cats[$key]['id_children']):'');
1185
			print ' fullpath: '.$this->cats[$key]['fullpath'];
1186
			print ' fulllabel: '.$this->cats[$key]['fulllabel'];
1187
			print "<br>\n";
1188
		}
1189
	}
1190
1191
1192
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1193
	/**
1194
	 * 	Returns all categories
1195
	 *
1196
	 *	@param	int			$type		Type of category (0, 1, ...)
1197
	 *	@param	boolean		$parent		Just parent categories if true
1198
	 *	@return	array|int				Table of Object Category, -1 on error
1199
	 */
1200
	public function get_all_categories($type = null, $parent = false)
1201
	{
1202
		// phpcs:enable
1203
		if (!is_numeric($type)) $type = $this->MAP_ID[$type];
1204
1205
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
1206
		$sql .= " WHERE entity IN (".getEntity('category').")";
1207
		if (!is_null($type))
1208
			$sql .= " AND type = ".(int) $type;
1209
		if ($parent)
1210
			$sql .= " AND fk_parent = 0";
1211
1212
		$res = $this->db->query($sql);
1213
		if ($res) {
1214
			$cats = array();
1215
			while ($rec = $this->db->fetch_array($res)) {
1216
				$cat = new Categorie($this->db);
1217
				$cat->fetch($rec['rowid']);
1218
				$cats[$rec['rowid']] = $cat;
1219
			}
1220
			return $cats;
1221
		} else {
1222
			dol_print_error($this->db);
1223
			return -1;
1224
		}
1225
	}
1226
1227
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1228
	/**
1229
	 *	Returns the top level categories (which are not child)
1230
	 *
1231
	 *	@param		int		$type		Type of category (0, 1, ...)
1232
	 *	@return		array
1233
	 */
1234
	public function get_main_categories($type = null)
1235
	{
1236
		// phpcs:enable
1237
		return $this->get_all_categories($type, true);
1238
	}
1239
1240
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1241
	/**
1242
	 * 	Check if no category with same label already exists for this cat's parent or root and for this cat's type
1243
	 *
1244
	 * 	@return		integer		1 if already exist, 0 otherwise, -1 if error
1245
	 */
1246
	public function already_exists()
1247
	{
1248
		// phpcs:enable
1249
		$type = $this->type;
1250
1251
		if (!is_numeric($type)) $type = $this->MAP_ID[$type];
1252
1253
		/* We have to select any rowid from llx_categorie which category's mother and label
1254
		 * are equals to those of the calling category
1255
		 */
1256
		$sql = "SELECT c.rowid";
1257
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie as c ";
1258
		$sql .= " WHERE c.entity IN (".getEntity('category').")";
1259
		$sql .= " AND c.type = ".$type;
1260
		$sql .= " AND c.fk_parent = ".$this->fk_parent;
1261
		$sql .= " AND c.label = '".$this->db->escape($this->label)."'";
1262
1263
		dol_syslog(get_class($this)."::already_exists", LOG_DEBUG);
1264
		$resql = $this->db->query($sql);
1265
		if ($resql) {
1266
			if ($this->db->num_rows($resql) > 0) {						// Checking for empty resql
1267
				$obj = $this->db->fetch_array($resql);
1268
				/* If object called create, obj cannot have is id.
1269
				 * If object called update, he mustn't have the same label as an other category for this mother.
1270
				 * So if the result have the same id, update is not for label, and if result have an other one,
1271
				 * update may be for label.
1272
				 */
1273
				if ($obj[0] > 0 && $obj[0] != $this->id) {
1274
					dol_syslog(get_class($this)."::already_exists category with name=".$this->label." and parent ".$this->fk_parent." exists: rowid=".$obj[0]." current_id=".$this->id, LOG_DEBUG);
1275
					return 1;
1276
				}
1277
			}
1278
			dol_syslog(get_class($this)."::already_exists no category with same name=".$this->label." and same parent ".$this->fk_parent." than category id=".$this->id, LOG_DEBUG);
1279
			return 0;
1280
		} else {
1281
			$this->error = $this->db->error();
1282
			return -1;
1283
		}
1284
	}
1285
1286
1287
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1288
	/**
1289
	 * Returns the path of the category, with the names of the categories
1290
	 * separated by $sep (" >> " by default)
1291
	 *
1292
	 * @param	string	$sep	     Separator
1293
	 * @param	string	$url	     Url ('', 'none' or 'urltouse')
1294
	 * @param   int     $nocolor     0
1295
	 * @param	string	$addpicto	 Add picto into link
1296
	 * @return	array
1297
	 */
1298
	public function print_all_ways($sep = ' &gt;&gt; ', $url = '', $nocolor = 0, $addpicto = 0)
1299
	{
1300
		// phpcs:enable
1301
		$ways = array();
1302
1303
		$allways = $this->get_all_ways(); // Load array of categories
1304
		foreach ($allways as $way) {
1305
			$w = array();
1306
			$i = 0;
1307
			$forced_color = '';
1308
			foreach ($way as $cat) {
1309
				$i++;
1310
1311
				if (empty($nocolor)) {
1312
					$forced_color = 'toreplace';
1313
					if ($i == count($way)) {
1314
						// Check contrast with background and correct text color
1315
						$forced_color = 'categtextwhite';
1316
						if ($cat->color) {
1317
							if (colorIsLight($cat->color)) $forced_color = 'categtextblack';
1318
						}
1319
					}
1320
				}
1321
1322
				if ($url == '') {
1323
					$link = '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.$cat->id.'&type='.$cat->type.'" class="'.$forced_color.'">';
1324
					$linkend = '</a>';
1325
					$w[] = $link.($addpicto ? img_object('', 'category', 'class="paddingright"') : '').$cat->label.$linkend;
1326
				} elseif ($url == 'none') {
1327
					$link = '<span class="'.$forced_color.'">';
1328
					$linkend = '</span>';
1329
					$w[] = $link.($addpicto ? img_object('', 'category', 'class="paddingright"') : '').$cat->label.$linkend;
1330
				} else {
1331
					$w[] = '<a class="'.$forced_color.'" href="'.DOL_URL_ROOT.'/'.$url.'?catid='.$cat->id.'">'.($addpicto ? img_object('', 'category') : '').$cat->label.'</a>';
1332
				}
1333
			}
1334
			$newcategwithpath = preg_replace('/toreplace/', $forced_color, implode($sep, $w));
1335
1336
			$ways[] = $newcategwithpath;
1337
		}
1338
1339
		return $ways;
1340
	}
1341
1342
1343
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1344
	/**
1345
	 *	Returns an array containing the list of parent categories
1346
	 *
1347
	 *	@return	int|array <0 KO, array OK
1348
	 */
1349
	public function get_meres()
1350
	{
1351
		// phpcs:enable
1352
		$parents = array();
1353
1354
		$sql = "SELECT fk_parent FROM ".MAIN_DB_PREFIX."categorie";
1355
		$sql .= " WHERE rowid = ".$this->id;
1356
1357
		$res = $this->db->query($sql);
1358
1359
		if ($res) {
1360
			while ($rec = $this->db->fetch_array($res)) {
1361
				if ($rec['fk_parent'] > 0) {
1362
					$cat = new Categorie($this->db);
1363
					$cat->fetch($rec['fk_parent']);
1364
					$parents[] = $cat;
1365
				}
1366
			}
1367
			return $parents;
1368
		} else {
1369
			dol_print_error($this->db);
1370
			return -1;
1371
		}
1372
	}
1373
1374
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1375
	/**
1376
	 * 	Returns in a table all possible paths to get to the category
1377
	 * 	starting with the major categories represented by Tables of categories
1378
	 *
1379
	 *	@return	array
1380
	 */
1381
	public function get_all_ways()
1382
	{
1383
		// phpcs:enable
1384
		$ways = array();
1385
1386
		$parents = $this->get_meres();
1387
		if (!empty($parents)) {
1388
			foreach ($parents as $parent) {
0 ignored issues
show
Bug introduced by
The expression $parents of type integer is not traversable.
Loading history...
1389
				$allways = $parent->get_all_ways();
1390
				foreach ($allways as $way) {
1391
					$w = $way;
1392
					$w[] = $this;
1393
					$ways[] = $w;
1394
				}
1395
			}
1396
		}
1397
1398
		if (count($ways) == 0)
1399
			$ways[0][0] = $this;
1400
1401
		return $ways;
1402
	}
1403
1404
	/**
1405
	 * Return list of categories (object instances or labels) linked to element of id $id and type $type
1406
	 * Should be named getListOfCategForObject
1407
	 *
1408
	 * @param   int    		$id     Id of element
1409
	 * @param   string|int	$type   Type of category ('customer', 'supplier', 'contact', 'product', 'member') or (0, 1, 2, ...)
1410
	 * @param   string 		$mode   'id'=Get array of category ids, 'object'=Get array of fetched category instances, 'label'=Get array of category
1411
	 *                      	    labels, 'id'= Get array of category IDs
1412
	 * @return  Categorie[]|int     Array of category objects or < 0 if KO
1413
	 */
1414
	public function containing($id, $type, $mode = 'object')
1415
	{
1416
		$cats = array();
1417
1418
		if (is_numeric($type)) $type = Categorie::$MAP_ID_TO_CODE[$type];
1419
1420
		if ($type === Categorie::TYPE_BANK_LINE) {   // TODO Remove this with standard category code after migration of llx_bank_categ into llx_categorie
1421
			// Load bank categories
1422
			$sql = "SELECT c.label, c.rowid";
1423
			$sql .= " FROM ".MAIN_DB_PREFIX."bank_class as a, ".MAIN_DB_PREFIX."bank_categ as c";
1424
			$sql .= " WHERE a.lineid=".$id." AND a.fk_categ = c.rowid";
1425
			$sql .= " AND c.entity IN (".getEntity('category').")";
1426
			$sql .= " ORDER BY c.label";
1427
1428
			$res = $this->db->query($sql);
1429
			if ($res) {
1430
				while ($obj = $this->db->fetch_object($res)) {
1431
					if ($mode == 'id') {
1432
						$cats[] = $obj->rowid;
1433
					} elseif ($mode == 'label') {
1434
						$cats[] = $obj->label;
1435
					} else {
1436
						$cat = new Categorie($this->db);
1437
						$cat->id = $obj->rowid;
1438
						$cat->label = $obj->label;
1439
						$cats[] = $cat;
1440
					}
1441
				}
1442
			} else {
1443
				dol_print_error($this->db);
1444
				return -1;
1445
			}
1446
		} else {
1447
			$sql = "SELECT ct.fk_categorie, c.label, c.rowid";
1448
			$sql .= " FROM ".MAIN_DB_PREFIX."categorie_".(empty($this->MAP_CAT_TABLE[$type]) ? $type : $this->MAP_CAT_TABLE[$type])." as ct, ".MAIN_DB_PREFIX."categorie as c";
1449
			$sql .= " WHERE ct.fk_categorie = c.rowid AND ct.fk_".(empty($this->MAP_CAT_FK[$type]) ? $type : $this->MAP_CAT_FK[$type])." = ".(int) $id." AND c.type = ".$this->MAP_ID[$type];
1450
			$sql .= " AND c.entity IN (".getEntity('category').")";
1451
1452
			$res = $this->db->query($sql);
1453
			if ($res) {
1454
				while ($obj = $this->db->fetch_object($res)) {
1455
					if ($mode == 'id') {
1456
						$cats[] = $obj->rowid;
1457
					} elseif ($mode == 'label') {
1458
						$cats[] = $obj->label;
1459
					} else {
1460
						$cat = new Categorie($this->db);
1461
						$cat->fetch($obj->fk_categorie);
1462
						$cats[] = $cat;
1463
					}
1464
				}
1465
			} else {
1466
				dol_print_error($this->db);
1467
				return -1;
1468
			}
1469
		}
1470
1471
		return $cats;
1472
	}
1473
1474
1475
	/**
1476
	 * 	Returns categories whose id or name match
1477
	 * 	add wildcards in the name unless $exact = true
1478
	 *
1479
	 * 	@param		int			$id			Id
1480
	 * 	@param		string		$nom		Name
1481
	 * 	@param		string		$type		Type of category ('member', 'customer', 'supplier', 'product', 'contact'). Old mode (0, 1, 2, ...) is deprecated.
1482
	 * 	@param		boolean		$exact		Exact string search (true/false)
1483
	 * 	@param		boolean		$case		Case sensitive (true/false)
1484
	 * 	@return		Categorie[]|int			Array of Categorie, -1 if error
1485
	 */
1486
	public function rechercher($id, $nom, $type, $exact = false, $case = false)
1487
	{
1488
		// Deprecation warning
1489
		if (is_numeric($type)) {
1490
			dol_syslog(__METHOD__.': using numeric types is deprecated.', LOG_WARNING);
1491
		}
1492
1493
		$cats = array();
1494
1495
		// For backward compatibility
1496
		if (is_numeric($type)) {
1497
			// We want to reverse lookup
1498
			$map_type = array_flip($this->MAP_ID);
1499
			$type = $map_type[$type];
1500
			dol_syslog(get_class($this)."::rechercher(): numeric types are deprecated, please use string instead", LOG_WARNING);
1501
		}
1502
1503
		// Generation requete recherche
1504
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."categorie";
1505
		$sql .= " WHERE type = ".$this->MAP_ID[$type];
1506
		$sql .= " AND entity IN (".getEntity('category').")";
1507
		if ($nom) {
1508
			if (!$exact)
1509
				$nom = '%'.str_replace('*', '%', $nom).'%';
1510
			if (!$case)
1511
				$sql .= " AND label LIKE '".$this->db->escape($nom)."'";
1512
			else $sql .= " AND label LIKE BINARY '".$this->db->escape($nom)."'";
1513
		}
1514
		if ($id) {
1515
			$sql .= " AND rowid = '".$id."'";
1516
		}
1517
1518
		$res = $this->db->query($sql);
1519
		if ($res) {
1520
			while ($rec = $this->db->fetch_array($res)) {
1521
				$cat = new Categorie($this->db);
1522
				$cat->fetch($rec['rowid']);
1523
				$cats[] = $cat;
1524
			}
1525
1526
			return $cats;
1527
		} else {
1528
			$this->error = $this->db->error().' sql='.$sql;
1529
			return -1;
1530
		}
1531
	}
1532
1533
	/**
1534
	 *	Return name and link of category (with picto)
1535
	 *  Use ->id, ->ref, ->label, ->color
1536
	 *
1537
	 *	@param		int		$withpicto		0=No picto, 1=Include picto into link, 2=Only picto
1538
	 *	@param		string	$option			Sur quoi pointe le lien ('', 'xyz')
1539
	 * 	@param		int		$maxlength		Max length of text
1540
	 *  @param		string	$moreparam		More param on URL link
1541
	 *	@return		string					Chaine avec URL
1542
	 */
1543
	public function getNomUrl($withpicto = 0, $option = '', $maxlength = 0, $moreparam = '')
1544
	{
1545
		global $langs;
1546
1547
		$result = '';
1548
		$label = $langs->trans("ShowCategory").': '.($this->ref ? $this->ref : $this->label);
1549
1550
		// Check contrast with background and correct text color
1551
		$forced_color = 'categtextwhite';
1552
		if ($this->color) {
1553
			if (colorIsLight($this->color)) $forced_color = 'categtextblack';
1554
		}
1555
1556
		$link = '<a href="'.DOL_URL_ROOT.'/categories/viewcat.php?id='.$this->id.'&type='.$this->type.$moreparam.'&backtopage='.urlencode($_SERVER['PHP_SELF'].($moreparam ? '?'.$moreparam : '')).'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip '.$forced_color.'">';
1557
		$linkend = '</a>';
1558
1559
		$picto = 'category';
1560
1561
1562
		if ($withpicto) $result .= ($link.img_object($label, $picto, 'class="classfortooltip"').$linkend);
1563
		if ($withpicto && $withpicto != 2) $result .= ' ';
1564
		if ($withpicto != 2) $result .= $link.dol_trunc(($this->ref ? $this->ref : $this->label), $maxlength).$linkend;
1565
		return $result;
1566
	}
1567
1568
1569
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1570
	/**
1571
	 *  Deplace fichier uploade sous le nom $files dans le repertoire sdir
1572
	 *
1573
	 *  @param      string	$sdir       Repertoire destination finale
1574
	 *  @param      string	$file		Nom du fichier uploade
1575
	 *	@return		void
1576
	 */
1577
	public function add_photo($sdir, $file)
1578
	{
1579
		// phpcs:enable
1580
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1581
1582
		$dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, 'category').$this->id."/";
1583
		$dir .= "photos/";
1584
1585
		if (!file_exists($dir)) {
1586
			dol_mkdir($dir);
1587
		}
1588
1589
		if (file_exists($dir)) {
1590
			if (is_array($file['name']) && count($file['name']) > 0) {
1591
				$nbfile = count($file['name']);
1592
				for ($i = 0; $i <= $nbfile; $i++) {
1593
					$originImage = $dir.$file['name'][$i];
1594
1595
					// Cree fichier en taille origine
1596
					dol_move_uploaded_file($file['tmp_name'][$i], $originImage, 1, 0, 0);
1597
1598
					if (file_exists($originImage)) {
1599
						// Create thumbs
1600
						$this->addThumbs($originImage);
1601
					}
1602
				}
1603
			} else {
1604
				$originImage = $dir.$file['name'];
1605
1606
				// Cree fichier en taille origine
1607
				dol_move_uploaded_file($file['tmp_name'], $originImage, 1, 0, 0);
1608
1609
				if (file_exists($originImage)) {
1610
					// Create thumbs
1611
					$this->addThumbs($originImage);
1612
				}
1613
			}
1614
		}
1615
	}
1616
1617
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1618
	/**
1619
	 *    Return tableau de toutes les photos de la categorie
1620
	 *
1621
	 *    @param      string	$dir        Repertoire a scanner
1622
	 *    @param      int		$nbmax      Nombre maximum de photos (0=pas de max)
1623
	 *    @return     array       			Tableau de photos
1624
	 */
1625
	public function liste_photos($dir, $nbmax = 0)
1626
	{
1627
		// phpcs:enable
1628
		include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1629
1630
		$nbphoto = 0;
1631
		$tabobj = array();
1632
1633
		$dirthumb = $dir.'thumbs/';
1634
1635
		if (file_exists($dir)) {
1636
			$handle = opendir($dir);
1637
			if (is_resource($handle)) {
1638
				while (($file = readdir($handle)) !== false) {
1639
					if (dol_is_file($dir.$file) && preg_match('/(\.jpeg|\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i', $dir.$file)) {
1640
						$nbphoto++;
1641
						$photo = $file;
1642
1643
						// On determine nom du fichier vignette
1644
						$photo_vignette = '';
1645
						if (preg_match('/(\.jpeg|\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i', $photo, $regs)) {
1646
							$photo_vignette = preg_replace('/'.$regs[0].'/i', '', $photo).'_small'.$regs[0];
1647
						}
1648
1649
						// Objet
1650
						$obj = array();
1651
						$obj['photo'] = $photo;
1652
						if ($photo_vignette && is_file($dirthumb.$photo_vignette)) $obj['photo_vignette'] = 'thumbs/'.$photo_vignette;
1653
						else $obj['photo_vignette'] = "";
1654
1655
						$tabobj[$nbphoto - 1] = $obj;
1656
1657
						// On continue ou on arrete de boucler
1658
						if ($nbmax && $nbphoto >= $nbmax) break;
1659
					}
1660
				}
1661
1662
				closedir($handle);
1663
			}
1664
		}
1665
1666
		return $tabobj;
1667
	}
1668
1669
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1670
	/**
1671
	 *    Efface la photo de la categorie et sa vignette
1672
	 *
1673
	 *    @param	string		$file		Path to file
1674
	 *    @return	void
1675
	 */
1676
	public function delete_photo($file)
1677
	{
1678
		// phpcs:enable
1679
		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1680
1681
		$dir = dirname($file).'/'; // Chemin du dossier contenant l'image d'origine
1682
		$dirthumb = $dir.'/thumbs/'; // Chemin du dossier contenant la vignette
1683
		$filename = preg_replace('/'.preg_quote($dir, '/').'/i', '', $file); // Nom du fichier
1684
1685
		// On efface l'image d'origine
1686
		dol_delete_file($file, 1);
1687
1688
		// Si elle existe, on efface la vignette
1689
		if (preg_match('/(\.jpeg|\.jpg|\.bmp|\.gif|\.png|\.tiff)$/i', $filename, $regs)) {
1690
			$photo_vignette = preg_replace('/'.$regs[0].'/i', '', $filename).'_small'.$regs[0];
1691
			if (file_exists($dirthumb.$photo_vignette)) {
1692
				dol_delete_file($dirthumb.$photo_vignette, 1);
1693
			}
1694
		}
1695
	}
1696
1697
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1698
	/**
1699
	 *  Load size of image file
1700
	 *
1701
	 *  @param    	string	$file        Path to file
1702
	 *  @return		void
1703
	 */
1704
	public function get_image_size($file)
1705
	{
1706
		// phpcs:enable
1707
		$infoImg = getimagesize($file); // Recuperation des infos de l'image
1708
		$this->imgWidth = $infoImg[0]; // Largeur de l'image
1709
		$this->imgHeight = $infoImg[1]; // Hauteur de l'image
1710
	}
1711
1712
	/**
1713
	 *	Update ou cree les traductions des infos produits
1714
	 *
1715
	 *	@param	User	$user		Object user
1716
	 *
1717
	 *	@return		int		<0 if KO, >0 if OK
1718
	 */
1719
	public function setMultiLangs($user)
1720
	{
1721
		global $langs;
1722
1723
		$langs_available = $langs->get_available_languages();
1724
		$current_lang = $langs->getDefaultLang();
1725
1726
		foreach ($langs_available as $key => $value) {
1727
			$sql = "SELECT rowid";
1728
			$sql .= " FROM ".MAIN_DB_PREFIX."categorie_lang";
1729
			$sql .= " WHERE fk_category=".$this->id;
1730
			$sql .= " AND lang='".$key."'";
1731
1732
			$result = $this->db->query($sql);
1733
1734
			if ($key == $current_lang) {
1735
				if ($this->db->num_rows($result)) { // si aucune ligne dans la base
1736
					$sql2 = "UPDATE ".MAIN_DB_PREFIX."categorie_lang";
1737
					$sql2 .= " SET label='".$this->db->escape($this->label)."',";
1738
					$sql2 .= " description='".$this->db->escape($this->description)."'";
1739
					$sql2 .= " WHERE fk_category=".$this->id." AND lang='".$this->db->escape($key)."'";
1740
				} else {
1741
					$sql2 = "INSERT INTO ".MAIN_DB_PREFIX."categorie_lang (fk_category, lang, label, description)";
1742
					$sql2 .= " VALUES(".$this->id.",'".$key."','".$this->db->escape($this->label);
1743
					$sql2 .= "','".$this->db->escape($this->multilangs["$key"]["description"])."')";
1744
				}
1745
				dol_syslog(get_class($this).'::setMultiLangs', LOG_DEBUG);
1746
				if (!$this->db->query($sql2)) {
1747
					$this->error = $this->db->lasterror();
1748
					return -1;
1749
				}
1750
			} elseif (isset($this->multilangs["$key"])) {
1751
				if ($this->db->num_rows($result)) { // si aucune ligne dans la base
1752
					$sql2 = "UPDATE ".MAIN_DB_PREFIX."categorie_lang";
1753
					$sql2 .= " SET label='".$this->db->escape($this->multilangs["$key"]["label"])."',";
1754
					$sql2 .= " description='".$this->db->escape($this->multilangs["$key"]["description"])."'";
1755
					$sql2 .= " WHERE fk_category=".$this->id." AND lang='".$this->db->escape($key)."'";
1756
				} else {
1757
					$sql2 = "INSERT INTO ".MAIN_DB_PREFIX."categorie_lang (fk_category, lang, label, description)";
1758
					$sql2 .= " VALUES(".$this->id.",'".$key."','".$this->db->escape($this->multilangs["$key"]["label"]);
1759
					$sql2 .= "','".$this->db->escape($this->multilangs["$key"]["description"])."')";
1760
				}
1761
1762
				// on ne sauvegarde pas des champs vides
1763
				if ($this->multilangs["$key"]["label"] || $this->multilangs["$key"]["description"] || $this->multilangs["$key"]["note"])
1764
					dol_syslog(get_class($this).'::setMultiLangs', LOG_DEBUG);
1765
				if (!$this->db->query($sql2)) {
1766
					$this->error = $this->db->lasterror();
1767
					return -1;
1768
				}
1769
			}
1770
		}
1771
1772
		// Call trigger
1773
		$result = $this->call_trigger('CATEGORY_SET_MULTILANGS', $user);
1774
		if ($result < 0) {
1775
			$this->error = $this->db->lasterror();
1776
			return -1;
1777
		}
1778
		// End call triggers
1779
1780
		return 1;
1781
	}
1782
1783
	/**
1784
	 *	Load array this->multilangs
1785
	 *
1786
	 *	@return		int		<0 if KO, >0 if OK
1787
	 */
1788
	public function getMultiLangs()
1789
	{
1790
		global $langs;
1791
1792
		$current_lang = $langs->getDefaultLang();
1793
1794
		$sql = "SELECT lang, label, description";
1795
		$sql .= " FROM ".MAIN_DB_PREFIX."categorie_lang";
1796
		$sql .= " WHERE fk_category=".$this->id;
1797
1798
		$result = $this->db->query($sql);
1799
		if ($result) {
1800
			while ($obj = $this->db->fetch_object($result)) {
1801
				//print 'lang='.$obj->lang.' current='.$current_lang.'<br>';
1802
				if ($obj->lang == $current_lang) { // si on a les traduct. dans la langue courante on les charge en infos principales.
1803
					$this->label = $obj->label;
1804
					$this->description = $obj->description;
1805
				}
1806
				$this->multilangs["$obj->lang"]["label"] = $obj->label;
1807
				$this->multilangs["$obj->lang"]["description"] = $obj->description;
1808
			}
1809
			return 1;
1810
		} else {
1811
			$this->error = $langs->trans("Error")." : ".$this->db->error()." - ".$sql;
1812
			return -1;
1813
		}
1814
	}
1815
1816
	/**
1817
	 *	Return label of contact status
1818
	 *
1819
	 *	@param      int		$mode       0=Long label, 1=Short label, 2=Picto + Short label, 3=Picto, 4=Picto + Long label, 5=Short label + Picto, 6=Long label + Picto
1820
	 * 	@return 	string				Label of contact status
1821
	 */
1822
	public function getLibStatut($mode)
1823
	{
1824
		return '';
1825
	}
1826
1827
1828
	/**
1829
	 *  Initialise an instance with random values.
1830
	 *  Used to build previews or test instances.
1831
	 *	id must be 0 if object instance is a specimen.
1832
	 *
1833
	 *  @return	void
1834
	 */
1835
	public function initAsSpecimen()
1836
	{
1837
		dol_syslog(get_class($this)."::initAsSpecimen");
1838
1839
		// Initialise parametres
1840
		$this->id = 0;
1841
		$this->fk_parent = 0;
1842
		$this->label = 'SPECIMEN';
1843
		$this->specimen = 1;
1844
		$this->description = 'This is a description';
1845
		$this->socid = 1;
1846
		$this->type = self::TYPE_PRODUCT;
1847
	}
1848
1849
	/**
1850
	 * Function used to replace a thirdparty id with another one.
1851
	 *
1852
	 * @param DoliDB $db Database handler
1853
	 * @param int $origin_id Old thirdparty id
1854
	 * @param int $dest_id New thirdparty id
1855
	 * @return bool
1856
	 */
1857
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
1858
	{
1859
		$tables = array(
1860
			'categorie_societe'
1861
		);
1862
1863
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables, 1);
1864
	}
1865
1866
	/**
1867
	 * Return the addtional SQL JOIN query for filtering a list by a category
1868
	 *
1869
	 * @param string	$type			The category type (e.g Categorie::TYPE_WAREHOUSE)
1870
	 * @param string	$rowIdName		The name of the row id inside the whole sql query (e.g. "e.rowid")
1871
	 * @return string					A additional SQL JOIN query
1872
	 */
1873
	public static function getFilterJoinQuery($type, $rowIdName)
1874
	{
1875
		if ($type == 'bank_account') $type = 'account';
1876
1877
		return " LEFT JOIN ".MAIN_DB_PREFIX."categorie_".$type." as cp ON ".$rowIdName." = cp.fk_".$type;
1878
	}
1879
1880
	/**
1881
	 * Return the addtional SQL SELECT query for filtering a list by a category
1882
	 *
1883
	 * @param string	$type			The category type (e.g Categorie::TYPE_WAREHOUSE)
1884
	 * @param string	$rowIdName		The name of the row id inside the whole sql query (e.g. "e.rowid")
1885
	 * @param Array		$searchList		A list with the selected categories
1886
	 * @return string					A additional SQL SELECT query
1887
	 */
1888
	public static function getFilterSelectQuery($type, $rowIdName, $searchList)
1889
	{
1890
		if ($type == 'bank_account') $type = 'account';
1891
1892
		if (empty($searchList) && !is_array($searchList)) {
1893
			return "";
1894
		}
1895
1896
		$searchCategorySqlList = array();
1897
		foreach ($searchList as $searchCategory) {
1898
			if (intval($searchCategory) == -2) {
1899
				$searchCategorySqlList[] = " cp.fk_categorie IS NULL";
1900
			} elseif (intval($searchCategory) > 0) {
1901
				$searchCategorySqlList[] = " ".$rowIdName." IN (SELECT fk_".$type." FROM ".MAIN_DB_PREFIX."categorie_".$type." WHERE fk_categorie = ".((int) $searchCategory).")";
1902
			}
1903
		}
1904
1905
		if (!empty($searchCategorySqlList)) {
1906
			return " AND (".implode(' AND ', $searchCategorySqlList).")";
1907
		} else {
1908
			return "";
1909
		}
1910
	}
1911
1912
	/**
1913
	 *      Count all categories
1914
	 *
1915
	 *      @return int                             Number of categories, -1 on error
1916
	 */
1917
	public function countNbOfCategories()
1918
	{
1919
		dol_syslog(get_class($this)."::count_all_categories", LOG_DEBUG);
1920
		$sql = "SELECT COUNT(rowid) FROM ".MAIN_DB_PREFIX."categorie";
1921
		$sql .= " WHERE entity IN (".getEntity('category').")";
1922
1923
		$res = $this->db->query($sql);
1924
		if ($res) {
1925
			$obj = $this->db->fetch_object($res);
1926
			return $obj->count;
1927
		} else {
1928
			dol_print_error($this->db);
1929
			return -1;
1930
		}
1931
	}
1932
}
1933