Completed
Branch develop (b526f7)
by
unknown
29:42
created

CommonObject::isNull()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 1
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) 2006-2015 Laurent Destailleur  <[email protected]>
3
 * Copyright (C) 2005-2013 Regis Houssin        <[email protected]>
4
 * Copyright (C) 2010-2015 Juanjo Menent        <[email protected]>
5
 * Copyright (C) 2012-2013 Christophe Battarel  <[email protected]>
6
 * Copyright (C) 2011-2019 Philippe Grand       <[email protected]>
7
 * Copyright (C) 2012-2015 Marcos García        <[email protected]>
8
 * Copyright (C) 2012-2015 Raphaël Doursenaud   <[email protected]>
9
 * Copyright (C) 2012      Cedric Salvador      <[email protected]>
10
 * Copyright (C) 2015      Alexandre Spangaro   <[email protected]>
11
 * Copyright (C) 2016      Bahfir abbes         <[email protected]>
12
 * Copyright (C) 2017      ATM Consulting       <[email protected]>
13
 * Copyright (C) 2017      Nicolas ZABOURI      <[email protected]>
14
 * Copyright (C) 2017      Rui Strecht          <[email protected]>
15
 * Copyright (C) 2018      Frédéric France      <[email protected]>
16
 * Copyright (C) 2018      Josep Lluís Amador   <[email protected]>
17
 *
18
 * This program is free software; you can redistribute it and/or modify
19
 * it under the terms of the GNU General Public License as published by
20
 * the Free Software Foundation; either version 3 of the License, or
21
 * (at your option) any later version.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26
 * GNU General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU General Public License
29
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30
 */
31
32
/**
33
 *	\file       htdocs/core/class/commonobject.class.php
34
 *	\ingroup    core
35
 *	\brief      File of parent class of all other business classes (invoices, contracts, proposals, orders, ...)
36
 */
37
38
39
/**
40
 *	Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
41
 */
42
abstract class CommonObject
43
{
44
	/**
45
	 * @var DoliDb		Database handler (result of a new DoliDB)
46
	 */
47
	public $db;
48
49
	/**
50
	 * @var int The object identifier
51
	 */
52
	public $id;
53
54
	/**
55
	 * @var string 		Error string
56
	 */
57
	public $error;
58
59
	/**
60
	 * @var string[]	Array of error strings
61
	 */
62
	public $errors=array();
63
64
	/**
65
	 * @var string ID to identify managed object
66
	 */
67
	public $element;
68
69
	/**
70
	 * @var string Name of table without prefix where object is stored
71
	 */
72
	public $table_element;
73
74
	/**
75
	 * @var int    Name of subtable line
76
	 */
77
	public $table_element_line='';
78
79
	/**
80
	 * @var string		Key value used to track if data is coming from import wizard
81
	 */
82
	public $import_key;
83
84
	/**
85
	 * @var mixed		Contains data to manage extrafields
86
	 */
87
	public $array_options=array();
88
89
	/**
90
	 * @var int[][]		Array of linked objects ids. Loaded by ->fetchObjectLinked
91
	 */
92
	public $linkedObjectsIds;
93
94
	/**
95
	 * @var mixed		Array of linked objects. Loaded by ->fetchObjectLinked
96
	 */
97
	public $linkedObjects;
98
99
	/**
100
	 * @var Object      To store a cloned copy of object before to edit it and keep track of old properties
101
	 */
102
	public $oldcopy;
103
104
	/**
105
	 * @var string		Column name of the ref field.
106
	 */
107
	protected $table_ref_field = '';
108
109
110
111
	// Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
112
113
	/**
114
	 * @var array<string,mixed>		Can be used to pass information when only object is provided to method
115
	 */
116
	public $context=array();
117
118
	/**
119
	 * @var string		Contains canvas name if record is an alternative canvas record
120
	 */
121
	public $canvas;
122
123
	/**
124
	 * @var Project The related project
125
	 * @see fetch_projet()
126
	 */
127
	public $project;
128
129
	/**
130
	 * @var int The related project ID
131
	 * @see setProject(), project
132
	 */
133
	public $fk_project;
134
135
	/**
136
	 * @deprecated
137
	 * @see project
138
	 */
139
	public $projet;
140
141
	/**
142
	 * @var Contact a related contact
143
	 * @see fetch_contact()
144
	 */
145
	public $contact;
146
147
	/**
148
	 * @var int The related contact ID
149
	 * @see fetch_contact()
150
	 */
151
	public $contact_id;
152
153
	/**
154
	 * @var Societe A related thirdparty
155
	 * @see fetch_thirdparty()
156
	 */
157
	public $thirdparty;
158
159
	/**
160
	 * @var User A related user
161
	 * @see fetch_user()
162
	 */
163
	public $user;
164
165
	/**
166
	 * @var string 	The type of originating object ('commande', 'facture', ...)
167
	 * @see fetch_origin()
168
	 */
169
	public $origin;
170
171
	/**
172
	 * @var int 	The id of originating object
173
	 * @see fetch_origin()
174
	 */
175
	public $origin_id;
176
177
	/**
178
	 * @var string The object's reference
179
	 */
180
	public $ref;
181
182
	/**
183
	 * @var string The object's previous reference
184
	 */
185
	public $ref_previous;
186
187
	/**
188
	 * @var string The object's next reference
189
	 */
190
	public $ref_next;
191
192
	/**
193
	 * @var string An external reference for the object
194
	 */
195
	public $ref_ext;
196
197
	/**
198
	 * @var int The object's status
199
	 * @see setStatut()
200
	 */
201
	public $statut;
202
203
	/**
204
	 * @var string
205
	 * @see getFullAddress()
206
	 */
207
	public $country;
208
209
	/**
210
	 * @var int
211
	 * @see getFullAddress(), country
212
	 */
213
	public $country_id;
214
215
	/**
216
	 * @var string
217
	 * @see getFullAddress(), isInEEC(), country
218
	 */
219
    public $country_code;
220
221
    /**
222
	 * @var string
223
	 * @see getFullAddress()
224
	 */
225
	public $state;
226
227
	/**
228
	 * @var int
229
	 * @see getFullAddress(), state
230
	 */
231
	public $state_id;
232
233
	/**
234
	 * @var string
235
	 * @see getFullAddress(), state
236
	 */
237
    public $state_code;
238
239
    /**
240
	 * @var string
241
	 * @see getFullAddress(), region
242
	 */
243
	public $region;
244
245
	/**
246
	 * @var string
247
	 * @see getFullAddress(), region
248
	 */
249
    public $region_code;
250
251
	/**
252
	 * @var int
253
	 * @see fetch_barcode()
254
	 */
255
	public $barcode_type;
256
257
	/**
258
	 * @var string
259
	 * @see fetch_barcode(), barcode_type
260
	 */
261
	public $barcode_type_code;
262
263
	/**
264
	 * @var string
265
	 * @see fetch_barcode(), barcode_type
266
	 */
267
	public $barcode_type_label;
268
269
	/**
270
	 * @var string
271
	 * @see fetch_barcode(), barcode_type
272
	 */
273
	public $barcode_type_coder;
274
275
	/**
276
	 * @var int Payment method ID (cheque, cash, ...)
277
	 * @see setPaymentMethods()
278
	 */
279
	public $mode_reglement_id;
280
281
	/**
282
	 * @var int Payment terms ID
283
	 * @see setPaymentTerms()
284
	 */
285
	public $cond_reglement_id;
286
287
	/**
288
	 * @var int Payment terms ID
289
	 * @deprecated Kept for compatibility
290
	 * @see cond_reglement_id;
291
	 */
292
	public $cond_reglement;
293
294
	/**
295
	 * @var int Delivery address ID
296
	 * @deprecated
297
	 * @see setDeliveryAddress()
298
	 */
299
	public $fk_delivery_address;
300
301
	/**
302
	 * @var int Shipping method ID
303
	 * @see setShippingMethod()
304
	 */
305
	public $shipping_method_id;
306
307
	/**
308
	 * @var string
309
	 * @see SetDocModel()
310
	 */
311
	public $modelpdf;
312
313
	/**
314
	 * @var string
315
	 * Contains relative path of last generated main file
316
	 */
317
	public $last_main_doc;
318
319
	/**
320
	 * @var int Bank account ID
321
	 * @see SetBankAccount()
322
	 */
323
	public $fk_account;
324
325
	/**
326
	 * @var string Public note
327
	 * @see update_note()
328
	 */
329
	public $note_public;
330
331
	/**
332
	 * @var string Private note
333
	 * @see update_note()
334
	 */
335
	public $note_private;
336
337
	/**
338
	 * @deprecated
339
	 * @see $note_public
340
	 */
341
	public $note;
342
343
	/**
344
	 * @var float Total amount before taxes
345
	 * @see update_price()
346
	 */
347
	public $total_ht;
348
349
	/**
350
	 * @var float Total VAT amount
351
	 * @see update_price()
352
	 */
353
	public $total_tva;
354
355
	/**
356
	 * @var float Total local tax 1 amount
357
	 * @see update_price()
358
	 */
359
	public $total_localtax1;
360
361
	/**
362
	 * @var float Total local tax 2 amount
363
	 * @see update_price()
364
	 */
365
	public $total_localtax2;
366
367
	/**
368
	 * @var float Total amount with taxes
369
	 * @see update_price()
370
	 */
371
	public $total_ttc;
372
373
	/**
374
	 * @var CommonObjectLine[]
375
	 */
376
	public $lines;
377
378
	/**
379
	 * @var mixed		Contains comments
380
	 * @see fetchComments()
381
	 */
382
	public $comments=array();
383
384
	/**
385
	 * @var int
386
	 * @see setIncoterms()
387
	 */
388
	public $fk_incoterms;
389
390
	/**
391
	 * @var string
392
	 * @see SetIncoterms()
393
	 */
394
	public $libelle_incoterms;
395
396
	/**
397
	 * @var string
398
	 * @see display_incoterms()
399
	 */
400
	public $location_incoterms;
401
402
	/**
403
	 * @var string The name
404
	 */
405
	public $name;
406
407
    /**
408
     * @var string The lastname
409
     */
410
	public $lastname;
411
412
    /**
413
     * @var string The firstname
414
     */
415
	public $firstname;
416
417
    /**
418
     * @var string The civility code, not an integer
419
     */
420
	public $civility_id;
421
422
	// Dates
423
	public $date_creation;			// Date creation
424
	public $date_validation;		// Date validation
425
	public $date_modification;		// Date last change (tms field)
426
427
428
429
	// No constructor as it is an abstract class
430
431
	/**
432
	 * Check an object id/ref exists
433
	 * If you don't need/want to instantiate object and just need to know if object exists, use this method instead of fetch
434
	 *
435
	 *  @param	string	$element   	String of element ('product', 'facture', ...)
436
	 *  @param	int		$id      	Id of object
437
	 *  @param  string	$ref     	Ref of object to check
438
	 *  @param	string	$ref_ext	Ref ext of object to check
439
	 *  @return int     			<0 if KO, 0 if OK but not found, >0 if OK and exists
440
	 */
441
	public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
442
	{
443
		global $db,$conf;
444
445
		$sql = "SELECT rowid, ref, ref_ext";
446
		$sql.= " FROM ".MAIN_DB_PREFIX.$element;
447
		$sql.= " WHERE entity IN (".getEntity($element).")" ;
448
449
		if ($id > 0) $sql.= " AND rowid = ".$db->escape($id);
450
		elseif ($ref) $sql.= " AND ref = '".$db->escape($ref)."'";
451
		elseif ($ref_ext) $sql.= " AND ref_ext = '".$db->escape($ref_ext)."'";
452
		else {
453
			$error='ErrorWrongParameters';
454
			dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
455
			return -1;
456
		}
457
		if ($ref || $ref_ext) $sql.= " AND entity = ".$conf->entity;
458
459
		dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
460
		$resql = $db->query($sql);
461
		if ($resql)
462
		{
463
			$num=$db->num_rows($resql);
464
			if ($num > 0) return 1;
465
			else return 0;
466
		}
467
		return -1;
468
	}
469
470
	/**
471
	 * Method to output saved errors
472
	 *
473
	 * @return	string		String with errors
474
	 */
475
	public function errorsToString()
476
	{
477
		return $this->error.(is_array($this->errors)?(($this->error!=''?', ':'').join(', ', $this->errors)):'');
478
	}
479
480
	/**
481
	 * Return customer ref for screen output.
482
	 *
483
	 * @param  string      $objref        Customer ref
484
	 * @return string                     Customer ref formated
485
	 */
486
	public function getFormatedCustomerRef($objref)
487
	{
488
	    global $hookmanager;
489
490
	    $parameters=array('objref'=>$objref);
491
	    $action='';
492
	    $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
493
	    if ($reshook > 0)
494
	    {
495
	        return $hookmanager->resArray['objref'];
496
	    }
497
	    return $objref.(isset($hookmanager->resArray['objref'])?$hookmanager->resArray['objref']:'');
498
	}
499
500
	/**
501
	 * Return supplier ref for screen output.
502
	 *
503
	 * @param  string      $objref        Supplier ref
504
	 * @return string                     Supplier ref formated
505
	 */
506
	public function getFormatedSupplierRef($objref)
507
	{
508
	    global $hookmanager;
509
510
	    $parameters=array('objref'=>$objref);
511
	    $action='';
512
	    $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
513
	    if ($reshook > 0)
514
	    {
515
	        return $hookmanager->resArray['objref'];
516
	    }
517
	    return $objref.(isset($hookmanager->resArray['objref'])?$hookmanager->resArray['objref']:'');
518
	}
519
520
	/**
521
	 *	Return full name (civility+' '+name+' '+lastname)
522
	 *
523
	 *	@param	Translate	$langs			Language object for translation of civility (used only if option is 1)
524
	 *	@param	int			$option			0=No option, 1=Add civility
525
	 * 	@param	int			$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname, 3=Firstname if defined else lastname
526
	 * 	@param	int			$maxlen			Maximum length
527
	 * 	@return	string						String with full name
528
	 */
529
	public function getFullName($langs, $option = 0, $nameorder = -1, $maxlen = 0)
530
	{
531
		//print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n";
532
		$lastname=$this->lastname;
533
		$firstname=$this->firstname;
534
		if (empty($lastname))  $lastname=(isset($this->lastname)?$this->lastname:(isset($this->name)?$this->name:(isset($this->nom)?$this->nom:(isset($this->societe)?$this->societe:(isset($this->company)?$this->company:'')))));
535
536
		$ret='';
537
		if ($option && $this->civility_id)
538
		{
539
			if ($langs->transnoentitiesnoconv("Civility".$this->civility_id)!="Civility".$this->civility_id) $ret.=$langs->transnoentitiesnoconv("Civility".$this->civility_id).' ';
540
			else $ret.=$this->civility_id.' ';
541
		}
542
543
		$ret.=dolGetFirstLastname($firstname, $lastname, $nameorder);
544
545
		return dol_trunc($ret, $maxlen);
546
	}
547
548
	/**
549
	 * 	Return full address of contact
550
	 *
551
	 * 	@param		int			$withcountry		1=Add country into address string
552
	 *  @param		string		$sep				Separator to use to build string
553
	 *  @param		int		    $withregion			1=Add region into address string
554
	 *	@return		string							Full address string
555
	 */
556
	public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0)
557
	{
558
		if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country)))
559
		{
560
			require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
561
			$tmparray=getCountry($this->country_id, 'all');
562
			$this->country_code=$tmparray['code'];
563
			$this->country     =$tmparray['label'];
564
		}
565
566
        if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_cpde)))
567
    	{
568
    		require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
569
    		$tmparray=getState($this->state_id, 'all', 0, 1);
570
			$this->state_code   =$tmparray['code'];
571
			$this->state        =$tmparray['label'];
572
			$this->region_code  =$tmparray['region_code'];
573
			$this->region       =$tmparray['region'];
574
        }
575
576
		return dol_format_address($this, $withcountry, $sep);
577
	}
578
579
580
	/**
581
	 * 	Return full address for banner
582
	 *
583
	 * 	@param		string		$htmlkey            HTML id to make banner content unique
584
	 *  @param      Object      $object				Object (thirdparty, thirdparty of contact for contact, null for a member)
585
	 *	@return		string							Full address string
586
	 */
587
	public function getBannerAddress($htmlkey, $object)
588
	{
589
		global $conf, $langs;
590
591
		$countriesusingstate=array('AU','US','IN','GB','ES','UK','TR');    // See also option MAIN_FORCE_STATE_INTO_ADDRESS
592
593
		$contactid=0;
594
		$thirdpartyid=0;
595
		if ($this->element == 'societe')
596
		{
597
			$thirdpartyid=$this->id;
598
		}
599
		if ($this->element == 'contact')
600
		{
601
			$contactid=$this->id;
602
			$thirdpartyid=$object->fk_soc;
603
		}
604
		if ($this->element == 'user')
605
		{
606
			$contactid=$this->contact_id;
607
			$thirdpartyid=$object->fk_soc;
608
		}
609
610
		$out='<!-- BEGIN part to show address block -->';
611
612
		$outdone=0;
613
		$coords = $this->getFullAddress(1, ', ', $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT);
614
		if ($coords)
615
		{
616
			if (! empty($conf->use_javascript_ajax))
617
			{
618
				$namecoords = $this->getFullName($langs, 1).'<br>'.$coords;
619
				// hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile
620
				$out.='<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">';
621
				$out.=img_picto($langs->trans("Address"), 'object_address.png');
622
				$out.='</a> ';
623
			}
624
			$out.=dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); $outdone++;
625
			$outdone++;
626
		}
627
628
		if (! in_array($this->country_code, $countriesusingstate) && empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)   // If MAIN_FORCE_STATE_INTO_ADDRESS is on, state is already returned previously with getFullAddress
629
				&& empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state)
630
		{
631
            if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) {
632
                $out.=($outdone?' - ':'').$this->region.' - '.$this->state;
633
            }
634
            else {
635
                $out.=($outdone?' - ':'').$this->state;
636
            }
637
			$outdone++;
638
		}
639
640
		if (! empty($this->phone) || ! empty($this->phone_pro) || ! empty($this->phone_mobile) || ! empty($this->phone_perso) || ! empty($this->fax) || ! empty($this->office_phone) || ! empty($this->user_mobile) || ! empty($this->office_fax)) $out.=($outdone?'<br>':'');
641
		if (! empty($this->phone) && empty($this->phone_pro)) {		// For objects that store pro phone into ->phone
642
			$out.=dol_print_phone($this->phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro")); $outdone++;
643
		}
644
		if (! empty($this->phone_pro)) {
645
			$out.=dol_print_phone($this->phone_pro, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro")); $outdone++;
646
		}
647
		if (! empty($this->phone_mobile)) {
648
			$out.=dol_print_phone($this->phone_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'mobile', $langs->trans("PhoneMobile")); $outdone++;
649
		}
650
		if (! empty($this->phone_perso)) {
651
			$out.=dol_print_phone($this->phone_perso, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePerso")); $outdone++;
652
		}
653
		if (! empty($this->office_phone)) {
654
			$out.=dol_print_phone($this->office_phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro")); $outdone++;
655
		}
656
		if (! empty($this->user_mobile)) {
657
			$out.=dol_print_phone($this->user_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'mobile', $langs->trans("PhoneMobile")); $outdone++;
658
		}
659
		if (! empty($this->fax)) {
660
			$out.=dol_print_phone($this->fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', '&nbsp;', 'fax', $langs->trans("Fax")); $outdone++;
661
		}
662
		if (! empty($this->office_fax)) {
663
			$out.=dol_print_phone($this->office_fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', '&nbsp;', 'fax', $langs->trans("Fax")); $outdone++;
664
		}
665
666
		$out.='<div style="clear: both;"></div>';
667
		$outdone=0;
668
		if (! empty($this->email))
669
		{
670
			$out.=dol_print_email($this->email, $this->id, $object->id, 'AC_EMAIL', 0, 0, 1);
671
			$outdone++;
672
		}
673
		if (! empty($this->url))
674
		{
675
            //$out.=dol_print_url($this->url,'_goout',0,1);//steve changed to blank
676
		    $out.=dol_print_url($this->url, '_blank', 0, 1);
677
			$outdone++;
678
		}
679
		$out.='<div style="clear: both;">';
680
		if (! empty($conf->socialnetworks->enabled))
681
		{
682
			if ($this->skype) $out.=dol_print_socialnetworks($this->skype, $this->id, $object->id, 'skype');
683
			$outdone++;
684
			if ($this->jabberid) $out.=dol_print_socialnetworks($this->jabberid, $this->id, $object->id, 'jabber');
685
			$outdone++;
686
			if ($this->twitter) $out.=dol_print_socialnetworks($this->twitter, $this->id, $object->id, 'twitter');
687
			$outdone++;
688
			if ($this->facebook) $out.=dol_print_socialnetworks($this->facebook, $this->id, $object->id, 'facebook');
689
			$outdone++;
690
			if ($this->linkedin) $out.=dol_print_socialnetworks($this->linkedin, $this->id, $object->id, 'linkedin');
691
			$outdone++;
692
		}
693
		$out.='</div>';
694
695
		$out.='<!-- END Part to show address block -->';
696
697
		return $out;
698
	}
699
700
	/**
701
	 * Return the link of last main doc file for direct public download.
702
	 *
703
	 * @param	string	$modulepart			Module related to document
704
	 * @param	int		$initsharekey		Init the share key if it was not yet defined
705
	 * @param	int		$relativelink		0=Return full external link, 1=Return link relative to root of file
706
	 * @return	string						Link or empty string if there is no download link
707
	 */
708
	public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
709
	{
710
		global $user, $dolibarr_main_url_root;
711
712
		if (empty($this->last_main_doc))
713
		{
714
			return '';		// No way to known which document name to use
715
		}
716
717
		include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
718
		$ecmfile=new EcmFiles($this->db);
719
		$result = $ecmfile->fetch(0, '', $this->last_main_doc);
720
		if ($result < 0)
721
		{
722
			$this->error = $ecmfile->error;
723
			$this->errors = $ecmfile->errors;
724
			return -1;
725
		}
726
727
		if (empty($ecmfile->id))
728
		{
729
			// Add entry into index
730
			if ($initsharekey)
731
			{
732
				require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
733
				// TODO We can't, we dont' have full path of file, only last_main_doc adn ->element, so we must rebuild full path first
734
				/*
735
				$ecmfile->filepath = $rel_dir;
736
				$ecmfile->filename = $filename;
737
				$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
738
				$ecmfile->fullpath_orig = '';
739
				$ecmfile->gen_or_uploaded = 'generated';
740
				$ecmfile->description = '';    // indexed content
741
				$ecmfile->keyword = '';        // keyword content
742
				$ecmfile->share = getRandomPassword(true);
743
				$result = $ecmfile->create($user);
744
				if ($result < 0)
745
				{
746
					$this->error = $ecmfile->error;
747
					$this->errors = $ecmfile->errors;
748
				}
749
				*/
750
			}
751
			else return '';
752
		}
753
		elseif (empty($ecmfile->share))
754
		{
755
			// Add entry into index
756
			if ($initsharekey)
757
			{
758
				require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
759
				$ecmfile->share = getRandomPassword(true);
760
				$ecmfile->update($user);
761
			}
762
			else return '';
763
		}
764
		// Define $urlwithroot
765
		$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
766
		// This is to use external domain name found into config file
767
		//if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
768
		//else
769
		$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;
770
		//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
771
772
		$forcedownload=0;
773
774
		$paramlink='';
775
		//if (! empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart;		// For sharing with hash (so public files), modulepart is not required.
776
		//if (! empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; 					// For sharing with hash (so public files), entity is not required.
777
		//$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath);								// No need of name of file for public link, we will use the hash
778
		if (! empty($ecmfile->share)) $paramlink.=($paramlink?'&':'').'hashp='.$ecmfile->share;			// Hash for public share
779
		if ($forcedownload) $paramlink.=($paramlink?'&':'').'attachment=1';
780
781
		if ($relativelink)
782
		{
783
			$linktoreturn='document.php'.($paramlink?'?'.$paramlink:'');
784
		}
785
		else
786
		{
787
			$linktoreturn=$urlwithroot.'/document.php'.($paramlink?'?'.$paramlink:'');
788
		}
789
790
		// Here $ecmfile->share is defined
791
		return $linktoreturn;
792
	}
793
794
795
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
796
	/**
797
	 *  Add a link between element $this->element and a contact
798
	 *
799
	 *  @param	int		$fk_socpeople       Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
800
	 *  @param 	int		$type_contact 		Type of contact (code or id). Must be id or code found into table llx_c_type_contact. For example: SALESREPFOLL
801
	 *  @param  string	$source             external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
802
	 *  @param  int		$notrigger			Disable all triggers
803
	 *  @return int                 		<0 if KO, >0 if OK
804
	 */
805
	public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
806
	{
807
        // phpcs:enable
808
		global $user,$langs;
809
810
811
		dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
812
813
		// Check parameters
814
		if ($fk_socpeople <= 0)
815
		{
816
			$langs->load("errors");
817
			$this->error=$langs->trans("ErrorWrongValueForParameterX", "1");
818
			dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
819
			return -1;
820
		}
821
		if (! $type_contact)
822
		{
823
			$langs->load("errors");
824
			$this->error=$langs->trans("ErrorWrongValueForParameterX", "2");
825
			dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
826
			return -2;
827
		}
828
829
		$id_type_contact=0;
830
		if (is_numeric($type_contact))
831
		{
832
			$id_type_contact=$type_contact;
833
		}
834
		else
835
		{
836
			// We look for id type_contact
837
			$sql = "SELECT tc.rowid";
838
			$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
839
			$sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
840
			$sql.= " AND tc.source='".$this->db->escape($source)."'";
841
			$sql.= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
842
			//print $sql;
843
			$resql=$this->db->query($sql);
844
			if ($resql)
845
			{
846
				$obj = $this->db->fetch_object($resql);
847
				if ($obj) $id_type_contact=$obj->rowid;
848
			}
849
		}
850
851
		if ($id_type_contact == 0)
852
		{
853
			$this->error='CODE_NOT_VALID_FOR_THIS_ELEMENT';
854
			dol_syslog("CODE_NOT_VALID_FOR_THIS_ELEMENT: Code type of contact '".$type_contact."' does not exists or is not active for element ".$this->element.", we can ignore it");
855
			return -3;
856
		}
857
858
		$datecreate = dol_now();
859
860
		// Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
861
		$TListeContacts=$this->liste_contact(-1, $source);
862
		$already_added=false;
863
		if(!empty($TListeContacts)) {
864
			foreach($TListeContacts as $array_contact) {
865
				if($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
866
					$already_added=true;
867
					break;
868
				}
869
			}
870
		}
871
872
		if(!$already_added) {
873
874
			$this->db->begin();
875
876
			// Insert into database
877
			$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact";
878
			$sql.= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
879
			$sql.= " VALUES (".$this->id.", ".$fk_socpeople." , " ;
880
			$sql.= "'".$this->db->idate($datecreate)."'";
881
			$sql.= ", 4, ". $id_type_contact;
882
			$sql.= ")";
883
884
			$resql=$this->db->query($sql);
885
			if ($resql)
886
			{
887
				if (! $notrigger)
888
				{
889
					$result=$this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
890
					if ($result < 0)
891
					{
892
						$this->db->rollback();
893
						return -1;
894
					}
895
				}
896
897
				$this->db->commit();
898
				return 1;
899
			}
900
			else
901
			{
902
				if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
903
				{
904
					$this->error=$this->db->errno();
905
					$this->db->rollback();
906
					echo 'err rollback';
907
					return -2;
908
				}
909
				else
910
				{
911
					$this->error=$this->db->error();
912
					$this->db->rollback();
913
					return -1;
914
				}
915
			}
916
		} else return 0;
917
	}
918
919
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
920
	/**
921
	 *    Copy contact from one element to current
922
	 *
923
	 *    @param    CommonObject    $objFrom    Source element
924
	 *    @param    string          $source     Nature of contact ('internal' or 'external')
925
	 *    @return   int                         >0 if OK, <0 if KO
926
	 */
927
	public function copy_linked_contact($objFrom, $source = 'internal')
928
	{
929
        // phpcs:enable
930
		$contacts = $objFrom->liste_contact(-1, $source);
931
		foreach($contacts as $contact)
932
		{
933
			if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0)
934
			{
935
				$this->error=$this->db->lasterror();
936
				return -1;
937
			}
938
		}
939
		return 1;
940
	}
941
942
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
943
	/**
944
	 *      Update a link to contact line
945
	 *
946
	 *      @param	int		$rowid              Id of line contact-element
947
	 * 		@param	int		$statut	            New status of link
948
	 *      @param  int		$type_contact_id    Id of contact type (not modified if 0)
949
	 *      @param  int		$fk_socpeople	    Id of soc_people to update (not modified if 0)
950
	 *      @return int                 		<0 if KO, >= 0 if OK
951
	 */
952
	public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
953
	{
954
        // phpcs:enable
955
		// Insert into database
956
		$sql = "UPDATE ".MAIN_DB_PREFIX."element_contact set";
957
		$sql.= " statut = ".$statut;
958
		if ($type_contact_id) $sql.= ", fk_c_type_contact = '".$type_contact_id ."'";
959
		if ($fk_socpeople) $sql.= ", fk_socpeople = '".$fk_socpeople ."'";
960
		$sql.= " where rowid = ".$rowid;
961
		$resql=$this->db->query($sql);
962
		if ($resql)
963
		{
964
			return 0;
965
		}
966
		else
967
		{
968
			$this->error=$this->db->lasterror();
969
			return -1;
970
		}
971
	}
972
973
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
974
	/**
975
	 *    Delete a link to contact line
976
	 *
977
	 *    @param	int		$rowid			Id of contact link line to delete
978
	 *    @param	int		$notrigger		Disable all triggers
979
	 *    @return   int						>0 if OK, <0 if KO
980
	 */
981
	public function delete_contact($rowid, $notrigger = 0)
982
	{
983
        // phpcs:enable
984
		global $user;
985
986
987
		$this->db->begin();
988
989
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
990
		$sql.= " WHERE rowid =".$rowid;
991
992
		dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
993
		if ($this->db->query($sql))
994
		{
995
			if (! $notrigger)
996
			{
997
				$result=$this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
998
				if ($result < 0) { $this->db->rollback(); return -1; }
999
			}
1000
1001
			$this->db->commit();
1002
			return 1;
1003
		}
1004
		else
1005
		{
1006
			$this->error=$this->db->lasterror();
1007
			$this->db->rollback();
1008
			return -1;
1009
		}
1010
	}
1011
1012
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1013
	/**
1014
	 *    Delete all links between an object $this and all its contacts
1015
	 *
1016
	 *	  @param	string	$source		'' or 'internal' or 'external'
1017
	 *	  @param	string	$code		Type of contact (code or id)
1018
	 *    @return   int					>0 if OK, <0 if KO
1019
	 */
1020
	public function delete_linked_contact($source = '', $code = '')
1021
	{
1022
        // phpcs:enable
1023
		$temp = array();
1024
		$typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1025
1026
		foreach($typeContact as $key => $value)
1027
		{
1028
			array_push($temp, $key);
1029
		}
1030
		$listId = implode(",", $temp);
1031
1032
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1033
		$sql.= " WHERE element_id = ".$this->id;
1034
		if ($listId)
1035
			$sql.= " AND fk_c_type_contact IN (".$listId.")";
1036
1037
		dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1038
		if ($this->db->query($sql))
1039
		{
1040
			return 1;
1041
		}
1042
		else
1043
		{
1044
			$this->error=$this->db->lasterror();
1045
			return -1;
1046
		}
1047
	}
1048
1049
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1050
	/**
1051
	 *    Get array of all contacts for an object
1052
	 *
1053
	 *    @param	int			$statut		Status of links to get (-1=all)
1054
	 *    @param	string		$source		Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)
1055
	 *    @param	int         $list       0:Return array contains all properties, 1:Return array contains just id
1056
	 *    @param    string      $code       Filter on this code of contact type ('SHIPPING', 'BILLING', ...)
1057
	 *    @return	array|int		        Array of contacts, -1 if error
1058
	 */
1059
	public function liste_contact($statut = -1, $source = 'external', $list = 0, $code = '')
1060
	{
1061
        // phpcs:enable
1062
		global $langs;
1063
1064
		$tab=array();
1065
1066
		$sql = "SELECT ec.rowid, ec.statut as statuslink, ec.fk_socpeople as id, ec.fk_c_type_contact";    // This field contains id of llx_socpeople or id of llx_user
1067
		if ($source == 'internal') $sql.=", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
1068
		if ($source == 'external' || $source == 'thirdparty') $sql.=", t.fk_soc as socid, t.statut as statuscontact";
1069
		$sql.= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1070
		$sql.= ", tc.source, tc.element, tc.code, tc.libelle";
1071
		$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
1072
		$sql.= ", ".MAIN_DB_PREFIX."element_contact ec";
1073
		if ($source == 'internal') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
1074
		if ($source == 'external'|| $source == 'thirdparty') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
1075
		$sql.= " WHERE ec.element_id =".$this->id;
1076
		$sql.= " AND ec.fk_c_type_contact=tc.rowid";
1077
		$sql.= " AND tc.element='".$this->db->escape($this->element)."'";
1078
		if ($code) $sql.= " AND tc.code = '".$this->db->escape($code)."'";
1079
		if ($source == 'internal') $sql.= " AND tc.source = 'internal'";
1080
		if ($source == 'external' || $source == 'thirdparty') $sql.= " AND tc.source = 'external'";
1081
		$sql.= " AND tc.active=1";
1082
		if ($statut >= 0) $sql.= " AND ec.statut = '".$statut."'";
1083
		$sql.=" ORDER BY t.lastname ASC";
1084
1085
		dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1086
		$resql=$this->db->query($sql);
1087
		if ($resql)
1088
		{
1089
			$num=$this->db->num_rows($resql);
1090
			$i=0;
1091
			while ($i < $num)
1092
			{
1093
				$obj = $this->db->fetch_object($resql);
1094
1095
				if (! $list)
1096
				{
1097
					$transkey="TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1098
					$libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1099
					$tab[$i]=array('source'=>$obj->source,'socid'=>$obj->socid,'id'=>$obj->id,
1100
								   'nom'=>$obj->lastname,      // For backward compatibility
1101
								   'civility'=>$obj->civility, 'lastname'=>$obj->lastname, 'firstname'=>$obj->firstname, 'email'=>$obj->email, 'login'=>$obj->login, 'photo'=>$obj->photo, 'statuscontact'=>$obj->statuscontact,
1102
								   'rowid'=>$obj->rowid, 'code'=>$obj->code, 'libelle'=>$libelle_type, 'status'=>$obj->statuslink, 'fk_c_type_contact'=>$obj->fk_c_type_contact);
1103
				}
1104
				else
1105
				{
1106
					$tab[$i]=$obj->id;
1107
				}
1108
1109
				$i++;
1110
			}
1111
1112
			return $tab;
1113
		}
1114
		else
1115
		{
1116
			$this->error=$this->db->lasterror();
1117
			dol_print_error($this->db);
1118
			return -1;
1119
		}
1120
	}
1121
1122
1123
	/**
1124
	 * 		Update status of a contact linked to object
1125
	 *
1126
	 * 		@param	int		$rowid		Id of link between object and contact
1127
	 * 		@return	int					<0 if KO, >=0 if OK
1128
	 */
1129
	public function swapContactStatus($rowid)
1130
	{
1131
		$sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1132
		$sql.= " tc.code, tc.libelle";
1133
		//$sql.= ", s.fk_soc";
1134
		$sql.= " FROM (".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as tc)";
1135
		//$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as s ON ec.fk_socpeople=s.rowid";	// Si contact de type external, alors il est lie a une societe
1136
		$sql.= " WHERE ec.rowid =".$rowid;
1137
		$sql.= " AND ec.fk_c_type_contact=tc.rowid";
1138
		$sql.= " AND tc.element = '".$this->db->escape($this->element)."'";
1139
1140
		dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1141
		$resql=$this->db->query($sql);
1142
		if ($resql)
1143
		{
1144
			$obj = $this->db->fetch_object($resql);
1145
			$newstatut = ($obj->statut == 4) ? 5 : 4;
1146
			$result = $this->update_contact($rowid, $newstatut);
1147
			$this->db->free($resql);
1148
			return $result;
1149
		}
1150
		else
1151
		{
1152
			$this->error=$this->db->error();
1153
			dol_print_error($this->db);
1154
			return -1;
1155
		}
1156
	}
1157
1158
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1159
	/**
1160
	 *      Return array with list of possible values for type of contacts
1161
	 *
1162
	 *      @param	string	$source     'internal', 'external' or 'all'
1163
	 *      @param	string	$order		Sort order by : 'position', 'code', 'rowid'...
1164
	 *      @param  int		$option     0=Return array id->label, 1=Return array code->label
1165
	 *      @param  int		$activeonly 0=all status of contact, 1=only the active
1166
	 *		@param	string	$code		Type of contact (Example: 'CUSTOMER', 'SERVICE')
1167
	 *      @return array       		Array list of type of contacts (id->label if option=0, code->label if option=1)
1168
	 */
1169
	public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1170
	{
1171
        // phpcs:enable
1172
		global $langs;
1173
1174
		if (empty($order)) $order='position';
1175
		if ($order == 'position') $order.=',code';
1176
1177
		$tab = array();
1178
		$sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1179
		$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
1180
		$sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
1181
		if ($activeonly == 1) $sql.= " AND tc.active=1"; // only the active types
1182
		if (! empty($source) && $source != 'all') $sql.= " AND tc.source='".$this->db->escape($source)."'";
1183
		if (! empty($code)) $sql.= " AND tc.code='".$this->db->escape($code)."'";
1184
		$sql.= $this->db->order($order, 'ASC');
1185
1186
		//print "sql=".$sql;
1187
		$resql=$this->db->query($sql);
1188
		if ($resql)
1189
		{
1190
			$num=$this->db->num_rows($resql);
1191
			$i=0;
1192
			while ($i < $num)
1193
			{
1194
				$obj = $this->db->fetch_object($resql);
1195
1196
				$transkey="TypeContact_".$this->element."_".$source."_".$obj->code;
1197
				$libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1198
				if (empty($option)) $tab[$obj->rowid]=$libelle_type;
1199
				else $tab[$obj->code]=$libelle_type;
1200
				$i++;
1201
			}
1202
			return $tab;
1203
		}
1204
		else
1205
		{
1206
			$this->error=$this->db->lasterror();
1207
			//dol_print_error($this->db);
1208
			return null;
1209
		}
1210
	}
1211
1212
	/**
1213
	 *      Return id of contacts for a source and a contact code.
1214
	 *      Example: contact client de facturation ('external', 'BILLING')
1215
	 *      Example: contact client de livraison ('external', 'SHIPPING')
1216
	 *      Example: contact interne suivi paiement ('internal', 'SALESREPFOLL')
1217
	 *
1218
	 *		@param	string	$source		'external' or 'internal'
1219
	 *		@param	string	$code		'BILLING', 'SHIPPING', 'SALESREPFOLL', ...
1220
	 *		@param	int		$status		limited to a certain status
1221
	 *      @return array       		List of id for such contacts
1222
	 */
1223
	public function getIdContact($source, $code, $status = 0)
1224
	{
1225
		global $conf;
1226
1227
		$result=array();
1228
		$i=0;
1229
		//cas particulier pour les expeditions
1230
		if ($this->element=='shipping' && $this->origin_id != 0) {
1231
			$id=$this->origin_id;
1232
			$element='commande';
1233
		} elseif ($this->element=='reception' && $this->origin_id != 0) {
1234
			$id=$this->origin_id;
1235
			$element='order_supplier';
1236
		} else {
1237
			$id=$this->id;
1238
			$element=$this->element;
1239
		}
1240
1241
		$sql = "SELECT ec.fk_socpeople";
1242
		$sql.= " FROM ".MAIN_DB_PREFIX."element_contact as ec,";
1243
		if ($source == 'internal') $sql.= " ".MAIN_DB_PREFIX."user as c,";
1244
		if ($source == 'external') $sql.= " ".MAIN_DB_PREFIX."socpeople as c,";
1245
		$sql.= " ".MAIN_DB_PREFIX."c_type_contact as tc";
1246
		$sql.= " WHERE ec.element_id = ".$id;
1247
		$sql.= " AND ec.fk_socpeople = c.rowid";
1248
		if ($source == 'internal') $sql.= " AND c.entity IN (".getEntity('user').")";
1249
		if ($source == 'external') $sql.= " AND c.entity IN (".getEntity('societe').")";
1250
		$sql.= " AND ec.fk_c_type_contact = tc.rowid";
1251
		$sql.= " AND tc.element = '".$element."'";
1252
		$sql.= " AND tc.source = '".$source."'";
1253
		if ($code) $sql.= " AND tc.code = '".$code."'";
1254
		$sql.= " AND tc.active = 1";
1255
		if ($status) $sql.= " AND ec.statut = ".$status;
1256
1257
		dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1258
		$resql=$this->db->query($sql);
1259
		if ($resql)
1260
		{
1261
			while ($obj = $this->db->fetch_object($resql))
1262
			{
1263
				$result[$i]=$obj->fk_socpeople;
1264
				$i++;
1265
			}
1266
		}
1267
		else
1268
		{
1269
			$this->error=$this->db->error();
1270
			return null;
1271
		}
1272
1273
		return $result;
1274
	}
1275
1276
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1277
	/**
1278
	 *		Load object contact with id=$this->contactid into $this->contact
1279
	 *
1280
	 *		@param	int		$contactid      Id du contact. Use this->contactid if empty.
1281
	 *		@return	int						<0 if KO, >0 if OK
1282
	 */
1283
	public function fetch_contact($contactid = null)
1284
	{
1285
        // phpcs:enable
1286
		if (empty($contactid)) $contactid=$this->contactid;
1287
1288
		if (empty($contactid)) return 0;
1289
1290
		require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1291
		$contact = new Contact($this->db);
1292
		$result=$contact->fetch($contactid);
1293
		$this->contact = $contact;
1294
		return $result;
1295
	}
1296
1297
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1298
	/**
1299
	 *    	Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty
1300
	 *
1301
	 *		@param		int		$force_thirdparty_id	Force thirdparty id
1302
	 *		@return		int								<0 if KO, >0 if OK
1303
	 */
1304
	public function fetch_thirdparty($force_thirdparty_id = 0)
1305
	{
1306
        // phpcs:enable
1307
		global $conf;
1308
1309
		if (empty($this->socid) && empty($this->fk_soc) && empty($this->fk_thirdparty) && empty($force_thirdparty_id))
1310
			return 0;
1311
1312
		require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1313
1314
		$idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : $this->fk_thirdparty);
1315
		if ($force_thirdparty_id)
1316
			$idtofetch = $force_thirdparty_id;
1317
1318
		if ($idtofetch) {
1319
			$thirdparty = new Societe($this->db);
1320
			$result = $thirdparty->fetch($idtofetch);
1321
			$this->thirdparty = $thirdparty;
1322
1323
			// Use first price level if level not defined for third party
1324
			if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1325
				$this->thirdparty->price_level = 1;
1326
			}
1327
1328
			return $result;
1329
		} else
1330
			return -1;
1331
	}
1332
1333
1334
	/**
1335
	 * Looks for an object with ref matching the wildcard provided
1336
	 * It does only work when $this->table_ref_field is set
1337
	 *
1338
	 * @param string $ref Wildcard
1339
	 * @return int >1 = OK, 0 = Not found or table_ref_field not defined, <0 = KO
1340
	 */
1341
	public function fetchOneLike($ref)
1342
	{
1343
		if (!$this->table_ref_field) {
1344
			return 0;
1345
		}
1346
1347
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE '.$this->table_ref_field.' LIKE "'.$this->db->escape($ref).'" LIMIT 1';
1348
1349
		$query = $this->db->query($sql);
1350
1351
		if (!$this->db->num_rows($query)) {
1352
			return 0;
1353
		}
1354
1355
		$result = $this->db->fetch_object($query);
1356
1357
		return $this->fetch($result->rowid);
1358
	}
1359
1360
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1361
	/**
1362
	 *	Load data for barcode into properties ->barcode_type*
1363
	 *	Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
1364
	 *  if it is not defined, ->element must be defined to know default barcode type.
1365
	 *
1366
	 *	@return		int			<0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
1367
	 */
1368
	public function fetch_barcode()
1369
	{
1370
        // phpcs:enable
1371
		global $conf;
1372
1373
		dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1374
1375
		$idtype=$this->barcode_type;
1376
		if (empty($idtype) && $idtype != '0')	// If type of barcode no set, we try to guess. If set to '0' it means we forced to have type remain not defined
1377
		{
1378
			if ($this->element == 'product')      $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1379
			elseif ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1380
			else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1381
		}
1382
1383
		if ($idtype > 0)
1384
		{
1385
			if (empty($this->barcode_type) || empty($this->barcode_type_code) || empty($this->barcode_type_label) || empty($this->barcode_type_coder))    // If data not already loaded
1386
			{
1387
				$sql = "SELECT rowid, code, libelle as label, coder";
1388
				$sql.= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1389
				$sql.= " WHERE rowid = ".$idtype;
1390
				dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1391
				$resql = $this->db->query($sql);
1392
				if ($resql)
1393
				{
1394
					$obj = $this->db->fetch_object($resql);
1395
					$this->barcode_type       = $obj->rowid;
1396
					$this->barcode_type_code  = $obj->code;
1397
					$this->barcode_type_label = $obj->label;
1398
					$this->barcode_type_coder = $obj->coder;
1399
					return 1;
1400
				}
1401
				else
1402
				{
1403
					dol_print_error($this->db);
1404
					return -1;
1405
				}
1406
			}
1407
		}
1408
		return 0;
1409
	}
1410
1411
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1412
	/**
1413
	 *		Load the project with id $this->fk_project into this->project
1414
	 *
1415
	 *		@return		int			<0 if KO, >=0 if OK
1416
	 */
1417
	public function fetch_projet()
1418
	{
1419
        // phpcs:enable
1420
		include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1421
1422
		if (empty($this->fk_project) && ! empty($this->fk_projet)) $this->fk_project = $this->fk_projet;	// For backward compatibility
1423
		if (empty($this->fk_project)) return 0;
1424
1425
		$project = new Project($this->db);
1426
		$result = $project->fetch($this->fk_project);
1427
1428
		$this->projet = $project;	// deprecated
1429
		$this->project = $project;
1430
		return $result;
1431
	}
1432
1433
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1434
	/**
1435
	 *		Load the product with id $this->fk_product into this->product
1436
	 *
1437
	 *		@return		int			<0 if KO, >=0 if OK
1438
	 */
1439
	public function fetch_product()
1440
	{
1441
        // phpcs:enable
1442
		include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1443
1444
		if (empty($this->fk_product)) return 0;
1445
1446
		$product = new Product($this->db);
1447
		$result = $product->fetch($this->fk_product);
1448
1449
		$this->product = $product;
1450
		return $result;
1451
	}
1452
1453
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1454
	/**
1455
	 *		Load the user with id $userid into this->user
1456
	 *
1457
	 *		@param	int		$userid 		Id du contact
1458
	 *		@return	int						<0 if KO, >0 if OK
1459
	 */
1460
	public function fetch_user($userid)
1461
	{
1462
        // phpcs:enable
1463
		$user = new User($this->db);
1464
		$result=$user->fetch($userid);
1465
		$this->user = $user;
1466
		return $result;
1467
	}
1468
1469
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1470
	/**
1471
	 *	Read linked origin object
1472
	 *
1473
	 *	@return		void
1474
	 */
1475
	public function fetch_origin()
1476
	{
1477
        // phpcs:enable
1478
		if ($this->origin == 'shipping') $this->origin = 'expedition';
1479
		if ($this->origin == 'delivery') $this->origin = 'livraison';
1480
        if ($this->origin == 'order_supplier') $this->origin = 'commandeFournisseur';
1481
1482
		$origin = $this->origin;
1483
1484
		$classname = ucfirst($origin);
1485
		$this->$origin = new $classname($this->db);
1486
		$this->$origin->fetch($this->origin_id);
1487
	}
1488
1489
	/**
1490
     *  Load object from specific field
1491
     *
1492
     *  @param	string	$table		Table element or element line
1493
     *  @param	string	$field		Field selected
1494
     *  @param	string	$key		Import key
1495
     *  @param	string	$element	Element name
1496
     *	@return	int					<0 if KO, >0 if OK
1497
     */
1498
	public function fetchObjectFrom($table, $field, $key, $element = null)
1499
	{
1500
		global $conf;
1501
1502
		$result=false;
1503
1504
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$table;
1505
		$sql.= " WHERE ".$field." = '".$key."'";
1506
		if (! empty($element)) {
1507
			$sql.= " AND entity IN (".getEntity($element).")";
1508
		} else {
1509
			$sql.= " AND entity = ".$conf->entity;
1510
		}
1511
1512
		dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1513
		$resql = $this->db->query($sql);
1514
		if ($resql)
1515
		{
1516
			$row = $this->db->fetch_row($resql);
1517
			// Test for avoid error -1
1518
			if ($row[0] > 0) {
1519
				$result = $this->fetch($row[0]);
1520
			}
1521
		}
1522
1523
		return $result;
1524
	}
1525
1526
	/**
1527
	 *	Getter generic. Load value from a specific field
1528
	 *
1529
	 *	@param	string	$table		Table of element or element line
1530
	 *	@param	int		$id			Element id
1531
	 *	@param	string	$field		Field selected
1532
	 *	@return	int					<0 if KO, >0 if OK
1533
	 */
1534
	public function getValueFrom($table, $id, $field)
1535
	{
1536
		$result=false;
1537
		if (!empty($id) && !empty($field) && !empty($table)) {
1538
			$sql = "SELECT ".$field." FROM ".MAIN_DB_PREFIX.$table;
1539
			$sql.= " WHERE rowid = ".$id;
1540
1541
			dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1542
			$resql = $this->db->query($sql);
1543
			if ($resql)
1544
			{
1545
				$row = $this->db->fetch_row($resql);
1546
				$result = $row[0];
1547
			}
1548
		}
1549
		return $result;
1550
	}
1551
1552
	/**
1553
	 *	Setter generic. Update a specific field into database.
1554
	 *  Warning: Trigger is run only if param trigkey is provided.
1555
	 *
1556
	 *	@param	string		$field			Field to update
1557
	 *	@param	mixed		$value			New value
1558
	 *	@param	string		$table			To force other table element or element line (should not be used)
1559
	 *	@param	int			$id				To force other object id (should not be used)
1560
	 *	@param	string		$format			Data format ('text', 'date'). 'text' is used if not defined
1561
	 *	@param	string		$id_field		To force rowid field name. 'rowid' is used if not defined
1562
	 *	@param	User|string	$fuser			Update the user of last update field with this user. If not provided, current user is used except if value is 'none'
1563
	 *  @param  string      $trigkey    	Trigger key to run (in most cases something like 'XXX_MODIFY')
1564
	 *  @param	string		$fk_user_field	Name of field to save user id making change
1565
	 *	@return	int							<0 if KO, >0 if OK
1566
	 *  @see updateExtraField()
1567
	 */
1568
	public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
1569
	{
1570
		global $user,$langs,$conf;
1571
1572
		if (empty($table)) 	  $table=$this->table_element;
1573
		if (empty($id))    	  $id=$this->id;
1574
		if (empty($format))   $format='text';
1575
		if (empty($id_field)) $id_field='rowid';
1576
1577
		$error=0;
1578
1579
		$this->db->begin();
1580
1581
		// Special case
1582
		if ($table == 'product' && $field == 'note_private') $field='note';
1583
		if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) $fk_user_field = 'fk_user_mod';
1584
1585
		$sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET ";
1586
1587
		if ($format == 'text') $sql.= $field." = '".$this->db->escape($value)."'";
1588
		elseif ($format == 'int') $sql.= $field." = ".$this->db->escape($value);
1589
		elseif ($format == 'date') $sql.= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
1590
1591
		if ($fk_user_field)
1592
		{
1593
			if (! empty($fuser) && is_object($fuser)) $sql.=", ".$fk_user_field." = ".$fuser->id;
1594
			elseif (empty($fuser) || $fuser != 'none') $sql.=", ".$fk_user_field." = ".$user->id;
1595
		}
1596
1597
		$sql.= " WHERE ".$id_field." = ".$id;
1598
1599
		dol_syslog(get_class($this)."::".__FUNCTION__."", LOG_DEBUG);
1600
		$resql = $this->db->query($sql);
1601
		if ($resql)
1602
		{
1603
			if ($trigkey)
1604
			{
1605
				// call trigger with updated object values
1606
				if (empty($this->fields) && method_exists($this, 'fetch'))
1607
				{
1608
					$result = $this->fetch($id);
1609
				}
1610
				else
1611
				{
1612
					$result = $this->fetchCommon($id);
1613
				}
1614
				if ($result >= 0) $result=$this->call_trigger($trigkey, (! empty($fuser) && is_object($fuser)) ? $fuser : $user);   // This may set this->errors
1615
				if ($result < 0) $error++;
1616
			}
1617
1618
			if (! $error)
1619
			{
1620
				if (property_exists($this, $field)) $this->$field = $value;
1621
				$this->db->commit();
1622
				return 1;
1623
			}
1624
			else
1625
			{
1626
				$this->db->rollback();
1627
				return -2;
1628
			}
1629
		}
1630
		else
1631
		{
1632
			$this->error=$this->db->lasterror();
1633
			$this->db->rollback();
1634
			return -1;
1635
		}
1636
	}
1637
1638
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1639
	/**
1640
	 *      Load properties id_previous and id_next by comparing $fieldid with $this->ref
1641
	 *
1642
	 *      @param	string	$filter		Optional filter. Example: " AND (t.field1 = 'aa' OR t.field2 = 'bb')"
1643
	 *	 	@param  string	$fieldid   	Name of field to use for the select MAX and MIN
1644
	 *		@param	int		$nodbprefix	Do not include DB prefix to forge table name
1645
	 *      @return int         		<0 if KO, >0 if OK
1646
	 */
1647
	public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
1648
	{
1649
        // phpcs:enable
1650
		global $conf, $user;
1651
1652
		if (! $this->table_element)
1653
		{
1654
			dol_print_error('', get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
1655
			return -1;
1656
		}
1657
		if ($fieldid == 'none') return 1;
1658
1659
		// Security on socid
1660
		$socid = 0;
1661
		if ($user->societe_id > 0) $socid = $user->societe_id;
1662
1663
		// this->ismultientitymanaged contains
1664
		// 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
1665
		$alias = 's';
1666
		if ($this->element == 'societe') $alias = 'te';
1667
1668
		$sql = "SELECT MAX(te.".$fieldid.")";
1669
		$sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1670
		if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1671
			$sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1672
		}
1673
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to entity
1674
		elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to socid
1675
		elseif ($this->restrictiononfksoc == 2 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON te.fk_soc = s.rowid";	// If we need to link to societe to limit select to socid
1676
		if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid)  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1677
		$sql.= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1678
		if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1679
		if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1680
		if (! empty($filter))
1681
		{
1682
			if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1683
			$sql.=$filter;
1684
		}
1685
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to entity
1686
		elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to socid
1687
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1688
			if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1689
				if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1690
					$sql.= " AND te.entity IS NOT NULL"; // Show all users
1691
				} else {
1692
					$sql.= " AND ug.fk_user = te.rowid";
1693
					$sql.= " AND ug.entity IN (".getEntity($this->element).")";
1694
				}
1695
			} else {
1696
				$sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1697
			}
1698
		}
1699
		if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1700
		if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1701
		if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1702
		//print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1703
1704
		$result = $this->db->query($sql);
1705
		if (! $result)
1706
		{
1707
			$this->error=$this->db->lasterror();
1708
			return -1;
1709
		}
1710
		$row = $this->db->fetch_row($result);
1711
		$this->ref_previous = $row[0];
1712
1713
1714
		$sql = "SELECT MIN(te.".$fieldid.")";
1715
		$sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1716
		if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1717
			$sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1718
		}
1719
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to entity
1720
		elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to socid
1721
		elseif ($this->restrictiononfksoc == 2 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON te.fk_soc = s.rowid";	// If we need to link to societe to limit select to socid
1722
		if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1723
		$sql.= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1724
		if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1725
		if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1726
		if (! empty($filter))
1727
		{
1728
			if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1729
			$sql.=$filter;
1730
		}
1731
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to entity
1732
		elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to socid
1733
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1734
			if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1735
				if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1736
					$sql.= " AND te.entity IS NOT NULL"; // Show all users
1737
				} else {
1738
					$sql.= " AND ug.fk_user = te.rowid";
1739
					$sql.= " AND ug.entity IN (".getEntity($this->element).")";
1740
				}
1741
			} else {
1742
				$sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1743
			}
1744
		}
1745
		if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1746
		if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1747
		if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1748
		//print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1749
		// Rem: Bug in some mysql version: SELECT MIN(rowid) FROM llx_socpeople WHERE rowid > 1 when one row in database with rowid=1, returns 1 instead of null
1750
1751
		$result = $this->db->query($sql);
1752
		if (! $result)
1753
		{
1754
			$this->error=$this->db->lasterror();
1755
			return -2;
1756
		}
1757
		$row = $this->db->fetch_row($result);
1758
		$this->ref_next = $row[0];
1759
1760
		return 1;
1761
	}
1762
1763
1764
	/**
1765
	 *      Return list of id of contacts of object
1766
	 *
1767
	 *      @param	string	$source     Source of contact: external (llx_socpeople) or internal (llx_user) or thirdparty (llx_societe)
1768
	 *      @return array				Array of id of contacts (if source=external or internal)
1769
	 * 									Array of id of third parties with at least one contact on object (if source=thirdparty)
1770
	 */
1771
	public function getListContactId($source = 'external')
1772
	{
1773
		$contactAlreadySelected = array();
1774
		$tab = $this->liste_contact(-1, $source);
1775
		$num=count($tab);
1776
		$i = 0;
1777
		while ($i < $num)
1778
		{
1779
			if ($source == 'thirdparty') $contactAlreadySelected[$i] = $tab[$i]['socid'];
1780
			else  $contactAlreadySelected[$i] = $tab[$i]['id'];
1781
			$i++;
1782
		}
1783
		return $contactAlreadySelected;
1784
	}
1785
1786
1787
	/**
1788
	 *	Link element with a project
1789
	 *
1790
	 *	@param     	int		$projectid		Project id to link element to
1791
	 *	@return		int						<0 if KO, >0 if OK
1792
	 */
1793
	public function setProject($projectid)
1794
	{
1795
		if (! $this->table_element)
1796
		{
1797
			dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR);
1798
			return -1;
1799
		}
1800
1801
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1802
		if ($this->table_element == 'actioncomm')
1803
		{
1804
			if ($projectid) $sql.= ' SET fk_project = '.$projectid;
1805
			else $sql.= ' SET fk_project = NULL';
1806
			$sql.= ' WHERE id = '.$this->id;
1807
		}
1808
		else
1809
		{
1810
			if ($projectid) $sql.= ' SET fk_projet = '.$projectid;
1811
			else $sql.= ' SET fk_projet = NULL';
1812
			$sql.= ' WHERE rowid = '.$this->id;
1813
		}
1814
1815
		dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
1816
		if ($this->db->query($sql))
1817
		{
1818
			$this->fk_project = $projectid;
1819
			return 1;
1820
		}
1821
		else
1822
		{
1823
			dol_print_error($this->db);
1824
			return -1;
1825
		}
1826
	}
1827
1828
	/**
1829
	 *  Change the payments methods
1830
	 *
1831
	 *  @param		int		$id		Id of new payment method
1832
	 *  @return		int				>0 if OK, <0 if KO
1833
	 */
1834
	public function setPaymentMethods($id)
1835
	{
1836
		dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
1837
		if ($this->statut >= 0 || $this->element == 'societe')
1838
		{
1839
			// TODO uniformize field name
1840
			$fieldname = 'fk_mode_reglement';
1841
			if ($this->element == 'societe') $fieldname = 'mode_reglement';
1842
			if (get_class($this) == 'Fournisseur') $fieldname = 'mode_reglement_supplier';
1843
1844
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1845
			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
1846
			$sql .= ' WHERE rowid='.$this->id;
1847
1848
			if ($this->db->query($sql))
1849
			{
1850
				$this->mode_reglement_id = $id;
1851
				// for supplier
1852
				if (get_class($this) == 'Fournisseur') $this->mode_reglement_supplier_id = $id;
1853
				return 1;
1854
			}
1855
			else
1856
			{
1857
				dol_syslog(get_class($this).'::setPaymentMethods Erreur '.$sql.' - '.$this->db->error());
1858
				$this->error=$this->db->error();
1859
				return -1;
1860
			}
1861
		}
1862
		else
1863
		{
1864
			dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
1865
			$this->error='Status of the object is incompatible '.$this->statut;
1866
			return -2;
1867
		}
1868
	}
1869
1870
	/**
1871
	 *  Change the multicurrency code
1872
	 *
1873
	 *  @param		string	$code	multicurrency code
1874
	 *  @return		int				>0 if OK, <0 if KO
1875
	 */
1876
	public function setMulticurrencyCode($code)
1877
	{
1878
		dol_syslog(get_class($this).'::setMulticurrencyCode('.$id.')');
0 ignored issues
show
Bug introduced by
The variable $id does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1879
		if ($this->statut >= 0 || $this->element == 'societe')
1880
		{
1881
			$fieldname = 'multicurrency_code';
1882
1883
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1884
			$sql .= ' SET '.$fieldname." = '".$this->db->escape($code)."'";
1885
			$sql .= ' WHERE rowid='.$this->id;
1886
1887
			if ($this->db->query($sql))
1888
			{
1889
				$this->multicurrency_code = $code;
1890
1891
				list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
1892
				if ($rate) $this->setMulticurrencyRate($rate, 2);
1893
1894
				return 1;
1895
			}
1896
			else
1897
			{
1898
				dol_syslog(get_class($this).'::setMulticurrencyCode Erreur '.$sql.' - '.$this->db->error());
1899
				$this->error=$this->db->error();
1900
				return -1;
1901
			}
1902
		}
1903
		else
1904
		{
1905
			dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
1906
			$this->error='Status of the object is incompatible '.$this->statut;
1907
			return -2;
1908
		}
1909
	}
1910
1911
	/**
1912
	 *  Change the multicurrency rate
1913
	 *
1914
	 *  @param		double	$rate	multicurrency rate
1915
	 *  @param		int		$mode	mode 1 : amounts in company currency will be recalculated, mode 2 : amounts in foreign currency
1916
	 *  @return		int				>0 if OK, <0 if KO
1917
	 */
1918
	public function setMulticurrencyRate($rate, $mode = 1)
1919
	{
1920
		dol_syslog(get_class($this).'::setMulticurrencyRate('.$id.')');
0 ignored issues
show
Bug introduced by
The variable $id does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1921
		if ($this->statut >= 0 || $this->element == 'societe')
1922
		{
1923
			$fieldname = 'multicurrency_tx';
1924
1925
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1926
			$sql .= ' SET '.$fieldname.' = '.$rate;
1927
			$sql .= ' WHERE rowid='.$this->id;
1928
1929
			if ($this->db->query($sql))
1930
			{
1931
				$this->multicurrency_tx = $rate;
1932
1933
				// Update line price
1934
				if (!empty($this->lines))
1935
				{
1936
					foreach ($this->lines as &$line)
1937
					{
1938
						if($mode == 1) {
1939
							$line->subprice = 0;
1940
						}
1941
1942
						switch ($this->element) {
1943
							case 'propal':
1944
								$this->updateline(
1945
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1946
									($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1947
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start,
1948
									$line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1949
								);
1950
								break;
1951
							case 'commande':
1952
								$this->updateline(
1953
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1954
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end,
1955
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1956
									$line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1957
								);
1958
								break;
1959
							case 'facture':
1960
								$this->updateline(
1961
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1962
									$line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,
1963
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1964
									$line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice
1965
								);
1966
								break;
1967
							case 'supplier_proposal':
1968
								$this->updateline(
1969
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1970
									($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1971
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options,
1972
									$line->ref_fourn, $line->multicurrency_subprice
1973
								);
1974
								break;
1975
							case 'order_supplier':
1976
								$this->updateline(
1977
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1978
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false,
1979
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1980
								);
1981
								break;
1982
							case 'invoice_supplier':
1983
								$this->updateline(
1984
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx,
1985
									$line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false,
1986
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1987
								);
1988
								break;
1989
							default:
1990
								dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
1991
								break;
1992
						}
1993
					}
1994
				}
1995
1996
				return 1;
1997
			}
1998
			else
1999
			{
2000
				dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
2001
				$this->error=$this->db->error();
2002
				return -1;
2003
			}
2004
		}
2005
		else
2006
		{
2007
			dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2008
			$this->error='Status of the object is incompatible '.$this->statut;
2009
			return -2;
2010
		}
2011
	}
2012
2013
	/**
2014
	 *  Change the payments terms
2015
	 *
2016
	 *  @param		int		$id		Id of new payment terms
2017
	 *  @return		int				>0 if OK, <0 if KO
2018
	 */
2019
	public function setPaymentTerms($id)
2020
	{
2021
		dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
2022
		if ($this->statut >= 0 || $this->element == 'societe')
2023
		{
2024
			// TODO uniformize field name
2025
			$fieldname = 'fk_cond_reglement';
2026
			if ($this->element == 'societe') $fieldname = 'cond_reglement';
2027
			if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
2028
2029
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2030
			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
2031
			$sql .= ' WHERE rowid='.$this->id;
2032
2033
			if ($this->db->query($sql))
2034
			{
2035
				$this->cond_reglement_id = $id;
2036
				// for supplier
2037
				if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
2038
				$this->cond_reglement = $id;	// for compatibility
2039
				return 1;
2040
			}
2041
			else
2042
			{
2043
				dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
2044
				$this->error=$this->db->error();
2045
				return -1;
2046
			}
2047
		}
2048
		else
2049
		{
2050
			dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2051
			$this->error='Status of the object is incompatible '.$this->statut;
2052
			return -2;
2053
		}
2054
	}
2055
2056
	/**
2057
	 *	Define delivery address
2058
	 *  @deprecated
2059
	 *
2060
	 *	@param      int		$id		Address id
2061
	 *	@return     int				<0 si ko, >0 si ok
2062
	 */
2063
	public function setDeliveryAddress($id)
2064
	{
2065
		$fieldname = 'fk_delivery_address';
2066
		if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
2067
2068
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
2069
		$sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
2070
2071
		if ($this->db->query($sql))
2072
		{
2073
			$this->fk_delivery_address = $id;
2074
			return 1;
2075
		}
2076
		else
2077
		{
2078
			$this->error=$this->db->error();
2079
			dol_syslog(get_class($this).'::setDeliveryAddress Erreur '.$sql.' - '.$this->error);
2080
			return -1;
2081
		}
2082
	}
2083
2084
2085
	/**
2086
	 *  Change the shipping method
2087
	 *
2088
	 *  @param      int     $shipping_method_id     Id of shipping method
2089
	 *  @param      bool    $notrigger              false=launch triggers after, true=disable triggers
2090
	 *  @param      User	$userused               Object user
2091
	 *
2092
	 *  @return     int              1 if OK, 0 if KO
2093
	 */
2094
	public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null)
2095
	{
2096
		global $user;
2097
2098
		if (empty($userused)) $userused=$user;
2099
2100
		$error = 0;
2101
2102
		if (! $this->table_element) {
2103
			dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR);
2104
			return -1;
2105
		}
2106
2107
		$this->db->begin();
2108
2109
		if ($shipping_method_id<0) $shipping_method_id='NULL';
2110
		dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2111
2112
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2113
		$sql.= " SET fk_shipping_method = ".$shipping_method_id;
2114
		$sql.= " WHERE rowid=".$this->id;
2115
		$resql = $this->db->query($sql);
2116
		if (! $resql) {
2117
			dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2118
			$this->error = $this->db->lasterror();
2119
			$error++;
2120
		} else {
2121
			if (!$notrigger)
2122
			{
2123
				// Call trigger
2124
				$this->context=array('shippingmethodupdate'=>1);
2125
				$result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2126
				if ($result < 0) $error++;
2127
				// End call trigger
2128
			}
2129
		}
2130
		if ($error)
2131
		{
2132
			$this->db->rollback();
2133
			return -1;
2134
		} else {
2135
			$this->shipping_method_id = ($shipping_method_id=='NULL')?null:$shipping_method_id;
0 ignored issues
show
Documentation Bug introduced by
It seems like $shipping_method_id == '...l : $shipping_method_id can also be of type string. However, the property $shipping_method_id is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2136
			$this->db->commit();
2137
			return 1;
2138
		}
2139
	}
2140
2141
2142
	/**
2143
	 *  Change the warehouse
2144
	 *
2145
	 *  @param      int     $warehouse_id     Id of warehouse
2146
	 *  @return     int              1 if OK, 0 if KO
2147
	 */
2148
	public function setWarehouse($warehouse_id)
2149
	{
2150
		if (! $this->table_element) {
2151
			dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR);
2152
			return -1;
2153
		}
2154
		if ($warehouse_id<0) $warehouse_id='NULL';
2155
		dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2156
2157
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2158
		$sql.= " SET fk_warehouse = ".$warehouse_id;
2159
		$sql.= " WHERE rowid=".$this->id;
2160
2161
		if ($this->db->query($sql)) {
2162
			$this->warehouse_id = ($warehouse_id=='NULL')?null:$warehouse_id;
2163
			return 1;
2164
		} else {
2165
			dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2166
			$this->error=$this->db->error();
2167
			return 0;
2168
		}
2169
	}
2170
2171
2172
	/**
2173
	 *		Set last model used by doc generator
2174
	 *
2175
	 *		@param		User	$user		User object that make change
2176
	 *		@param		string	$modelpdf	Modele name
2177
	 *		@return		int					<0 if KO, >0 if OK
2178
	 */
2179
	public function setDocModel($user, $modelpdf)
2180
	{
2181
		if (! $this->table_element)
2182
		{
2183
			dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR);
2184
			return -1;
2185
		}
2186
2187
		$newmodelpdf=dol_trunc($modelpdf, 255);
2188
2189
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2190
		$sql.= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2191
		$sql.= " WHERE rowid = ".$this->id;
2192
		// if ($this->element == 'facture') $sql.= " AND fk_statut < 2";
2193
		// if ($this->element == 'propal')  $sql.= " AND fk_statut = 0";
2194
2195
		dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2196
		$resql=$this->db->query($sql);
2197
		if ($resql)
2198
		{
2199
			$this->modelpdf=$modelpdf;
2200
			return 1;
2201
		}
2202
		else
2203
		{
2204
			dol_print_error($this->db);
2205
			return 0;
2206
		}
2207
	}
2208
2209
2210
	/**
2211
	 *  Change the bank account
2212
	 *
2213
	 *  @param		int		$fk_account		Id of bank account
2214
	 *  @param      bool    $notrigger      false=launch triggers after, true=disable triggers
2215
	 *  @param      User	$userused		Object user
2216
	 *  @return		int				1 if OK, 0 if KO
2217
	 */
2218
	public function setBankAccount($fk_account, $notrigger = false, $userused = null)
2219
	{
2220
		global $user;
2221
2222
		if (empty($userused)) $userused=$user;
2223
2224
		$error = 0;
2225
2226
		if (! $this->table_element) {
2227
			dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR);
2228
			return -1;
2229
		}
2230
		$this->db->begin();
2231
2232
		if ($fk_account<0) $fk_account='NULL';
2233
		dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2234
2235
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2236
		$sql.= " SET fk_account = ".$fk_account;
2237
		$sql.= " WHERE rowid=".$this->id;
2238
2239
		$resql = $this->db->query($sql);
2240
		if (! $resql)
2241
		{
2242
			dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2243
			$this->error = $this->db->lasterror();
2244
			$error++;
2245
		}
2246
		else
2247
		{
2248
			if (!$notrigger)
2249
			{
2250
				// Call trigger
2251
				$this->context=array('bankaccountupdate'=>1);
2252
				$result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2253
				if ($result < 0) $error++;
2254
				// End call trigger
2255
			}
2256
		}
2257
		if ($error)
2258
		{
2259
			$this->db->rollback();
2260
			return -1;
2261
		}
2262
		else
2263
		{
2264
			$this->fk_account = ($fk_account=='NULL')?null:$fk_account;
0 ignored issues
show
Documentation Bug introduced by
It seems like $fk_account == 'NULL' ? null : $fk_account can also be of type string. However, the property $fk_account is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2265
			$this->db->commit();
2266
			return 1;
2267
		}
2268
	}
2269
2270
2271
	// TODO: Move line related operations to CommonObjectLine?
2272
2273
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2274
	/**
2275
	 *  Save a new position (field rang) for details lines.
2276
	 *  You can choose to set position for lines with already a position or lines without any position defined.
2277
	 *
2278
	 * 	@param		boolean		$renum			   True to renum all already ordered lines, false to renum only not already ordered lines.
2279
	 * 	@param		string		$rowidorder		   ASC or DESC
2280
	 * 	@param		boolean		$fk_parent_line    Table with fk_parent_line field or not
2281
	 * 	@return		int                            <0 if KO, >0 if OK
2282
	 */
2283
	public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
2284
	{
2285
        // phpcs:enable
2286
		if (! $this->table_element_line)
2287
		{
2288
			dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR);
2289
			return -1;
2290
		}
2291
		if (! $this->fk_element)
2292
		{
2293
			dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR);
2294
			return -1;
2295
		}
2296
2297
		// Count number of lines to reorder (according to choice $renum)
2298
		$nl=0;
2299
		$sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2300
		$sql.= ' WHERE '.$this->fk_element.'='.$this->id;
2301
		if (! $renum) $sql.= ' AND rang = 0';
2302
		if ($renum) $sql.= ' AND rang <> 0';
2303
2304
		dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
2305
		$resql = $this->db->query($sql);
2306
		if ($resql)
2307
		{
2308
			$row = $this->db->fetch_row($resql);
2309
			$nl = $row[0];
2310
		}
2311
		else dol_print_error($this->db);
2312
		if ($nl > 0)
2313
		{
2314
			// The goal of this part is to reorder all lines, with all children lines sharing the same
2315
			// counter that parents.
2316
			$rows=array();
2317
2318
			// We first search all lines that are parent lines (for multilevel details lines)
2319
			$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2320
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2321
			if ($fk_parent_line) $sql.= ' AND fk_parent_line IS NULL';
2322
			$sql.= ' ORDER BY rang ASC, rowid '.$rowidorder;
2323
2324
			dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
2325
			$resql = $this->db->query($sql);
2326
			if ($resql)
2327
			{
2328
				$i=0;
2329
				$num = $this->db->num_rows($resql);
2330
				while ($i < $num)
2331
				{
2332
					$row = $this->db->fetch_row($resql);
2333
					$rows[] = $row[0];	// Add parent line into array rows
2334
					$childrens = $this->getChildrenOfLine($row[0]);
2335
					if (! empty($childrens))
2336
					{
2337
						foreach($childrens as $child)
2338
						{
2339
							array_push($rows, $child);
2340
						}
2341
					}
2342
					$i++;
2343
				}
2344
2345
				// Now we set a new number for each lines (parent and children with children included into parent tree)
2346
				if (! empty($rows))
2347
				{
2348
					foreach($rows as $key => $row)
2349
					{
2350
						$this->updateRangOfLine($row, ($key+1));
2351
					}
2352
				}
2353
			}
2354
			else
2355
			{
2356
				dol_print_error($this->db);
2357
			}
2358
		}
2359
		return 1;
2360
	}
2361
2362
	/**
2363
	 * 	Get children of line
2364
	 *
2365
	 * 	@param	int		$id		Id of parent line
2366
	 * 	@return	array			Array with list of children lines id
2367
	 */
2368
	public function getChildrenOfLine($id)
2369
	{
2370
		$rows=array();
2371
2372
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2373
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2374
		$sql.= ' AND fk_parent_line = '.$id;
2375
		$sql.= ' ORDER BY rang ASC';
2376
2377
		dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG);
2378
		$resql = $this->db->query($sql);
2379
		if ($resql)
2380
		{
2381
			$i=0;
2382
			$num = $this->db->num_rows($resql);
2383
			while ($i < $num)
2384
			{
2385
				$row = $this->db->fetch_row($resql);
2386
				$rows[$i] = $row[0];
2387
				$i++;
2388
			}
2389
		}
2390
2391
		return $rows;
2392
	}
2393
2394
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2395
	/**
2396
	 * 	Update a line to have a lower rank
2397
	 *
2398
	 * 	@param 	int			$rowid				Id of line
2399
	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2400
	 * 	@return	void
2401
	 */
2402
	public function line_up($rowid, $fk_parent_line = true)
2403
	{
2404
        // phpcs:enable
2405
		$this->line_order(false, 'ASC', $fk_parent_line);
2406
2407
		// Get rang of line
2408
		$rang = $this->getRangOfLine($rowid);
2409
2410
		// Update position of line
2411
		$this->updateLineUp($rowid, $rang);
2412
	}
2413
2414
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2415
	/**
2416
	 * 	Update a line to have a higher rank
2417
	 *
2418
	 * 	@param	int			$rowid				Id of line
2419
	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2420
	 * 	@return	void
2421
	 */
2422
	public function line_down($rowid, $fk_parent_line = true)
2423
	{
2424
        // phpcs:enable
2425
		$this->line_order(false, 'ASC', $fk_parent_line);
2426
2427
		// Get rang of line
2428
		$rang = $this->getRangOfLine($rowid);
2429
2430
		// Get max value for rang
2431
		$max = $this->line_max();
2432
2433
		// Update position of line
2434
		$this->updateLineDown($rowid, $rang, $max);
2435
	}
2436
2437
	/**
2438
	 * 	Update position of line (rang)
2439
	 *
2440
	 * 	@param	int		$rowid		Id of line
2441
	 * 	@param	int		$rang		Position
2442
	 * 	@return	void
2443
	 */
2444
	public function updateRangOfLine($rowid, $rang)
2445
	{
2446
		$fieldposition = 'rang';
2447
		if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2448
2449
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2450
		$sql.= ' WHERE rowid = '.$rowid;
2451
2452
		dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
2453
		if (! $this->db->query($sql))
2454
		{
2455
			dol_print_error($this->db);
2456
		}
2457
	}
2458
2459
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2460
	/**
2461
	 * 	Update position of line with ajax (rang)
2462
	 *
2463
	 * 	@param	array	$rows	Array of rows
2464
	 * 	@return	void
2465
	 */
2466
	public function line_ajaxorder($rows)
2467
	{
2468
        // phpcs:enable
2469
		$num = count($rows);
2470
		for ($i = 0 ; $i < $num ; $i++)
2471
		{
2472
			$this->updateRangOfLine($rows[$i], ($i+1));
2473
		}
2474
	}
2475
2476
	/**
2477
	 * 	Update position of line up (rang)
2478
	 *
2479
	 * 	@param	int		$rowid		Id of line
2480
	 * 	@param	int		$rang		Position
2481
	 * 	@return	void
2482
	 */
2483
	public function updateLineUp($rowid, $rang)
2484
	{
2485
		if ($rang > 1)
2486
		{
2487
			$fieldposition = 'rang';
2488
			if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2489
2490
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang ;
2491
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2492
			$sql.= ' AND rang = '.($rang - 1);
2493
			if ($this->db->query($sql) )
2494
			{
2495
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang - 1);
2496
				$sql.= ' WHERE rowid = '.$rowid;
2497
				if (! $this->db->query($sql) )
2498
				{
2499
					dol_print_error($this->db);
2500
				}
2501
			}
2502
			else
2503
			{
2504
				dol_print_error($this->db);
2505
			}
2506
		}
2507
	}
2508
2509
	/**
2510
	 * 	Update position of line down (rang)
2511
	 *
2512
	 * 	@param	int		$rowid		Id of line
2513
	 * 	@param	int		$rang		Position
2514
	 * 	@param	int		$max		Max
2515
	 * 	@return	void
2516
	 */
2517
	public function updateLineDown($rowid, $rang, $max)
2518
	{
2519
		if ($rang < $max)
2520
		{
2521
			$fieldposition = 'rang';
2522
			if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2523
2524
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2525
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2526
			$sql.= ' AND rang = '.($rang+1);
2527
			if ($this->db->query($sql) )
2528
			{
2529
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang+1);
2530
				$sql.= ' WHERE rowid = '.$rowid;
2531
				if (! $this->db->query($sql) )
2532
				{
2533
					dol_print_error($this->db);
2534
				}
2535
			}
2536
			else
2537
			{
2538
				dol_print_error($this->db);
2539
			}
2540
		}
2541
	}
2542
2543
	/**
2544
	 * 	Get position of line (rang)
2545
	 *
2546
	 * 	@param		int		$rowid		Id of line
2547
	 *  @return		int     			Value of rang in table of lines
2548
	 */
2549
	public function getRangOfLine($rowid)
2550
	{
2551
		$sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2552
		$sql.= ' WHERE rowid ='.$rowid;
2553
2554
		dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
2555
		$resql = $this->db->query($sql);
2556
		if ($resql)
2557
		{
2558
			$row = $this->db->fetch_row($resql);
2559
			return $row[0];
2560
		}
2561
	}
2562
2563
	/**
2564
	 * 	Get rowid of the line relative to its position
2565
	 *
2566
	 * 	@param		int		$rang		Rang value
2567
	 *  @return     int     			Rowid of the line
2568
	 */
2569
	public function getIdOfLine($rang)
2570
	{
2571
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2572
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2573
		$sql.= ' AND rang = '.$rang;
2574
		$resql = $this->db->query($sql);
2575
		if ($resql)
2576
		{
2577
			$row = $this->db->fetch_row($resql);
2578
			return $row[0];
2579
		}
2580
	}
2581
2582
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2583
	/**
2584
	 * 	Get max value used for position of line (rang)
2585
	 *
2586
	 * 	@param		int		$fk_parent_line		Parent line id
2587
	 *  @return     int  			   			Max value of rang in table of lines
2588
	 */
2589
	public function line_max($fk_parent_line = 0)
2590
	{
2591
        // phpcs:enable
2592
		// Search the last rang with fk_parent_line
2593
		if ($fk_parent_line)
2594
		{
2595
			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2596
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2597
			$sql.= ' AND fk_parent_line = '.$fk_parent_line;
2598
2599
			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2600
			$resql = $this->db->query($sql);
2601
			if ($resql)
2602
			{
2603
				$row = $this->db->fetch_row($resql);
2604
				if (! empty($row[0]))
2605
				{
2606
					return $row[0];
2607
				}
2608
				else
2609
				{
2610
					return $this->getRangOfLine($fk_parent_line);
2611
				}
2612
			}
2613
		}
2614
		// If not, search the last rang of element
2615
		else
2616
		{
2617
			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2618
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2619
2620
			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2621
			$resql = $this->db->query($sql);
2622
			if ($resql)
2623
			{
2624
				$row = $this->db->fetch_row($resql);
2625
				return $row[0];
2626
			}
2627
		}
2628
	}
2629
2630
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2631
	/**
2632
	 *  Update external ref of element
2633
	 *
2634
	 *  @param      string		$ref_ext	Update field ref_ext
2635
	 *  @return     int      		   		<0 if KO, >0 if OK
2636
	 */
2637
	public function update_ref_ext($ref_ext)
2638
	{
2639
        // phpcs:enable
2640
		if (! $this->table_element)
2641
		{
2642
			dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
2643
			return -1;
2644
		}
2645
2646
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2647
		$sql.= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
2648
		$sql.= " WHERE ".(isset($this->table_rowid)?$this->table_rowid:'rowid')." = ". $this->id;
2649
2650
		dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
2651
		if ($this->db->query($sql))
2652
		{
2653
			$this->ref_ext = $ref_ext;
2654
			return 1;
2655
		}
2656
		else
2657
		{
2658
			$this->error=$this->db->error();
2659
			return -1;
2660
		}
2661
	}
2662
2663
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2664
	/**
2665
	 *  Update note of element
2666
	 *
2667
	 *  @param      string		$note		New value for note
2668
	 *  @param		string		$suffix		'', '_public' or '_private'
2669
	 *  @return     int      		   		<0 if KO, >0 if OK
2670
	 */
2671
	public function update_note($note, $suffix = '')
2672
	{
2673
        // phpcs:enable
2674
		global $user;
2675
2676
		if (! $this->table_element)
2677
		{
2678
			$this->error='update_note was called on objet with property table_element not defined';
2679
			dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
2680
			return -1;
2681
		}
2682
		if (! in_array($suffix, array('','_public','_private')))
2683
		{
2684
			$this->error='update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
2685
			dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
2686
			return -2;
2687
		}
2688
		// Special cas
2689
		//var_dump($this->table_element);exit;
2690
		if ($this->table_element == 'product') $suffix='';
2691
2692
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2693
		$sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL");
2694
		$sql.= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))?"fk_user_mod":"fk_user_modif")." = ".$user->id;
2695
		$sql.= " WHERE rowid =". $this->id;
2696
2697
		dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
2698
		if ($this->db->query($sql))
2699
		{
2700
			if ($suffix == '_public') $this->note_public = $note;
2701
			elseif ($suffix == '_private') $this->note_private = $note;
2702
			else
2703
			{
2704
				$this->note = $note;      // deprecated
2705
				$this->note_private = $note;
2706
			}
2707
			return 1;
2708
		}
2709
		else
2710
		{
2711
			$this->error=$this->db->lasterror();
2712
			return -1;
2713
		}
2714
	}
2715
2716
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2717
	/**
2718
	 * 	Update public note (kept for backward compatibility)
2719
	 *
2720
	 * @param      string		$note		New value for note
2721
	 * @return     int      		   		<0 if KO, >0 if OK
2722
	 * @deprecated
2723
	 * @see update_note()
2724
	 */
2725
	public function update_note_public($note)
2726
	{
2727
        // phpcs:enable
2728
		return $this->update_note($note, '_public');
2729
	}
2730
2731
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2732
	/**
2733
	 *	Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
2734
	 *  Must be called at end of methods addline or updateline.
2735
	 *
2736
	 *	@param	int		$exclspec          	>0 = Exclude special product (product_type=9)
2737
	 *  @param  string	$roundingadjust    	'none'=Do nothing, 'auto'=Use default method (MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND if defined, or '0'), '0'=Force mode total of rounding, '1'=Force mode rounding of total
2738
	 *  @param	int		$nodatabaseupdate	1=Do not update database. Update only properties of object.
2739
	 *  @param	Societe	$seller				If roundingadjust is '0' or '1' or maybe 'auto', it means we recalculate total for lines before calculating total for object and for this, we need seller object.
2740
	 *	@return	int    			           	<0 if KO, >0 if OK
2741
	 */
2742
	public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null)
2743
	{
2744
        // phpcs:enable
2745
		global $conf, $hookmanager, $action;
2746
2747
		// Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
2748
		$MODULE = "";
2749
		if ($this->element == 'propal')
2750
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
2751
		elseif ($this->element == 'order')
2752
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
2753
		elseif ($this->element == 'facture')
2754
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
2755
		elseif ($this->element == 'facture_fourn')
2756
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
2757
		elseif ($this->element == 'order_supplier')
2758
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
2759
		elseif ($this->element == 'supplier_proposal')
2760
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
2761
2762
		if (! empty($MODULE)) {
2763
			if (! empty($conf->global->$MODULE)) {
2764
				$modsactivated = explode(',', $conf->global->$MODULE);
2765
				foreach ($modsactivated as $mod) {
2766
					if ($conf->$mod->enabled)
2767
						return 1; // update was disabled by specific setup
2768
				}
2769
			}
2770
		}
2771
2772
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2773
2774
		if ($roundingadjust == '-1') $roundingadjust='auto';	// For backward compatibility
2775
2776
		$forcedroundingmode=$roundingadjust;
2777
		if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode=$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
2778
		elseif ($forcedroundingmode == 'auto') $forcedroundingmode='0';
2779
2780
		$error=0;
2781
2782
		$multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
2783
2784
		// Define constants to find lines to sum
2785
		$fieldtva='total_tva';
2786
		$fieldlocaltax1='total_localtax1';
2787
		$fieldlocaltax2='total_localtax2';
2788
		$fieldup='subprice';
2789
		if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
2790
		{
2791
			$fieldtva='tva';
2792
			$fieldup='pu_ht';
2793
		}
2794
		if ($this->element == 'expensereport')
2795
		{
2796
			$fieldup='value_unit';
2797
		}
2798
2799
		$sql = 'SELECT rowid, qty, '.$fieldup.' as up, remise_percent, total_ht, '.$fieldtva.' as total_tva, total_ttc, '.$fieldlocaltax1.' as total_localtax1, '.$fieldlocaltax2.' as total_localtax2,';
2800
		$sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
2801
			if ($this->table_element_line == 'facturedet') $sql.= ', situation_percent';
2802
			$sql.= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
2803
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2804
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2805
		if ($exclspec)
2806
		{
2807
			$product_field='product_type';
2808
			if ($this->table_element_line == 'contratdet') $product_field='';    // contratdet table has no product_type field
2809
			if ($product_field) $sql.= ' AND '.$product_field.' <> 9';
2810
		}
2811
		$sql.= ' ORDER by rowid';	// We want to be sure to always use same order of line to not change lines differently when option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND is used
2812
2813
		dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2814
		$resql = $this->db->query($sql);
2815
		if ($resql)
2816
		{
2817
			$this->total_ht  = 0;
2818
			$this->total_tva = 0;
2819
			$this->total_localtax1 = 0;
2820
			$this->total_localtax2 = 0;
2821
			$this->total_ttc = 0;
2822
			$total_ht_by_vats  = array();
2823
			$total_tva_by_vats = array();
2824
			$total_ttc_by_vats = array();
2825
			$this->multicurrency_total_ht	= 0;
2826
			$this->multicurrency_total_tva	= 0;
2827
			$this->multicurrency_total_ttc	= 0;
2828
2829
			$num = $this->db->num_rows($resql);
2830
			$i = 0;
2831
			while ($i < $num)
2832
			{
2833
				$obj = $this->db->fetch_object($resql);
2834
2835
				// Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
2836
				$parameters=array('fk_element' => $obj->rowid);
2837
				$reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2838
2839
				if (empty($reshook) && $forcedroundingmode == '0')	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode='auto'
2840
				{
2841
					$localtax_array=array($obj->localtax1_type,$obj->localtax1_tx,$obj->localtax2_type,$obj->localtax2_tx);
2842
					$tmpcal=calcul_price_total($obj->qty, $obj->up, $obj->remise_percent, $obj->vatrate, $obj->localtax1_tx, $obj->localtax2_tx, 0, 'HT', $obj->info_bits, $obj->product_type, $seller, $localtax_array, (isset($obj->situation_percent) ? $obj->situation_percent : 100), $multicurrency_tx);
0 ignored issues
show
Bug introduced by
It seems like $seller defined by parameter $seller on line 2742 can be null; however, calcul_price_total() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
2843
					$diff=price2num($tmpcal[1] - $obj->total_tva, 'MT', 1);
2844
					if ($diff)
2845
					{
2846
						$sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid;
2847
						dol_syslog('We found unconsistent data into detailed line (difference of '.$diff.') for line rowid = '.$obj->rowid." (total vat of line calculated=".$tmpcal[1].", database=".$obj->total_tva."). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
2848
								$resqlfix=$this->db->query($sqlfix);
2849
								if (! $resqlfix) dol_print_error($this->db, 'Failed to update line');
2850
								$obj->total_tva = $tmpcal[1];
2851
								$obj->total_ttc = $tmpcal[2];
2852
						//
2853
					}
2854
				}
2855
2856
				$this->total_ht        += $obj->total_ht;		// The field visible at end of line detail
2857
				$this->total_tva       += $obj->total_tva;
2858
				$this->total_localtax1 += $obj->total_localtax1;
2859
				$this->total_localtax2 += $obj->total_localtax2;
2860
				$this->total_ttc       += $obj->total_ttc;
2861
				$this->multicurrency_total_ht        += $obj->multicurrency_total_ht;		// The field visible at end of line detail
2862
				$this->multicurrency_total_tva       += $obj->multicurrency_total_tva;
2863
				$this->multicurrency_total_ttc       += $obj->multicurrency_total_ttc;
2864
2865
				if (! isset($total_ht_by_vats[$obj->vatrate]))  $total_ht_by_vats[$obj->vatrate]=0;
2866
				if (! isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate]=0;
2867
				if (! isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate]=0;
2868
				$total_ht_by_vats[$obj->vatrate]  += $obj->total_ht;
2869
				$total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
2870
				$total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
2871
2872
				if ($forcedroundingmode == '1')	// Check if we need adjustement onto line for vat. TODO This works on the company currency but not on multicurrency
2873
				{
2874
					$tmpvat=price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
2875
					$diff=price2num($total_tva_by_vats[$obj->vatrate]-$tmpvat, 'MT', 1);
2876
					//print 'Line '.$i.' rowid='.$obj->rowid.' vat_rate='.$obj->vatrate.' total_ht='.$obj->total_ht.' total_tva='.$obj->total_tva.' total_ttc='.$obj->total_ttc.' total_ht_by_vats='.$total_ht_by_vats[$obj->vatrate].' total_tva_by_vats='.$total_tva_by_vats[$obj->vatrate].' (new calculation = '.$tmpvat.') total_ttc_by_vats='.$total_ttc_by_vats[$obj->vatrate].($diff?" => DIFF":"")."<br>\n";
2877
					if ($diff)
2878
					{
2879
						if (abs($diff) > 0.1) { dol_syslog('A rounding difference was detected into TOTAL but is too high to be corrected', LOG_WARNING); exit; }
2880
						$sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".($obj->total_tva - $diff).", total_ttc = ".($obj->total_ttc - $diff)." WHERE rowid = ".$obj->rowid;
2881
						dol_syslog('We found a difference of '.$diff.' for line rowid = '.$obj->rowid.". We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
2882
								$resqlfix=$this->db->query($sqlfix);
2883
								if (! $resqlfix) dol_print_error($this->db, 'Failed to update line');
2884
								$this->total_tva -= $diff;
2885
								$this->total_ttc -= $diff;
2886
								$total_tva_by_vats[$obj->vatrate] -= $diff;
2887
								$total_ttc_by_vats[$obj->vatrate] -= $diff;
2888
					}
2889
				}
2890
2891
				$i++;
2892
			}
2893
2894
			// Add revenue stamp to total
2895
			$this->total_ttc       			+= isset($this->revenuestamp)?$this->revenuestamp:0;
2896
			$this->multicurrency_total_ttc  += isset($this->revenuestamp)?($this->revenuestamp * $multicurrency_tx):0;
2897
2898
			// Situations totals
2899
			if ($this->situation_cycle_ref && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE )
2900
			{
2901
				$prev_sits = $this->get_prev_sits();
2902
2903
				foreach ($prev_sits as $sit) {				// $sit is an object Facture loaded with a fetch.
2904
					$this->total_ht -= $sit->total_ht;
2905
					$this->total_tva -= $sit->total_tva;
2906
					$this->total_localtax1 -= $sit->total_localtax1;
2907
					$this->total_localtax2 -= $sit->total_localtax2;
2908
					$this->total_ttc -= $sit->total_ttc;
2909
					$this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
2910
					$this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
2911
					$this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
2912
				}
2913
			}
2914
2915
			$this->db->free($resql);
2916
2917
			// Now update global field total_ht, total_ttc and tva
2918
			$fieldht='total_ht';
2919
			$fieldtva='tva';
2920
			$fieldlocaltax1='localtax1';
2921
			$fieldlocaltax2='localtax2';
2922
			$fieldttc='total_ttc';
2923
			// Specific code for backward compatibility with old field names
2924
			if ($this->element == 'facture' || $this->element == 'facturerec')             $fieldht='total';
2925
			if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='total_tva';
2926
			if ($this->element == 'propal')                                                $fieldttc='total';
2927
			if ($this->element == 'expensereport')                                         $fieldtva='total_tva';
2928
			if ($this->element == 'supplier_proposal')                                     $fieldttc='total';
2929
2930
			if (empty($nodatabaseupdate))
2931
			{
2932
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2933
				$sql .= " ".$fieldht."='".price2num($this->total_ht)."',";
2934
				$sql .= " ".$fieldtva."='".price2num($this->total_tva)."',";
2935
				$sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',";
2936
				$sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',";
2937
				$sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'";
2938
						$sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'";
2939
						$sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'";
2940
						$sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'";
2941
				$sql .= ' WHERE rowid = '.$this->id;
2942
2943
2944
				dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2945
				$resql=$this->db->query($sql);
2946
				if (! $resql)
2947
				{
2948
					$error++;
2949
					$this->error=$this->db->lasterror();
2950
					$this->errors[]=$this->db->lasterror();
2951
				}
2952
			}
2953
2954
			if (! $error)
2955
			{
2956
				return 1;
2957
			}
2958
			else
2959
			{
2960
				return -1;
2961
			}
2962
		}
2963
		else
2964
		{
2965
			dol_print_error($this->db, 'Bad request in update_price');
2966
			return -1;
2967
		}
2968
	}
2969
2970
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2971
	/**
2972
	 *	Add objects linked in llx_element_element.
2973
	 *
2974
	 *	@param		string	$origin		Linked element type
2975
	 *	@param		int		$origin_id	Linked element id
2976
	 *	@return		int					<=0 if KO, >0 if OK
2977
	 *	@see		fetchObjectLinked(), updateObjectLinked(), deleteObjectLinked()
2978
	 */
2979
	public function add_object_linked($origin = null, $origin_id = null)
2980
	{
2981
		// phpcs:enable
2982
		$origin = (! empty($origin) ? $origin : $this->origin);
2983
		$origin_id = (! empty($origin_id) ? $origin_id : $this->origin_id);
2984
2985
		// Special case
2986
		if ($origin == 'order') $origin='commande';
2987
		if ($origin == 'invoice') $origin='facture';
2988
		if ($origin == 'invoice_template') $origin='facturerec';
2989
		if ($origin == 'supplierorder') $origin='order_supplier';
2990
		$this->db->begin();
2991
2992
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
2993
		$sql.= "fk_source";
2994
		$sql.= ", sourcetype";
2995
		$sql.= ", fk_target";
2996
		$sql.= ", targettype";
2997
		$sql.= ") VALUES (";
2998
		$sql.= $origin_id;
2999
		$sql.= ", '".$this->db->escape($origin)."'";
3000
		$sql.= ", ".$this->id;
3001
		$sql.= ", '".$this->db->escape($this->element)."'";
3002
		$sql.= ")";
3003
3004
		dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG);
3005
		if ($this->db->query($sql))
3006
		{
3007
			$this->db->commit();
3008
			return 1;
3009
		}
3010
		else
3011
		{
3012
			$this->error=$this->db->lasterror();
3013
			$this->db->rollback();
3014
			return 0;
3015
		}
3016
	}
3017
3018
	/**
3019
	 *	Fetch array of objects linked to current object (object of enabled modules only). Links are loaded into
3020
	 *		this->linkedObjectsIds array and
3021
	 *		this->linkedObjects array if $loadalsoobjects = 1
3022
	 *  Possible usage for parameters:
3023
	 *  - all parameters empty -> we look all link to current object (current object can be source or target)
3024
	 *  - source id+type -> will get target list linked to source
3025
	 *  - target id+type -> will get source list linked to target
3026
	 *  - source id+type + target type -> will get target list of the type
3027
	 *  - target id+type + target source -> will get source list of the type
3028
	 *
3029
	 *	@param	int		$sourceid			Object source id (if not defined, id of object)
3030
	 *	@param  string	$sourcetype			Object source type (if not defined, element name of object)
3031
	 *	@param  int		$targetid			Object target id (if not defined, id of object)
3032
	 *	@param  string	$targettype			Object target type (if not defined, elemennt name of object)
3033
	 *	@param  string	$clause				'OR' or 'AND' clause used when both source id and target id are provided
3034
	 *  @param  int		$alsosametype		0=Return only links to object that differs from source type. 1=Include also link to objects of same type.
3035
	 *  @param  string	$orderby			SQL 'ORDER BY' clause
3036
	 *  @param	int		$loadalsoobjects	Load also array this->linkedObjects (Use 0 to increase performances)
3037
	 *	@return int							<0 if KO, >0 if OK
3038
	 *  @see	add_object_linked(), updateObjectLinked(), deleteObjectLinked()
3039
	 */
3040
	public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
3041
	{
3042
		global $conf;
3043
3044
		$this->linkedObjectsIds=array();
3045
		$this->linkedObjects=array();
3046
3047
		$justsource=false;
3048
		$justtarget=false;
3049
		$withtargettype=false;
3050
		$withsourcetype=false;
3051
3052
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid))
3053
		{
3054
			$justsource=true;  // the source (id and type) is a search criteria
3055
			if (! empty($targettype)) $withtargettype=true;
3056
		}
3057
		if (! empty($targetid) && ! empty($targettype) && empty($sourceid))
3058
		{
3059
			$justtarget=true;  // the target (id and type) is a search criteria
3060
			if (! empty($sourcetype)) $withsourcetype=true;
3061
		}
3062
3063
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3064
		$targetid = (! empty($targetid) ? $targetid : $this->id);
3065
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3066
		$targettype = (! empty($targettype) ? $targettype : $this->element);
3067
3068
		/*if (empty($sourceid) && empty($targetid))
3069
		 {
3070
		 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
3071
		 return -1;
3072
		 }*/
3073
3074
		// Links between objects are stored in table element_element
3075
		$sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype';
3076
		$sql.= ' FROM '.MAIN_DB_PREFIX.'element_element';
3077
		$sql.= " WHERE ";
3078
		if ($justsource || $justtarget)
3079
		{
3080
			if ($justsource)
3081
			{
3082
				$sql.= "fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."'";
3083
				if ($withtargettype) $sql.= " AND targettype = '".$targettype."'";
3084
			}
3085
			elseif ($justtarget)
3086
			{
3087
				$sql.= "fk_target = ".$targetid." AND targettype = '".$targettype."'";
3088
				if ($withsourcetype) $sql.= " AND sourcetype = '".$sourcetype."'";
3089
			}
3090
		}
3091
		else
3092
		{
3093
			$sql.= "(fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."')";
3094
			$sql.= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$targettype."')";
3095
		}
3096
		$sql .= ' ORDER BY '.$orderby;
3097
3098
		dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3099
		$resql = $this->db->query($sql);
3100
		if ($resql)
3101
		{
3102
			$num = $this->db->num_rows($resql);
3103
			$i = 0;
3104
			while ($i < $num)
3105
			{
3106
				$obj = $this->db->fetch_object($resql);
3107
				if ($justsource || $justtarget)
3108
				{
3109
					if ($justsource)
3110
					{
3111
						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3112
					}
3113
					elseif ($justtarget)
3114
					{
3115
						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3116
					}
3117
				}
3118
				else
3119
				{
3120
					if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype)
3121
					{
3122
						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3123
					}
3124
					if ($obj->fk_target == $targetid && $obj->targettype == $targettype)
3125
					{
3126
						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3127
					}
3128
				}
3129
				$i++;
3130
			}
3131
3132
			if (! empty($this->linkedObjectsIds))
3133
			{
3134
				$tmparray = $this->linkedObjectsIds;
3135
				foreach($tmparray as $objecttype => $objectids)       // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
3136
				{
3137
					// Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
3138
					$module = $element = $subelement = $objecttype;
3139
					if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
3140
						&& preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs))
3141
					{
3142
						$module = $element = $regs[1];
3143
						$subelement = $regs[2];
3144
					}
3145
3146
					$classpath = $element.'/class';
3147
					// To work with non standard classpath or module name
3148
					if ($objecttype == 'facture')			{
3149
						$classpath = 'compta/facture/class';
3150
					}
3151
					elseif ($objecttype == 'facturerec')			{
3152
						$classpath = 'compta/facture/class'; $module = 'facture';
3153
					}
3154
					elseif ($objecttype == 'propal')			{
3155
						$classpath = 'comm/propal/class';
3156
					}
3157
					elseif ($objecttype == 'supplier_proposal')			{
3158
						$classpath = 'supplier_proposal/class';
3159
					}
3160
					elseif ($objecttype == 'shipping')			{
3161
						$classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon';
3162
					}
3163
					elseif ($objecttype == 'delivery')			{
3164
						$classpath = 'livraison/class'; $subelement = 'livraison'; $module = 'livraison_bon';
3165
					}
3166
					elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier')	{
3167
						$classpath = 'fourn/class'; $module = 'fournisseur';
3168
					}
3169
					elseif ($objecttype == 'fichinter')			{
3170
						$classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter';
3171
					}
3172
					elseif ($objecttype == 'subscription')			{
3173
						$classpath = 'adherents/class'; $module = 'adherent';
3174
					}
3175
3176
					// Set classfile
3177
					$classfile = strtolower($subelement); $classname = ucfirst($subelement);
3178
3179
					if ($objecttype == 'order') {
3180
						$classfile = 'commande'; $classname = 'Commande';
3181
					}
3182
					elseif ($objecttype == 'invoice_supplier') {
3183
						$classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur';
3184
					}
3185
					elseif ($objecttype == 'order_supplier')   {
3186
						$classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur';
3187
					}
3188
					elseif ($objecttype == 'supplier_proposal')   {
3189
						$classfile = 'supplier_proposal'; $classname = 'SupplierProposal';
3190
					}
3191
					elseif ($objecttype == 'facturerec')   {
3192
						$classfile = 'facture-rec'; $classname = 'FactureRec';
3193
					}
3194
					elseif ($objecttype == 'subscription')   {
3195
						$classfile = 'subscription'; $classname = 'Subscription';
3196
					}
3197
3198
					// Here $module, $classfile and $classname are set
3199
					if ($conf->$module->enabled && (($element != $this->element) || $alsosametype))
3200
					{
3201
						if ($loadalsoobjects)
3202
						{
3203
							dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
3204
							//print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
3205
							if (class_exists($classname))
3206
							{
3207
								foreach($objectids as $i => $objectid)	// $i is rowid into llx_element_element
3208
								{
3209
									$object = new $classname($this->db);
3210
									$ret = $object->fetch($objectid);
3211
									if ($ret >= 0)
3212
									{
3213
										$this->linkedObjects[$objecttype][$i] = $object;
3214
									}
3215
								}
3216
							}
3217
						}
3218
					}
3219
					else
3220
					{
3221
						unset($this->linkedObjectsIds[$objecttype]);
3222
					}
3223
				}
3224
			}
3225
			return 1;
3226
		}
3227
		else
3228
		{
3229
			dol_print_error($this->db);
3230
			return -1;
3231
		}
3232
	}
3233
3234
	/**
3235
	 *	Update object linked of a current object
3236
	 *
3237
	 *	@param	int		$sourceid		Object source id
3238
	 *	@param  string	$sourcetype		Object source type
3239
	 *	@param  int		$targetid		Object target id
3240
	 *	@param  string	$targettype		Object target type
3241
	 *	@return							int	>0 if OK, <0 if KO
3242
	 *	@see	add_object_linked(), fetObjectLinked(), deleteObjectLinked()
3243
	 */
3244
	public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '')
3245
	{
3246
		$updatesource=false;
3247
		$updatetarget=false;
3248
3249
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource=true;
3250
		elseif (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $updatetarget=true;
3251
3252
		$sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET ";
3253
		if ($updatesource)
3254
		{
3255
			$sql.= "fk_source = ".$sourceid;
3256
			$sql.= ", sourcetype = '".$this->db->escape($sourcetype)."'";
3257
			$sql.= " WHERE fk_target = ".$this->id;
3258
			$sql.= " AND targettype = '".$this->db->escape($this->element)."'";
3259
		}
3260
		elseif ($updatetarget)
3261
		{
3262
			$sql.= "fk_target = ".$targetid;
3263
			$sql.= ", targettype = '".$this->db->escape($targettype)."'";
3264
			$sql.= " WHERE fk_source = ".$this->id;
3265
			$sql.= " AND sourcetype = '".$this->db->escape($this->element)."'";
3266
		}
3267
3268
		dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG);
3269
		if ($this->db->query($sql))
3270
		{
3271
			return 1;
3272
		}
3273
		else
3274
		{
3275
			$this->error=$this->db->lasterror();
3276
			return -1;
3277
		}
3278
	}
3279
3280
	/**
3281
	 *	Delete all links between an object $this
3282
	 *
3283
	 *	@param	int		$sourceid		Object source id
3284
	 *	@param  string	$sourcetype		Object source type
3285
	 *	@param  int		$targetid		Object target id
3286
	 *	@param  string	$targettype		Object target type
3287
	 *  @param	int		$rowid			Row id of line to delete. If defined, other parameters are not used.
3288
	 *	@return     					int	>0 if OK, <0 if KO
3289
	 *	@see	add_object_linked(), updateObjectLinked(), fetchObjectLinked()
3290
	 */
3291
	public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = '')
3292
	{
3293
		$deletesource=false;
3294
		$deletetarget=false;
3295
3296
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource=true;
3297
		elseif (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $deletetarget=true;
3298
3299
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3300
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3301
		$targetid = (! empty($targetid) ? $targetid : $this->id);
3302
		$targettype = (! empty($targettype) ? $targettype : $this->element);
3303
3304
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element";
3305
		$sql.= " WHERE";
3306
		if ($rowid > 0)
3307
		{
3308
			$sql.=" rowid = ".$rowid;
3309
		}
3310
		else
3311
		{
3312
			if ($deletesource)
3313
			{
3314
				$sql.= " fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3315
				$sql.= " AND fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."'";
3316
			}
3317
			elseif ($deletetarget)
3318
			{
3319
				$sql.= " fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'";
3320
				$sql.= " AND fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."'";
3321
			}
3322
			else
3323
			{
3324
				$sql.= " (fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."')";
3325
				$sql.= " OR";
3326
				$sql.= " (fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."')";
3327
			}
3328
		}
3329
3330
		dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG);
3331
		if ($this->db->query($sql))
3332
		{
3333
			return 1;
3334
		}
3335
		else
3336
		{
3337
			$this->error=$this->db->lasterror();
3338
			$this->errors[]=$this->error;
3339
			return -1;
3340
		}
3341
	}
3342
3343
	/**
3344
	 *      Set status of an object
3345
	 *
3346
	 *      @param	int		$status			Status to set
3347
	 *      @param	int		$elementId		Id of element to force (use this->id by default)
3348
	 *      @param	string	$elementType	Type of element to force (use this->table_element by default)
3349
	 *      @param	string	$trigkey		Trigger key to use for trigger
3350
	 *      @return int						<0 if KO, >0 if OK
3351
	 */
3352
	public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '')
3353
	{
3354
		global $user,$langs,$conf;
3355
3356
		$savElementId=$elementId;  // To be used later to know if we were using the method using the id of this or not.
3357
3358
		$elementId = (!empty($elementId)?$elementId:$this->id);
3359
		$elementTable = (!empty($elementType)?$elementType:$this->table_element);
3360
3361
		$this->db->begin();
3362
3363
		$fieldstatus="fk_statut";
3364
		if ($elementTable == 'facture_rec') $fieldstatus="suspended";
3365
		if ($elementTable == 'mailing') $fieldstatus="statut";
3366
		if ($elementTable == 'cronjob') $fieldstatus="status";
3367
		if ($elementTable == 'user') $fieldstatus="statut";
3368
		if ($elementTable == 'expensereport') $fieldstatus="fk_statut";
3369
		if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status";
3370
3371
		$sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable;
3372
		$sql.= " SET ".$fieldstatus." = ".$status;
3373
		// If status = 1 = validated, update also fk_user_valid
3374
		if ($status == 1 && $elementTable == 'expensereport') $sql.=", fk_user_valid = ".$user->id;
3375
		$sql.= " WHERE rowid=".$elementId;
3376
3377
		dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
3378
		if ($this->db->query($sql))
3379
		{
3380
			$error = 0;
3381
3382
			// Try autoset of trigkey
3383
			if (empty($trigkey))
3384
			{
3385
				if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_SIGN';   // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
3386
				if ($this->element == 'supplier_proposal' && $status == 3) $trigkey='SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
3387
				if ($this->element == 'supplier_proposal' && $status == 4) $trigkey='SUPPLIER_PROPOSAL_CLOSE';  // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
3388
				if ($this->element == 'fichinter' && $status == 3) $trigkey='FICHINTER_CLASSIFY_DONE';
3389
				if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED';
3390
				if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED';
3391
			}
3392
3393
			if ($trigkey)
3394
			{
3395
				// Appel des triggers
3396
				include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
3397
				$interface=new Interfaces($this->db);
3398
				$result=$interface->run_triggers($trigkey, $this, $user, $langs, $conf);
3399
				if ($result < 0) {
3400
					$error++; $this->errors=$interface->errors;
3401
				}
3402
				// Fin appel triggers
3403
			}
3404
3405
			if (! $error)
3406
			{
3407
				$this->db->commit();
3408
3409
				if (empty($savElementId))    // If the element we update was $this (so $elementId is null)
3410
				{
3411
					$this->statut = $status;
3412
					$this->status = $status;
3413
				}
3414
3415
				return 1;
3416
			}
3417
			else
3418
			{
3419
				$this->db->rollback();
3420
				dol_syslog(get_class($this)."::setStatus ".$this->error, LOG_ERR);
3421
				return -1;
3422
			}
3423
		}
3424
		else
3425
		{
3426
			$this->error=$this->db->lasterror();
3427
			$this->db->rollback();
3428
			return -1;
3429
		}
3430
	}
3431
3432
3433
	/**
3434
	 *  Load type of canvas of an object if it exists
3435
	 *
3436
	 *  @param      int		$id     Record id
3437
	 *  @param      string	$ref    Record ref
3438
	 *  @return		int				<0 if KO, 0 if nothing done, >0 if OK
3439
	 */
3440
	public function getCanvas($id = 0, $ref = '')
3441
	{
3442
		global $conf;
3443
3444
		if (empty($id) && empty($ref)) return 0;
3445
		if (! empty($conf->global->MAIN_DISABLE_CANVAS)) return 0;    // To increase speed. Not enabled by default.
3446
3447
		// Clean parameters
3448
		$ref = trim($ref);
3449
3450
		$sql = "SELECT rowid, canvas";
3451
		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
3452
		$sql.= " WHERE entity IN (".getEntity($this->element).")";
3453
		if (! empty($id))  $sql.= " AND rowid = ".$id;
3454
		if (! empty($ref)) $sql.= " AND ref = '".$this->db->escape($ref)."'";
3455
3456
		$resql = $this->db->query($sql);
3457
		if ($resql)
3458
		{
3459
			$obj = $this->db->fetch_object($resql);
3460
			if ($obj)
3461
			{
3462
				$this->canvas   = $obj->canvas;
3463
				return 1;
3464
			}
3465
			else return 0;
3466
		}
3467
		else
3468
		{
3469
			dol_print_error($this->db);
3470
			return -1;
3471
		}
3472
	}
3473
3474
3475
	/**
3476
	 * 	Get special code of a line
3477
	 *
3478
	 * 	@param	int		$lineid		Id of line
3479
	 * 	@return	int					Special code
3480
	 */
3481
	public function getSpecialCode($lineid)
3482
	{
3483
		$sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line;
3484
		$sql.= ' WHERE rowid = '.$lineid;
3485
		$resql = $this->db->query($sql);
3486
		if ($resql)
3487
		{
3488
			$row = $this->db->fetch_row($resql);
3489
			return $row[0];
3490
		}
3491
	}
3492
3493
	/**
3494
	 *  Function to check if an object is used by others.
3495
	 *  Check is done into this->childtables. There is no check into llx_element_element.
3496
	 *
3497
	 *  @param	int		$id			Force id of object
3498
	 *  @return	int					<0 if KO, 0 if not used, >0 if already used
3499
	 */
3500
	public function isObjectUsed($id = 0)
3501
	{
3502
		global $langs;
3503
3504
		if (empty($id)) $id=$this->id;
3505
3506
		// Check parameters
3507
		if (! isset($this->childtables) || ! is_array($this->childtables) || count($this->childtables) == 0)
3508
		{
3509
			dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
3510
			return -1;
3511
		}
3512
3513
		$arraytoscan = $this->childtables;
3514
		// For backward compatibility, we check if array is old format array('table1', 'table2', ...)
3515
		$tmparray=array_keys($this->childtables);
3516
		if (is_numeric($tmparray[0]))
3517
		{
3518
			$arraytoscan = array_flip($this->childtables);
3519
		}
3520
3521
		// Test if child exists
3522
		$haschild=0;
3523
		foreach($arraytoscan as $table => $elementname)
3524
		{
3525
			//print $id.'-'.$table.'-'.$elementname.'<br>';
3526
			// Check if third party can be deleted
3527
			$sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table;
3528
			$sql.= " WHERE ".$this->fk_element." = ".$id;
3529
			$resql=$this->db->query($sql);
3530
			if ($resql)
3531
			{
3532
				$obj=$this->db->fetch_object($resql);
3533
				if ($obj->nb > 0)
3534
				{
3535
					$langs->load("errors");
3536
					//print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
3537
					$haschild += $obj->nb;
3538
					if (is_numeric($elementname))	// old usage
3539
					{
3540
						$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $table);
3541
					}
3542
					else	// new usage: $elementname=Translation key
3543
					{
3544
						$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $langs->transnoentitiesnoconv($elementname));
3545
					}
3546
					break;    // We found at least one, we stop here
3547
				}
3548
			}
3549
			else
3550
			{
3551
				$this->errors[]=$this->db->lasterror();
3552
				return -1;
3553
			}
3554
		}
3555
		if ($haschild > 0)
3556
		{
3557
			$this->errors[]="ErrorRecordHasChildren";
3558
			return $haschild;
3559
		}
3560
		else return 0;
3561
	}
3562
3563
	/**
3564
	 *  Function to say how many lines object contains
3565
	 *
3566
	 *	@param	int		$predefined		-1=All, 0=Count free product/service only, 1=Count predefined product/service only, 2=Count predefined product, 3=Count predefined service
3567
	 *  @return	int						<0 if KO, 0 if no predefined products, nb of lines with predefined products if found
3568
	 */
3569
	public function hasProductsOrServices($predefined = -1)
3570
	{
3571
		$nb=0;
3572
3573
		foreach($this->lines as $key => $val)
3574
		{
3575
			$qualified=0;
3576
			if ($predefined == -1) $qualified=1;
3577
			if ($predefined == 1 && $val->fk_product > 0) $qualified=1;
3578
			if ($predefined == 0 && $val->fk_product <= 0) $qualified=1;
3579
			if ($predefined == 2 && $val->fk_product > 0 && $val->product_type==0) $qualified=1;
3580
			if ($predefined == 3 && $val->fk_product > 0 && $val->product_type==1) $qualified=1;
3581
			if ($qualified) $nb++;
3582
		}
3583
		dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
3584
		return $nb;
3585
	}
3586
3587
	/**
3588
	 * Function that returns the total amount HT of discounts applied for all lines.
3589
	 *
3590
	 * @return 	float
3591
	 */
3592
	public function getTotalDiscount()
3593
	{
3594
		$total_discount=0.00;
3595
3596
		$sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
3597
		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element."det";
3598
		$sql.= " WHERE ".$this->fk_element." = ".$this->id;
3599
3600
		dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
3601
		$resql = $this->db->query($sql);
3602
		if ($resql)
3603
		{
3604
			$num=$this->db->num_rows($resql);
3605
			$i=0;
3606
			while ($i < $num)
3607
			{
3608
				$obj = $this->db->fetch_object($resql);
3609
3610
				$pu_ht = $obj->pu_ht;
3611
				$qty= $obj->qty;
3612
				$total_ht = $obj->total_ht;
3613
3614
				$total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
3615
				$total_discount += $total_discount_line;
3616
3617
				$i++;
3618
			}
3619
		}
3620
3621
		//print $total_discount; exit;
3622
		return price2num($total_discount);
3623
	}
3624
3625
3626
	/**
3627
	 * Return into unit=0, the calculated total of weight and volume of all lines * qty
3628
	 * Calculate by adding weight and volume of each product line, so properties ->volume/volume_units/weight/weight_units must be loaded on line.
3629
	 *
3630
	 * @return  array                           array('weight'=>...,'volume'=>...)
3631
	 */
3632
	public function getTotalWeightVolume()
3633
	{
3634
		$totalWeight = 0;
3635
		$totalVolume = 0;
3636
		// defined for shipment only
3637
		$totalOrdered = '';
3638
		// defined for shipment only
3639
		$totalToShip = '';
3640
3641
		foreach ($this->lines as $line)
3642
		{
3643
			if (isset($line->qty_asked))
3644
			{
3645
				if (empty($totalOrdered)) $totalOrdered=0;  // Avoid warning because $totalOrdered is ''
3646
				$totalOrdered+=$line->qty_asked;    // defined for shipment only
3647
			}
3648
			if (isset($line->qty_shipped))
3649
			{
3650
				if (empty($totalToShip)) $totalToShip=0;    // Avoid warning because $totalToShip is ''
3651
				$totalToShip+=$line->qty_shipped;   // defined for shipment only
3652
			}
3653
			elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty))
3654
			{
3655
				if (empty($totalToShip)) $totalToShip=0;
3656
				$totalToShip+=$line->qty;   // defined for reception only
3657
			}
3658
3659
			// Define qty, weight, volume, weight_units, volume_units
3660
			if ($this->element == 'shipping') {
3661
				// for shipments
3662
				$qty = $line->qty_shipped ? $line->qty_shipped : 0;
3663
			}
3664
			else {
3665
				$qty = $line->qty ? $line->qty : 0;
3666
			}
3667
3668
			$weight = $line->weight ? $line->weight : 0;
3669
            ($weight==0 && !empty($line->product->weight))? $weight=$line->product->weight: 0;
3670
			$volume = $line->volume ? $line->volume : 0;
3671
			($volume==0 && !empty($line->product->volume))? $volume=$line->product->volume: 0;
3672
3673
			$weight_units=$line->weight_units;
3674
			($weight_units==0 && !empty($line->product->weight_units))? $weight_units=$line->product->weight_units: 0;
3675
			$volume_units=$line->volume_units;
3676
			($volume_units==0 && !empty($line->product->volume_units))? $volume_units=$line->product->volume_units: 0;
3677
3678
			$weightUnit=0;
3679
			$volumeUnit=0;
3680
			if (! empty($weight_units)) $weightUnit = $weight_units;
3681
			if (! empty($volume_units)) $volumeUnit = $volume_units;
3682
3683
			if (empty($totalWeight)) $totalWeight=0;  // Avoid warning because $totalWeight is ''
3684
			if (empty($totalVolume)) $totalVolume=0;  // Avoid warning because $totalVolume is ''
3685
3686
			//var_dump($line->volume_units);
3687
			if ($weight_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3688
			{
3689
				$trueWeightUnit=pow(10, $weightUnit);
3690
				$totalWeight += $weight * $qty * $trueWeightUnit;
3691
			}
3692
			else {
3693
		if ($weight_units == 99) {
3694
			// conversion 1 Pound = 0.45359237 KG
3695
			$trueWeightUnit = 0.45359237;
3696
			$totalWeight += $weight * $qty * $trueWeightUnit;
3697
		} elseif ($weight_units == 98) {
3698
			// conversion 1 Ounce = 0.0283495 KG
3699
			$trueWeightUnit = 0.0283495;
3700
			$totalWeight += $weight * $qty * $trueWeightUnit;
3701
		}
3702
		else
3703
					$totalWeight += $weight * $qty;   // This may be wrong if we mix different units
3704
			}
3705
			if ($volume_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3706
			{
3707
				//print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
3708
				$trueVolumeUnit=pow(10, $volumeUnit);
3709
				//print $line->volume;
3710
				$totalVolume += $volume * $qty * $trueVolumeUnit;
3711
			}
3712
			else
3713
			{
3714
				$totalVolume += $volume * $qty;   // This may be wrong if we mix different units
3715
			}
3716
		}
3717
3718
		return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
3719
	}
3720
3721
3722
	/**
3723
	 *	Set extra parameters
3724
	 *
3725
	 *	@return	int      <0 if KO, >0 if OK
3726
	 */
3727
	public function setExtraParameters()
3728
	{
3729
		$this->db->begin();
3730
3731
		$extraparams = (! empty($this->extraparams) ? json_encode($this->extraparams) : null);
3732
3733
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3734
		$sql.= " SET extraparams = ".(! empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
3735
		$sql.= " WHERE rowid = ".$this->id;
3736
3737
		dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
3738
		$resql = $this->db->query($sql);
3739
		if (! $resql)
3740
		{
3741
			$this->error=$this->db->lasterror();
3742
			$this->db->rollback();
3743
			return -1;
3744
		}
3745
		else
3746
		{
3747
			$this->db->commit();
3748
			return 1;
3749
		}
3750
	}
3751
3752
3753
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3754
	/**
3755
	 *    Return incoterms informations
3756
	 *    TODO Use a cache for label get
3757
	 *
3758
	 *    @return	string	incoterms info
3759
	 */
3760
	public function display_incoterms()
3761
	{
3762
        // phpcs:enable
3763
		$out = '';
3764
		$this->libelle_incoterms = '';
3765
		if (!empty($this->fk_incoterms))
3766
		{
3767
			$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3768
			$result = $this->db->query($sql);
3769
			if ($result)
3770
			{
3771
				$res = $this->db->fetch_object($result);
3772
				$out .= $res->code;
3773
			}
3774
		}
3775
3776
		$out .= (($res->code && $this->location_incoterms)?' - ':'').$this->location_incoterms;
0 ignored issues
show
Bug introduced by
The variable $res does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3777
3778
		return $out;
3779
	}
3780
3781
	/**
3782
	 *    Return incoterms informations for pdf display
3783
	 *
3784
	 *    @return	string		incoterms info
3785
	 */
3786
	public function getIncotermsForPDF()
3787
	{
3788
		$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3789
		$resql = $this->db->query($sql);
3790
		if ($resql)
3791
		{
3792
			$num = $this->db->num_rows($resql);
3793
			if ($num > 0)
3794
			{
3795
				$res = $this->db->fetch_object($resql);
3796
				return 'Incoterm : '.$res->code.' - '.$this->location_incoterms;
3797
			}
3798
			else
3799
			{
3800
				return '';
3801
			}
3802
		}
3803
		else
3804
		{
3805
			$this->errors[] = $this->db->lasterror();
3806
			return false;
3807
		}
3808
	}
3809
3810
	/**
3811
	 *    Define incoterms values of current object
3812
	 *
3813
	 *    @param	int		$id_incoterm     Id of incoterm to set or '' to remove
3814
	 * 	  @param 	string  $location		 location of incoterm
3815
	 *    @return	int     		<0 if KO, >0 if OK
3816
	 */
3817
	public function setIncoterms($id_incoterm, $location)
3818
	{
3819
		if ($this->id && $this->table_element)
3820
		{
3821
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3822
			$sql.= " SET fk_incoterms = ".($id_incoterm > 0 ? $id_incoterm : "null");
3823
			$sql.= ", location_incoterms = ".($id_incoterm > 0 ? "'".$this->db->escape($location)."'" : "null");
3824
			$sql.= " WHERE rowid = " . $this->id;
3825
			dol_syslog(get_class($this).'::setIncoterms', LOG_DEBUG);
3826
			$resql=$this->db->query($sql);
3827
			if ($resql)
3828
			{
3829
				$this->fk_incoterms = $id_incoterm;
3830
				$this->location_incoterms = $location;
3831
3832
				$sql = 'SELECT libelle FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3833
				$res = $this->db->query($sql);
3834
				if ($res)
3835
				{
3836
					$obj = $this->db->fetch_object($res);
3837
					$this->libelle_incoterms = $obj->libelle;
3838
				}
3839
				return 1;
3840
			}
3841
			else
3842
			{
3843
				$this->errors[] = $this->db->lasterror();
3844
				return -1;
3845
			}
3846
		}
3847
		else return -1;
3848
	}
3849
3850
3851
	// --------------------
3852
	// TODO: All functions here must be redesigned and moved as they are not business functions but output functions
3853
	// --------------------
3854
3855
	/* This is to show add lines */
3856
3857
	/**
3858
	 *	Show add free and predefined products/services form
3859
	 *
3860
	 *  @param	int		        $dateSelector       1=Show also date range input fields
3861
	 *  @param	Societe			$seller				Object thirdparty who sell
3862
	 *  @param	Societe			$buyer				Object thirdparty who buy
3863
	 *  @param	string			$defaulttpldir		Directory where to find the template
3864
	 *	@return	void
3865
	 */
3866
	public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
3867
	{
3868
		global $conf,$user,$langs,$object,$hookmanager;
3869
		global $form,$bcnd,$var;
3870
3871
		// Line extrafield
3872
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3873
		$extrafieldsline = new ExtraFields($this->db);
3874
		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3875
3876
		// Output template part (modules that overwrite templates must declare this into descriptor)
3877
		// Use global variables + $dateSelector + $seller and $buyer
3878
		$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
3879
		foreach($dirtpls as $module => $reldir)
3880
		{
3881
			if (!empty($module))
3882
			{
3883
				$tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
3884
			}
3885
			else
3886
			{
3887
				$tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
3888
			}
3889
			if (empty($conf->file->strict_mode)) {
3890
				$res=@include $tpl;
3891
			} else {
3892
				$res=include $tpl; // for debug
3893
			}
3894
			if ($res) break;
3895
		}
3896
	}
3897
3898
3899
3900
	/* This is to show array of line of details */
3901
3902
3903
	/**
3904
	 *	Return HTML table for object lines
3905
	 *	TODO Move this into an output class file (htmlline.class.php)
3906
	 *	If lines are into a template, title must also be into a template
3907
	 *	But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
3908
	 *
3909
	 *	@param	string		$action				Action code
3910
	 *	@param  string		$seller            	Object of seller third party
3911
	 *	@param  string  	$buyer             	Object of buyer third party
3912
	 *	@param	int			$selected		   	Object line selected
3913
	 *	@param  int	    	$dateSelector      	1=Show also date range input fields
3914
	 *  @param	string		$defaulttpldir		Directory where to find the template
3915
	 *	@return	void
3916
	 */
3917
	public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
3918
	{
3919
		global $conf, $hookmanager, $langs, $user, $object, $form;
3920
		// TODO We should not use global var for this
3921
		global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
3922
3923
		// Define usemargins
3924
		$usemargins=0;
3925
		if (! empty($conf->margin->enabled) && ! empty($this->element) && in_array($this->element, array('facture','facturerec','propal','commande'))) $usemargins=1;
3926
3927
		$num = count($this->lines);
3928
3929
		// Line extrafield
3930
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3931
		$extrafieldsline = new ExtraFields($this->db);
3932
		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3933
3934
		$parameters = array('num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
0 ignored issues
show
Bug introduced by
The variable $i seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
3935
		$reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3936
		if (empty($reshook))
3937
		{
3938
			// Output template part (modules that overwrite templates must declare this into descriptor)
3939
			// Use global variables + $dateSelector + $seller and $buyer
3940
			$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
3941
			foreach($dirtpls as $module => $reldir)
3942
			{
3943
				if (!empty($module))
3944
				{
3945
					$tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
3946
				}
3947
				else
3948
				{
3949
					$tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
3950
				}
3951
				if (empty($conf->file->strict_mode)) {
3952
					$res=@include $tpl;
3953
				} else {
3954
					$res=include $tpl; // for debug
3955
				}
3956
				if ($res) break;
3957
			}
3958
		}
3959
3960
		$var = true;
3961
		$i	 = 0;
3962
3963
		print "<tbody>\n";
3964
		foreach ($this->lines as $line)
3965
		{
3966
			//Line extrafield
3967
			$line->fetch_optionals();
3968
3969
			//if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
3970
			if (is_object($hookmanager))   // Old code is commented on preceding line.
3971
			{
3972
				if (empty($line->fk_parent_line))
3973
				{
3974
					$parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
3975
					$reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3976
				}
3977
				else
3978
				{
3979
					$parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline, 'fk_parent_line'=>$line->fk_parent_line);
3980
					$reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3981
				}
3982
			}
3983
			if (empty($reshook))
3984
			{
3985
				$this->printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafieldsline, $defaulttpldir);
3986
			}
3987
3988
			$i++;
3989
		}
3990
		print "</tbody><!-- end printObjectLines() -->\n";
3991
	}
3992
3993
	/**
3994
	 *	Return HTML content of a detail line
3995
	 *	TODO Move this into an output class file (htmlline.class.php)
3996
	 *
3997
	 *	@param	string      $action				GET/POST action
3998
	 *	@param  CommonObjectLine $line		       	Selected object line to output
3999
	 *	@param  string	    $var               	Is it a an odd line (true)
4000
	 *	@param  int		    $num               	Number of line (0)
4001
	 *	@param  int		    $i					I
4002
	 *	@param  int		    $dateSelector      	1=Show also date range input fields
4003
	 *	@param  string	    $seller            	Object of seller third party
4004
	 *	@param  string	    $buyer             	Object of buyer third party
4005
	 *	@param	int			$selected		   	Object line selected
4006
	 *  @param  int			$extrafieldsline	Object of extrafield line attribute
4007
	 *  @param	string		$defaulttpldir		Directory where to find the template
4008
	 *	@return	void
4009
	 */
4010
	public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafieldsline = 0, $defaulttpldir = '/core/tpl')
4011
	{
4012
		global $conf,$langs,$user,$object,$hookmanager;
4013
		global $form,$bc,$bcdd;
4014
		global $object_rights, $disableedit, $disablemove, $disableremove;   // TODO We should not use global var for this !
4015
4016
		$object_rights = $this->getRights();
4017
4018
		$element=$this->element;
4019
4020
		$text=''; $description=''; $type=0;
4021
4022
		// Show product and description
4023
		$type=(! empty($line->product_type)?$line->product_type:$line->fk_product_type);
4024
		// Try to enhance type detection using date_start and date_end for free lines where type was not saved.
4025
		if (! empty($line->date_start)) $type=1; // deprecated
4026
		if (! empty($line->date_end)) $type=1; // deprecated
4027
4028
		// Ligne en mode visu
4029
		if ($action != 'editline' || $selected != $line->id)
4030
		{
4031
			// Product
4032
			if ($line->fk_product > 0)
4033
			{
4034
				$product_static = new Product($this->db);
4035
				$product_static->fetch($line->fk_product);
4036
4037
				$product_static->ref = $line->ref; //can change ref in hook
4038
				$product_static->label = $line->label; //can change label in hook
4039
				$text=$product_static->getNomUrl(1);
4040
4041
				// Define output language and label
4042
				if (! empty($conf->global->MAIN_MULTILANGS))
4043
				{
4044
					if (property_exists($this, 'socid') && ! is_object($this->thirdparty))
4045
					{
4046
						dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
4047
						return;
4048
					}
4049
4050
					$prod = new Product($this->db);
4051
					$prod->fetch($line->fk_product);
4052
4053
					$outputlangs = $langs;
4054
					$newlang='';
4055
					if (empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang=GETPOST('lang_id', 'aZ09');
4056
					if (! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang) && is_object($this->thirdparty)) $newlang=$this->thirdparty->default_lang;		// To use language of customer
4057
					if (! empty($newlang))
4058
					{
4059
						$outputlangs = new Translate("", $conf);
4060
						$outputlangs->setDefaultLang($newlang);
4061
					}
4062
4063
					$label = (! empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
4064
				}
4065
				else
4066
				{
4067
					$label = $line->product_label;
4068
				}
4069
4070
				$text.= ' - '.(! empty($line->label)?$line->label:$label);
4071
				$description.=(! empty($conf->global->PRODUIT_DESC_IN_FORM)?'':dol_htmlentitiesbr($line->description));	// Description is what to show on popup. We shown nothing if already into desc.
4072
			}
4073
4074
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4075
4076
			// Output template part (modules that overwrite templates must declare this into descriptor)
4077
			// Use global variables + $dateSelector + $seller and $buyer
4078
			$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4079
			foreach($dirtpls as $module => $reldir)
4080
			{
4081
				if (!empty($module))
4082
				{
4083
					$tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
4084
				}
4085
				else
4086
				{
4087
					$tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
4088
				}
4089
				if (empty($conf->file->strict_mode)) {
4090
					$res=@include $tpl;
4091
				} else {
4092
					$res=include $tpl; // for debug
4093
				}
4094
				if ($res) break;
4095
			}
4096
		}
4097
4098
		// Line in update mode
4099
		if ($this->statut == 0 && $action == 'editline' && $selected == $line->id)
4100
		{
4101
			$label = (! empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
4102
			$placeholder=' placeholder="'.$langs->trans("Label").'"';
4103
4104
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4105
4106
			// Output template part (modules that overwrite templates must declare this into descriptor)
4107
			// Use global variables + $dateSelector + $seller and $buyer
4108
			$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4109
			foreach($dirtpls as $module => $reldir)
4110
			{
4111
				if (!empty($module))
4112
				{
4113
					$tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
4114
				}
4115
				else
4116
				{
4117
					$tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
4118
				}
4119
				if (empty($conf->file->strict_mode)) {
4120
					$res=@include $tpl;
4121
				} else {
4122
					$res=include $tpl; // for debug
4123
				}
4124
				if ($res) break;
4125
			}
4126
		}
4127
	}
4128
4129
4130
	/* This is to show array of line of details of source object */
4131
4132
4133
	/**
4134
	 * 	Return HTML table table of source object lines
4135
	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
4136
	 *  If lines are into a template, title must also be into a template
4137
	 *  But for the moment we don't know if it's possible, so we keep the method available on overloaded objects.
4138
	 *
4139
	 *	@param	string		$restrictlist		''=All lines, 'services'=Restrict to services only
4140
	 *  @return	void
4141
	 */
4142
	public function printOriginLinesList($restrictlist = '')
4143
	{
4144
		global $langs, $hookmanager, $conf;
4145
4146
		print '<tr class="liste_titre">';
4147
		print '<td>'.$langs->trans('Ref').'</td>';
4148
		print '<td>'.$langs->trans('Description').'</td>';
4149
		print '<td class="right">'.$langs->trans('VATRate').'</td>';
4150
		print '<td class="right">'.$langs->trans('PriceUHT').'</td>';
4151
		if (!empty($conf->multicurrency->enabled)) print '<td class="right">'.$langs->trans('PriceUHTCurrency').'</td>';
4152
		print '<td class="right">'.$langs->trans('Qty').'</td>';
4153
		if($conf->global->PRODUCT_USE_UNITS)
4154
		{
4155
			print '<td class="left">'.$langs->trans('Unit').'</td>';
4156
		}
4157
		print '<td class="right">'.$langs->trans('ReductionShort').'</td></tr>';
4158
4159
		$var = true;
4160
		$i	 = 0;
4161
4162
		if (! empty($this->lines))
4163
		{
4164
			foreach ($this->lines as $line)
4165
			{
4166
				if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
4167
				{
4168
					if (empty($line->fk_parent_line))
4169
					{
4170
						$parameters=array('line'=>$line,'var'=>$var,'i'=>$i);
4171
						$action='';
4172
						$hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
4173
					}
4174
				}
4175
				else
4176
				{
4177
					$this->printOriginLine($line, $var, $restrictlist);
4178
				}
4179
4180
				$i++;
4181
			}
4182
		}
4183
	}
4184
4185
	/**
4186
	 * 	Return HTML with a line of table array of source object lines
4187
	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
4188
	 *  If lines are into a template, title must also be into a template
4189
	 *  But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
4190
	 *
4191
	 * 	@param	CommonObjectLine	$line				Line
4192
	 * 	@param	string				$var				Var
4193
	 *	@param	string				$restrictlist		''=All lines, 'services'=Restrict to services only (strike line if not)
4194
	 * 	@return	void
4195
	 */
4196
	public function printOriginLine($line, $var, $restrictlist = '')
4197
	{
4198
		global $langs, $conf;
4199
4200
		//var_dump($line);
4201
		if (!empty($line->date_start))
4202
		{
4203
			$date_start=$line->date_start;
4204
		}
4205
		else
4206
		{
4207
			$date_start=$line->date_debut_prevue;
4208
			if ($line->date_debut_reel) $date_start=$line->date_debut_reel;
4209
		}
4210
		if (!empty($line->date_end))
4211
		{
4212
			$date_end=$line->date_end;
4213
		}
4214
		else
4215
		{
4216
			$date_end=$line->date_fin_prevue;
4217
			if ($line->date_fin_reel) $date_end=$line->date_fin_reel;
4218
		}
4219
4220
		$this->tpl['label'] = '';
4221
		if (! empty($line->fk_parent_line)) $this->tpl['label'].= img_picto('', 'rightarrow');
4222
4223
		if (($line->info_bits & 2) == 2)  // TODO Not sure this is used for source object
4224
		{
4225
			$discount=new DiscountAbsolute($this->db);
4226
			$discount->fk_soc = $this->socid;
4227
			$this->tpl['label'].= $discount->getNomUrl(0, 'discount');
4228
		}
4229
		elseif (! empty($line->fk_product))
4230
		{
4231
			$productstatic = new Product($this->db);
4232
			$productstatic->id = $line->fk_product;
4233
			$productstatic->ref = $line->ref;
4234
			$productstatic->type = $line->fk_product_type;
4235
			if (empty($productstatic->ref)) {
4236
				$line->fetch_product();
4237
				$productstatic = $line->product;
4238
			}
4239
4240
			$this->tpl['label'].= $productstatic->getNomUrl(1);
4241
			$this->tpl['label'].= ' - '.(! empty($line->label)?$line->label:$line->product_label);
4242
			// Dates
4243
			if ($line->product_type == 1 && ($date_start || $date_end))
4244
			{
4245
				$this->tpl['label'].= get_date_range($date_start, $date_end);
4246
			}
4247
		}
4248
		else
4249
		{
4250
			$this->tpl['label'].= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
4251
			if (!empty($line->desc)) {
4252
				$this->tpl['label'].=$line->desc;
4253
			}else {
4254
				$this->tpl['label'].= ($line->label ? '&nbsp;'.$line->label : '');
4255
			}
4256
4257
			// Dates
4258
			if ($line->product_type == 1 && ($date_start || $date_end))
4259
			{
4260
				$this->tpl['label'].= get_date_range($date_start, $date_end);
4261
			}
4262
		}
4263
4264
		if (! empty($line->desc))
4265
		{
4266
			if ($line->desc == '(CREDIT_NOTE)')  // TODO Not sure this is used for source object
4267
			{
4268
				$discount=new DiscountAbsolute($this->db);
4269
				$discount->fetch($line->fk_remise_except);
4270
				$this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
4271
			}
4272
			elseif ($line->desc == '(DEPOSIT)')  // TODO Not sure this is used for source object
4273
			{
4274
				$discount=new DiscountAbsolute($this->db);
4275
				$discount->fetch($line->fk_remise_except);
4276
				$this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
4277
			}
4278
			elseif ($line->desc == '(EXCESS RECEIVED)')
4279
			{
4280
				$discount=new DiscountAbsolute($this->db);
4281
				$discount->fetch($line->fk_remise_except);
4282
				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
4283
			}
4284
			elseif ($line->desc == '(EXCESS PAID)')
4285
			{
4286
				$discount=new DiscountAbsolute($this->db);
4287
				$discount->fetch($line->fk_remise_except);
4288
				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
4289
			}
4290
			else
4291
			{
4292
				$this->tpl['description'] = dol_trunc($line->desc, 60);
4293
			}
4294
		}
4295
		else
4296
		{
4297
			$this->tpl['description'] = '&nbsp;';
4298
		}
4299
4300
        // VAT Rate
4301
        $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
4302
        $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
4303
        if (! empty($line->vat_src_code) && ! preg_match('/\(/', $this->tpl['vat_rate'])) $this->tpl['vat_rate'].=' ('.$line->vat_src_code.')';
4304
4305
		$this->tpl['price'] = price($line->subprice);
4306
		$this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
4307
		$this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
4308
		if ($conf->global->PRODUCT_USE_UNITS) $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
4309
		$this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
4310
4311
		// Is the line strike or not
4312
		$this->tpl['strike']=0;
4313
		if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) $this->tpl['strike']=1;
4314
4315
		// Output template part (modules that overwrite templates must declare this into descriptor)
4316
		// Use global variables + $dateSelector + $seller and $buyer
4317
		$dirtpls=array_merge($conf->modules_parts['tpl'], array('/core/tpl'));
4318
		foreach($dirtpls as $module => $reldir)
4319
		{
4320
			if (!empty($module))
4321
			{
4322
				$tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
4323
			}
4324
			else
4325
			{
4326
				$tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
4327
			}
4328
			if (empty($conf->file->strict_mode)) {
4329
				$res=@include $tpl;
4330
			} else {
4331
				$res=include $tpl; // for debug
4332
			}
4333
			if ($res) break;
4334
		}
4335
	}
4336
4337
4338
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4339
	/**
4340
	 *	Add resources to the current object : add entry into llx_element_resources
4341
	 *	Need $this->element & $this->id
4342
	 *
4343
	 *	@param		int		$resource_id		Resource id
4344
	 *	@param		string	$resource_type		'resource'
4345
	 *	@param		int		$busy				Busy or not
4346
	 *	@param		int		$mandatory			Mandatory or not
4347
	 *	@return		int							<=0 if KO, >0 if OK
4348
	 */
4349
	public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
4350
	{
4351
        // phpcs:enable
4352
		$this->db->begin();
4353
4354
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources (";
4355
		$sql.= "resource_id";
4356
		$sql.= ", resource_type";
4357
		$sql.= ", element_id";
4358
		$sql.= ", element_type";
4359
		$sql.= ", busy";
4360
		$sql.= ", mandatory";
4361
		$sql.= ") VALUES (";
4362
		$sql.= $resource_id;
4363
		$sql.= ", '".$this->db->escape($resource_type)."'";
4364
		$sql.= ", '".$this->db->escape($this->id)."'";
4365
		$sql.= ", '".$this->db->escape($this->element)."'";
4366
		$sql.= ", '".$this->db->escape($busy)."'";
4367
		$sql.= ", '".$this->db->escape($mandatory)."'";
4368
		$sql.= ")";
4369
4370
		dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
4371
		if ($this->db->query($sql))
4372
		{
4373
			$this->db->commit();
4374
			return 1;
4375
		}
4376
		else
4377
		{
4378
			$this->error=$this->db->lasterror();
4379
			$this->db->rollback();
4380
			return  0;
4381
		}
4382
	}
4383
4384
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4385
	/**
4386
	 *    Delete a link to resource line
4387
	 *
4388
	 *    @param	int		$rowid			Id of resource line to delete
4389
	 *    @param	int		$element		element name (for trigger) TODO: use $this->element into commonobject class
4390
	 *    @param	int		$notrigger		Disable all triggers
4391
	 *    @return   int						>0 if OK, <0 if KO
4392
	 */
4393
	public function delete_resource($rowid, $element, $notrigger = 0)
4394
	{
4395
        // phpcs:enable
4396
		global $user;
4397
4398
		$this->db->begin();
4399
4400
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources";
4401
		$sql.= " WHERE rowid=".$rowid;
4402
4403
		dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
4404
4405
		$resql=$this->db->query($sql);
4406
		if (! $resql)
4407
		{
4408
			$this->error=$this->db->lasterror();
4409
			$this->db->rollback();
4410
			return -1;
4411
		}
4412
		else
4413
		{
4414
			if (! $notrigger)
4415
			{
4416
				$result=$this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
4417
				if ($result < 0) { $this->db->rollback(); return -1; }
4418
			}
4419
			$this->db->commit();
4420
			return 1;
4421
		}
4422
	}
4423
4424
4425
	/**
4426
	 * Overwrite magic function to solve problem of cloning object that are kept as references
4427
	 *
4428
	 * @return void
4429
	 */
4430
	public function __clone()
4431
	{
4432
		// Force a copy of this->lines, otherwise it will point to same object.
4433
		if (isset($this->lines) && is_array($this->lines))
4434
		{
4435
			$nboflines=count($this->lines);
4436
			for($i=0; $i < $nboflines; $i++)
4437
			{
4438
				$this->lines[$i] = clone $this->lines[$i];
4439
			}
4440
		}
4441
	}
4442
4443
	/**
4444
	 * Common function for all objects extending CommonObject for generating documents
4445
	 *
4446
	 * @param 	string 		$modelspath 	Relative folder where generators are placed
4447
	 * @param 	string 		$modele 		Generator to use. Caller must set it to obj->modelpdf or GETPOST('modelpdf') for example.
4448
	 * @param 	Translate 	$outputlangs 	Output language to use
4449
	 * @param 	int 		$hidedetails 	1 to hide details. 0 by default
4450
	 * @param 	int 		$hidedesc 		1 to hide product description. 0 by default
4451
	 * @param 	int 		$hideref 		1 to hide product reference. 0 by default
4452
	 * @param   null|array  $moreparams     Array to provide more information
4453
	 * @return 	int 						>0 if OK, <0 if KO
4454
	 * @see	addFileIntoDatabaseIndex()
4455
	 */
4456
	protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
4457
	{
4458
		global $conf, $langs, $user;
4459
4460
		$srctemplatepath='';
4461
4462
		// Increase limit for PDF build
4463
		$err=error_reporting();
4464
		error_reporting(0);
4465
		@set_time_limit(120);
4466
		error_reporting($err);
4467
4468
		// If selected model is a filename template (then $modele="modelname" or "modelname:filename")
4469
		$tmp=explode(':', $modele, 2);
4470
		if (! empty($tmp[1]))
4471
		{
4472
			$modele=$tmp[0];
4473
			$srctemplatepath=$tmp[1];
4474
		}
4475
4476
		// Search template files
4477
		$file=''; $classname=''; $filefound=0;
4478
		$dirmodels=array('/');
4479
		if (is_array($conf->modules_parts['models'])) $dirmodels=array_merge($dirmodels, $conf->modules_parts['models']);
4480
		foreach($dirmodels as $reldir)
4481
		{
4482
			foreach(array('doc','pdf') as $prefix)
4483
			{
4484
				if (in_array(get_class($this), array('Adherent'))) $file = $prefix."_".$modele.".class.php";     // Member module use prefix_module.class.php
4485
				else $file = $prefix."_".$modele.".modules.php";
4486
4487
				// On verifie l'emplacement du modele
4488
				$file=dol_buildpath($reldir.$modelspath.$file, 0);
4489
				if (file_exists($file))
4490
				{
4491
					$filefound=1;
4492
					$classname=$prefix.'_'.$modele;
4493
					break;
4494
				}
4495
			}
4496
			if ($filefound) break;
4497
		}
4498
4499
		// If generator was found
4500
		if ($filefound)
4501
		{
4502
			global $db;  // Required to solve a conception default in commonstickergenerator.class.php making an include of code using $db
4503
4504
			require_once $file;
4505
4506
			$obj = new $classname($this->db);
4507
4508
			// If generator is ODT, we must have srctemplatepath defined, if not we set it.
4509
			if ($obj->type == 'odt' && empty($srctemplatepath))
4510
			{
4511
				$varfortemplatedir=$obj->scandir;
4512
				if ($varfortemplatedir && ! empty($conf->global->$varfortemplatedir))
4513
				{
4514
					$dirtoscan=$conf->global->$varfortemplatedir;
4515
4516
					$listoffiles=array();
4517
4518
					// Now we add first model found in directories scanned
4519
					$listofdir=explode(',', $dirtoscan);
4520
					foreach($listofdir as $key => $tmpdir)
4521
					{
4522
						$tmpdir=trim($tmpdir);
4523
						$tmpdir=preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
4524
						if (! $tmpdir) { unset($listofdir[$key]); continue; }
4525
						if (is_dir($tmpdir))
4526
						{
4527
							$tmpfiles=dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
4528
							if (count($tmpfiles)) $listoffiles=array_merge($listoffiles, $tmpfiles);
4529
						}
4530
					}
4531
4532
					if (count($listoffiles))
4533
					{
4534
						foreach($listoffiles as $record)
4535
						{
4536
							$srctemplatepath=$record['fullname'];
4537
							break;
4538
						}
4539
					}
4540
				}
4541
4542
				if (empty($srctemplatepath))
4543
				{
4544
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
4545
					return -1;
4546
				}
4547
			}
4548
4549
			if ($obj->type == 'odt' && ! empty($srctemplatepath))
4550
			{
4551
				if (! dol_is_file($srctemplatepath))
4552
				{
4553
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
4554
					return -1;
4555
				}
4556
			}
4557
4558
			// We save charset_output to restore it because write_file can change it if needed for
4559
			// output format that does not support UTF8.
4560
			$sav_charset_output=$outputlangs->charset_output;
4561
4562
			if (in_array(get_class($this), array('Adherent')))
4563
			{
4564
				$arrayofrecords = array();   // The write_file of templates of adherent class need this var
4565
				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams);
4566
			}
4567
			else
4568
			{
4569
				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
4570
			}
4571
			// After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
4572
4573
			if ($resultwritefile > 0)
4574
			{
4575
				$outputlangs->charset_output=$sav_charset_output;
4576
4577
				// We delete old preview
4578
				require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4579
				dol_delete_preview($this);
4580
4581
				// Index file in database
4582
				if (! empty($obj->result['fullpath']))
4583
				{
4584
					$destfull = $obj->result['fullpath'];
4585
					$upload_dir = dirname($destfull);
4586
					$destfile = basename($destfull);
4587
					$rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
4588
4589
					if (! preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir))     // If not a tmp dir
4590
					{
4591
						$filename = basename($destfile);
4592
						$rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
4593
						$rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
4594
4595
						include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
4596
						$ecmfile=new EcmFiles($this->db);
4597
						$result = $ecmfile->fetch(0, '', ($rel_dir?$rel_dir.'/':'').$filename);
4598
4599
						// Set the public "share" key
4600
						$setsharekey = false;
4601
						if ($this->element == 'propal')
4602
						{
4603
							$useonlinesignature = $conf->global->MAIN_FEATURES_LEVEL;	// Replace this with 1 when feature to make online signature is ok
4604
							if ($useonlinesignature) $setsharekey=true;
4605
							if (! empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4606
						}
4607
						if ($this->element == 'commande' && ! empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) {
4608
							$setsharekey=true;
4609
						}
4610
						if ($this->element == 'facture' && ! empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) {
4611
							$setsharekey=true;
4612
						}
4613
						if ($this->element == 'bank_account' && ! empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) {
4614
							$setsharekey=true;
4615
						}
4616
4617
						if ($setsharekey)
4618
						{
4619
							if (empty($ecmfile->share))	// Because object not found or share not set yet
4620
							{
4621
								require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
4622
								$ecmfile->share = getRandomPassword(true);
4623
							}
4624
						}
4625
4626
						if ($result > 0)
4627
						{
4628
							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4629
							$ecmfile->fullpath_orig = '';
4630
							$ecmfile->gen_or_uploaded = 'generated';
4631
							$ecmfile->description = '';    // indexed content
4632
							$ecmfile->keyword = '';        // keyword content
4633
							$result = $ecmfile->update($user);
4634
							if ($result < 0)
4635
							{
4636
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4637
							}
4638
						}
4639
						else
4640
						{
4641
							$ecmfile->entity = $conf->entity;
4642
							$ecmfile->filepath = $rel_dir;
4643
							$ecmfile->filename = $filename;
4644
							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4645
							$ecmfile->fullpath_orig = '';
4646
							$ecmfile->gen_or_uploaded = 'generated';
4647
							$ecmfile->description = '';    // indexed content
4648
							$ecmfile->keyword = '';        // keyword content
4649
							$ecmfile->src_object_type = $this->table_element;
4650
							$ecmfile->src_object_id   = $this->id;
4651
4652
							$result = $ecmfile->create($user);
4653
							if ($result < 0)
4654
							{
4655
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4656
							}
4657
						}
4658
4659
						/*$this->result['fullname']=$destfull;
4660
						$this->result['filepath']=$ecmfile->filepath;
4661
						$this->result['filename']=$ecmfile->filename;*/
4662
						//var_dump($obj->update_main_doc_field);exit;
4663
4664
						// Update the last_main_doc field into main object (if documenent generator has property ->update_main_doc_field set)
4665
						$update_main_doc_field=0;
4666
						if (! empty($obj->update_main_doc_field)) $update_main_doc_field=1;
4667
						if ($update_main_doc_field && ! empty($this->table_element))
4668
						{
4669
							$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath.'/'.$ecmfile->filename)."'";
4670
							$sql.= ' WHERE rowid = '.$this->id;
4671
4672
							$resql = $this->db->query($sql);
4673
							if (! $resql) dol_print_error($this->db);
4674
							else
4675
							{
4676
							    $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
4677
							}
4678
						}
4679
					}
4680
				}
4681
				else
4682
				{
4683
					dol_syslog('Method ->write_file was called on object '.get_class($obj).' and return a success but the return array ->result["fullpath"] was not set.', LOG_WARNING);
4684
				}
4685
4686
				// Success in building document. We build meta file.
4687
				dol_meta_create($this);
4688
4689
				return 1;
4690
			}
4691
			else
4692
			{
4693
				$outputlangs->charset_output=$sav_charset_output;
4694
				dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors);
4695
				return -1;
4696
			}
4697
		}
4698
		else
4699
		{
4700
			$this->error=$langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists", $file);
4701
			dol_print_error('', $this->error);
4702
			return -1;
4703
		}
4704
	}
4705
4706
	/**
4707
	 *  Build thumb
4708
	 *  @TODO Move this into files.lib.php
4709
	 *
4710
	 *  @param      string	$file           Path file in UTF8 to original file to create thumbs from.
4711
	 *	@return		void
4712
	 */
4713
	public function addThumbs($file)
4714
	{
4715
		global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality;
4716
4717
		require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';		// This define also $maxwidthsmall, $quality, ...
4718
4719
		$file_osencoded=dol_osencode($file);
4720
		if (file_exists($file_osencoded))
4721
		{
4722
			// Create small thumbs for company (Ratio is near 16/9)
4723
			// Used on logon for example
4724
			vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
4725
4726
			// Create mini thumbs for company (Ratio is near 16/9)
4727
			// Used on menu or for setup page for example
4728
			vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
4729
		}
4730
	}
4731
4732
4733
	/* Functions common to commonobject and commonobjectline */
4734
4735
	/* For default values */
4736
4737
	/**
4738
	 * Return the default value to use for a field when showing the create form of object.
4739
	 * Return values in this order:
4740
	 * 1) If parameter is available into POST, we return it first.
4741
	 * 2) If not but an alternate value was provided as parameter of function, we return it.
4742
	 * 3) If not but a constant $conf->global->OBJECTELEMENT_FIELDNAME is set, we return it (It is better to use the dedicated table).
4743
	 * 4) Return value found into database (TODO No yet implemented)
4744
	 *
4745
	 * @param   string              $fieldname          Name of field
4746
	 * @param   string              $alternatevalue     Alternate value to use
4747
	 * @return  string|string[]                         Default value (can be an array if the GETPOST return an array)
4748
	 **/
4749
	public function getDefaultCreateValueFor($fieldname, $alternatevalue = null)
4750
	{
4751
		global $conf, $_POST;
4752
4753
		// If param here has been posted, we use this value first.
4754
		if (isset($_POST[$fieldname])) return GETPOST($fieldname, 2);
4755
4756
		if (isset($alternatevalue)) return $alternatevalue;
4757
4758
		$newelement=$this->element;
4759
		if ($newelement == 'facture') $newelement='invoice';
4760
		if ($newelement == 'commande') $newelement='order';
4761
		if (empty($newelement))
4762
		{
4763
			dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
4764
			return '';
4765
		}
4766
4767
		$keyforfieldname=strtoupper($newelement.'_DEFAULT_'.$fieldname);
4768
		//var_dump($keyforfieldname);
4769
		if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname;
4770
4771
		// TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
4772
	}
4773
4774
4775
	/* For triggers */
4776
4777
4778
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4779
	/**
4780
	 * Call trigger based on this instance.
4781
	 * Some context information may also be provided into array property this->context.
4782
	 * NB:  Error from trigger are stacked in interface->errors
4783
	 * NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction.
4784
	 *
4785
	 * @param   string    $trigger_name   trigger's name to execute
4786
	 * @param   User      $user           Object user
4787
	 * @return  int                       Result of run_triggers
4788
	 */
4789
	public function call_trigger($trigger_name, $user)
4790
	{
4791
		// phpcs:enable
4792
		global $langs,$conf;
4793
4794
		include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
4795
		$interface=new Interfaces($this->db);
4796
		$result=$interface->run_triggers($trigger_name, $this, $user, $langs, $conf);
4797
4798
		if ($result < 0)
4799
		{
4800
			if (!empty($this->errors))
4801
			{
4802
				$this->errors=array_unique(array_merge($this->errors, $interface->errors));   // We use array_unique because when a trigger call another trigger on same object, this->errors is added twice.
4803
			}
4804
			else
4805
			{
4806
				$this->errors=$interface->errors;
4807
			}
4808
		}
4809
		return $result;
4810
	}
4811
4812
4813
	/* Functions for extrafields */
4814
4815
4816
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4817
	/**
4818
	 *  Function to get extra fields of an object into $this->array_options
4819
	 *  This method is in most cases called by method fetch of objects but you can call it separately.
4820
	 *
4821
	 *  @param	int		$rowid			Id of line. Use the id of object if not defined. Deprecated. Function must be called without parameters.
4822
	 *  @param  array	$optionsArray   Array resulting of call of extrafields->fetch_name_optionals_label(). Deprecated. Function must be called without parameters.
4823
	 *  @return	int						<0 if error, 0 if no values of extrafield to find nor found, 1 if an attribute is found and value loaded
4824
	 */
4825
	public function fetch_optionals($rowid = null, $optionsArray = null)
4826
	{
4827
		// phpcs:enable
4828
		if (empty($rowid)) $rowid=$this->id;
4829
4830
		// To avoid SQL errors. Probably not the better solution though
4831
		if (!$this->table_element) {
4832
			return 0;
4833
		}
4834
4835
		$this->array_options=array();
4836
4837
		if (! is_array($optionsArray))
4838
		{
4839
			// If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
4840
			// TODO Use of existing $extrafield is not yet ready (must mutualize code that use extrafields in form first)
4841
			// global $extrafields;
4842
			//if (! is_object($extrafields))
4843
			//{
4844
				require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4845
				$extrafields = new ExtraFields($this->db);
4846
			//}
4847
4848
			// Load array of extrafields for elementype = $this->table_element
4849
			if (empty($extrafields->attributes[$this->table_element]['loaded']))
4850
			{
4851
				$extrafields->fetch_name_optionals_label($this->table_element);
4852
			}
4853
			$optionsArray = (! empty($extrafields->attributes[$this->table_element]['label'])?$extrafields->attributes[$this->table_element]['label']:null);
4854
		}
4855
		else
4856
		{
4857
			global $extrafields;
4858
			dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
4859
		}
4860
4861
		$table_element = $this->table_element;
4862
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4863
4864
		// Request to get complementary values
4865
		if (is_array($optionsArray) && count($optionsArray) > 0)
4866
		{
4867
			$sql = "SELECT rowid";
4868
			foreach ($optionsArray as $name => $label)
4869
			{
4870
				if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate')
4871
				{
4872
					$sql.= ", ".$name;
4873
				}
4874
			}
4875
			$sql.= " FROM ".MAIN_DB_PREFIX.$table_element."_extrafields";
4876
			$sql.= " WHERE fk_object = ".$rowid;
4877
4878
			//dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG);		// Too verbose
4879
			$resql=$this->db->query($sql);
4880
			if ($resql)
4881
			{
4882
				$this->array_options = array();
4883
				$numrows=$this->db->num_rows($resql);
4884
				if ($numrows)
4885
				{
4886
					$tab = $this->db->fetch_array($resql);
4887
4888
					foreach ($tab as $key => $value)
4889
					{
4890
						// Test fetch_array ! is_int($key) because fetch_array result is a mix table with Key as alpha and Key as int (depend db engine)
4891
						if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && ! is_int($key))
4892
						{
4893
							// we can add this attribute to object
4894
							if (! empty($extrafields) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime')))
4895
							{
4896
								//var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
4897
								$this->array_options["options_".$key]=$this->db->jdate($value);
4898
							}
4899
							else
4900
							{
4901
								$this->array_options["options_".$key]=$value;
4902
							}
4903
4904
							//var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
4905
						}
4906
					}
4907
4908
					// If field is a computed field, value must become result of compute
4909
					foreach ($tab as $key => $value) {
4910
						if (! empty($extrafields) && !empty($extrafields->attributes[$this->table_element]['computed'][$key]))
4911
						{
4912
							$this->array_options["options_".$key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0);
4913
						}
4914
					}
4915
				}
4916
4917
				$this->db->free($resql);
4918
4919
				if ($numrows) return $numrows;
4920
				else return 0;
4921
			}
4922
			else
4923
			{
4924
				dol_print_error($this->db);
4925
				return -1;
4926
			}
4927
		}
4928
		return 0;
4929
	}
4930
4931
	/**
4932
	 *	Delete all extra fields values for the current object.
4933
	 *
4934
	 *  @return	int		<0 if KO, >0 if OK
4935
	 */
4936
	public function deleteExtraFields()
4937
	{
4938
		$this->db->begin();
4939
4940
		$table_element = $this->table_element;
4941
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4942
4943
		$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
4944
		dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
4945
		$resql=$this->db->query($sql_del);
4946
		if (! $resql)
4947
		{
4948
			$this->error=$this->db->lasterror();
4949
			$this->db->rollback();
4950
			return -1;
4951
		}
4952
		else
4953
		{
4954
			$this->db->commit();
4955
			return 1;
4956
		}
4957
	}
4958
4959
	/**
4960
	 *	Add/Update all extra fields values for the current object.
4961
	 *  Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
4962
	 *  This function delete record with all extrafields and insert them again from the array $this->array_options.
4963
	 *
4964
	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
4965
	 *  @param	User		$userused		Object user
4966
	 *  @return int 						-1=error, O=did nothing, 1=OK
4967
	 *  @see updateExtraField(), setValueFrom()
4968
	 */
4969
	public function insertExtraFields($trigger = '', $userused = null)
4970
	{
4971
		global $conf,$langs,$user;
4972
4973
		if (empty($userused)) $userused=$user;
4974
4975
		$error=0;
4976
4977
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
4978
4979
		if (! empty($this->array_options))
4980
		{
4981
			// Check parameters
4982
			$langs->load('admin');
4983
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4984
			$extrafields = new ExtraFields($this->db);
4985
			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
4986
4987
			//Eliminate copied source object extra_fields that do not exist in target object
4988
			$new_array_options=array();
4989
			foreach ($this->array_options as $key => $value) {
4990
				if (in_array(substr($key, 8), array_keys($target_extrafields)))	// We remove the 'options_' from $key for test
4991
					$new_array_options[$key] = $value;
4992
				elseif (in_array($key, array_keys($target_extrafields)))		// We test on $key that does not contains the 'options_' prefix
4993
					$new_array_options['options_'.$key] = $value;
4994
			}
4995
4996
			foreach($new_array_options as $key => $value)
4997
			{
4998
			   	$attributeKey      = substr($key, 8);   // Remove 'options_' prefix
4999
			   	$attributeType     = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
5000
			   	$attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
5001
			   	$attributeParam    = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
5002
			   	$attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
5003
5004
			   	if ($attributeRequired)
5005
			   	{
5006
			   		$mandatorypb=false;
5007
			   		if ($attributeType == 'link' && $this->array_options[$key] == '-1') $mandatorypb=true;
5008
			   		if ($this->array_options[$key] === '') $mandatorypb=true;
5009
			   		if ($mandatorypb)
5010
			   		{
5011
			   		    dol_syslog("Mandatory extra field ".$key." is empty");
5012
			   			$this->errors[]=$langs->trans('ErrorFieldRequired', $attributeLabel);
5013
			   			return -1;
5014
			   		}
5015
			   	}
5016
5017
				//dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5018
				//dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5019
5020
			   	switch ($attributeType)
5021
			   	{
5022
			   		case 'int':
5023
			  			if (!is_numeric($value) && $value!='')
5024
			   			{
5025
			   				$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5026
			   				return -1;
5027
			  			}
5028
			   			elseif ($value=='')
5029
			   			{
5030
			   				$new_array_options[$key] = null;
5031
			   			}
5032
			 			break;
5033
					case 'double':
5034
						$value = price2num($value);
5035
						if (!is_numeric($value) && $value!='')
5036
						{
5037
							dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5038
							$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5039
							return -1;
5040
						}
5041
						elseif ($value=='')
5042
						{
5043
							$new_array_options[$key] = null;
5044
						}
5045
						//dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5046
						$new_array_options[$key] = $value;
5047
						break;
5048
			 		/*case 'select':	// Not required, we chosed value='0' for undefined values
5049
             			if ($value=='-1')
5050
             			{
5051
             				$this->array_options[$key] = null;
5052
             			}
5053
             			break;*/
5054
			   		case 'password':
5055
			   			$algo='';
5056
			   			if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']))
5057
			   			{
5058
			   				// If there is an encryption choice, we use it to crypt data before insert
5059
			   				$tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
5060
			   				$algo=reset($tmparrays);
5061
			   				if ($algo != '')
5062
			   				{
5063
			   					//global $action;		// $action may be 'create', 'update', 'update_extras'...
5064
			   					//var_dump($action);
5065
			   					//var_dump($this->oldcopy);exit;
5066
			   					if (is_object($this->oldcopy))		// If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
5067
			   					{
5068
			   						//var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
5069
				   					if ($this->array_options[$key] == $this->oldcopy->array_options[$key])	// If old value crypted in database is same than submited new value, it means we don't change it, so we don't update.
5070
				   					{
5071
				   						$new_array_options[$key] = $this->array_options[$key];	// Value is kept
5072
				   					}
5073
									else
5074
									{
5075
										// var_dump($algo);
5076
										$newvalue = dol_hash($this->array_options[$key], $algo);
5077
										$new_array_options[$key] = $newvalue;
5078
									}
5079
			   					}
5080
			   					else
5081
			   					{
5082
			   						$new_array_options[$key] = $this->array_options[$key];	// Value is kept
5083
			   					}
5084
			   				}
5085
			   			}
5086
			   			else	// Common usage
5087
			   			{
5088
			   				$new_array_options[$key] = $this->array_options[$key];
5089
			   			}
5090
			   			break;
5091
			   		case 'price':
5092
						$new_array_options[$key] = price2num($this->array_options[$key]);
5093
						break;
5094
					case 'date':
5095
					case 'datetime':
5096
						// If data is a string instead of a timestamp, we convert it
5097
						if (! is_int($this->array_options[$key])) {
5098
							$this->array_options[$key] = strtotime($this->array_options[$key]);
5099
						}
5100
						$new_array_options[$key] = $this->db->idate($this->array_options[$key]);
5101
						break;
5102
		   			case 'link':
5103
						$param_list=array_keys($attributeParam['options']);
5104
						// 0 : ObjectName
5105
						// 1 : classPath
5106
						$InfoFieldList = explode(":", $param_list[0]);
5107
						dol_include_once($InfoFieldList[1]);
5108
						if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
5109
						{
5110
							if ($value == '-1')	// -1 is key for no defined in combo list of objects
5111
							{
5112
								$new_array_options[$key]='';
5113
							}
5114
							elseif ($value)
5115
							{
5116
								$object = new $InfoFieldList[0]($this->db);
5117
								if (is_numeric($value)) $res=$object->fetch($value);
5118
								else $res=$object->fetch('', $value);
5119
5120
								if ($res > 0) $new_array_options[$key]=$object->id;
5121
								else
5122
								{
5123
									$this->error="Id/Ref '".$value."' for object '".$object->element."' not found";
5124
									$this->db->rollback();
5125
									return -1;
5126
								}
5127
							}
5128
						}
5129
						else
5130
						{
5131
							dol_syslog('Error bad setup of extrafield', LOG_WARNING);
5132
						}
5133
						break;
5134
			   	}
5135
			}
5136
5137
			$this->db->begin();
5138
5139
			$table_element = $this->table_element;
5140
			if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
5141
5142
			dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
5143
5144
			$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
5145
			$this->db->query($sql_del);
5146
5147
			$sql = "INSERT INTO ".MAIN_DB_PREFIX.$table_element."_extrafields (fk_object";
5148
			foreach($new_array_options as $key => $value)
5149
			{
5150
				$attributeKey = substr($key, 8);   // Remove 'options_' prefix
5151
				// Add field of attribut
5152
				if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator
5153
					$sql.=",".$attributeKey;
5154
			}
5155
			// We must insert a default value for fields for other entities that are mandatory to avoid not null error
5156
			if (is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']))
5157
			{
5158
    			foreach($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as  $tmpkey => $tmpval)
5159
    			{
5160
    			    if (! isset($extrafields->attributes[$this->table_element]['type'][$tmpkey]))    // If field not already added previously
5161
    			    {
5162
    			        $sql.=",".$tmpkey;
5163
    			    }
5164
    			}
5165
			}
5166
			$sql .= ") VALUES (".$this->id;
5167
5168
			foreach($new_array_options as $key => $value)
5169
			{
5170
				$attributeKey = substr($key, 8);   // Remove 'options_' prefix
5171
				// Add field of attribute
5172
				if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator)
5173
				{
5174
					if ($new_array_options[$key] != '')
5175
					{
5176
						$sql.=",'".$this->db->escape($new_array_options[$key])."'";
5177
					}
5178
					else
5179
					{
5180
						$sql.=",null";
5181
					}
5182
				}
5183
			}
5184
			// We must insert a default value for fields for other entities that are mandatory to avoid not null error
5185
			if (is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']))
5186
			{
5187
			    foreach($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as  $tmpkey => $tmpval)
5188
    			{
5189
    			    if (! isset($extrafields->attributes[$this->table_element]['type'][$tmpkey]))    // If field not already added previously
5190
    			    {
5191
                        if (in_array($tmpval, array('int', 'double'))) $sql.=", 0";
5192
                        else $sql.=", ''";
5193
    			    }
5194
    			}
5195
			}
5196
5197
			$sql.=")";
5198
5199
			$resql = $this->db->query($sql);
5200
5201
			if (! $resql)
5202
			{
5203
				$this->error=$this->db->lasterror();
5204
				$error++;
5205
			}
5206
5207
			if (! $error && $trigger)
5208
			{
5209
				// Call trigger
5210
				$this->context=array('extrafieldaddupdate'=>1);
5211
				$result=$this->call_trigger($trigger, $userused);
5212
				if ($result < 0) $error++;
5213
				// End call trigger
5214
			}
5215
5216
			if ($error)
5217
			{
5218
				$this->db->rollback();
5219
				return -1;
5220
			}
5221
			else
5222
			{
5223
				$this->db->commit();
5224
				return 1;
5225
			}
5226
		}
5227
		else return 0;
5228
	}
5229
5230
	/**
5231
	 *	Update an extra field value for the current object.
5232
	 *  Data to describe values to update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
5233
	 *
5234
	 *  @param  string      $key    		Key of the extrafield (without starting 'options_')
5235
	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
5236
	 *  @param	User		$userused		Object user
5237
	 *  @return int                 		-1=error, O=did nothing, 1=OK
5238
	 *  @see setValueFrom(), insertExtraFields()
5239
	 */
5240
	public function updateExtraField($key, $trigger = null, $userused = null)
5241
	{
5242
		global $conf,$langs,$user;
5243
5244
		if (empty($userused)) $userused=$user;
5245
5246
		$error=0;
5247
5248
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
5249
5250
		if (! empty($this->array_options) && isset($this->array_options["options_".$key]))
5251
		{
5252
			// Check parameters
5253
			$langs->load('admin');
5254
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5255
			$extrafields = new ExtraFields($this->db);
5256
			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
5257
5258
			$value=$this->array_options["options_".$key];
5259
5260
			$attributeType     = $extrafields->attributes[$this->table_element]['type'][$key];
5261
			$attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$key];
5262
			$attributeParam    = $extrafields->attributes[$this->table_element]['param'][$key];
5263
			$attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
5264
5265
			//dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5266
			//dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5267
5268
			switch ($attributeType)
5269
			{
5270
				case 'int':
5271
					if (!is_numeric($value) && $value!='')
5272
					{
5273
						$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5274
						return -1;
5275
					}
5276
					elseif ($value=='')
5277
					{
5278
						$this->array_options["options_".$key] = null;
5279
					}
5280
					break;
5281
				case 'double':
5282
					$value = price2num($value);
5283
					if (!is_numeric($value) && $value!='')
5284
					{
5285
						dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5286
						$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5287
						return -1;
5288
					}
5289
					elseif ($value=='')
5290
					{
5291
						$this->array_options["options_".$key] = null;
5292
					}
5293
					//dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5294
					$this->array_options["options_".$key] = $value;
5295
					break;
5296
			 	/*case 'select':	// Not required, we chosed value='0' for undefined values
5297
             		if ($value=='-1')
5298
             		{
5299
             			$this->array_options[$key] = null;
5300
             		}
5301
             		break;*/
5302
				case 'price':
5303
					$this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
5304
					break;
5305
				case 'date':
5306
					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5307
					break;
5308
				case 'datetime':
5309
					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5310
					break;
5311
				case 'link':
5312
					$param_list=array_keys($attributeParam['options']);
5313
					// 0 : ObjectName
5314
					// 1 : classPath
5315
					$InfoFieldList = explode(":", $param_list[0]);
5316
					dol_include_once($InfoFieldList[1]);
5317
					if ($value)
5318
					{
5319
						$object = new $InfoFieldList[0]($this->db);
5320
						$object->fetch(0, $value);
5321
						$this->array_options["options_".$key]=$object->id;
5322
					}
5323
					break;
5324
			}
5325
5326
			$this->db->begin();
5327
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key."='".$this->db->escape($this->array_options["options_".$key])."'";
5328
			$sql .= " WHERE fk_object = ".$this->id;
5329
			$resql = $this->db->query($sql);
5330
			if (! $resql)
5331
			{
5332
				$error++;
5333
				$this->error=$this->db->lasterror();
5334
			}
5335
5336
			if (! $error && $trigger)
5337
			{
5338
				// Call trigger
5339
				$this->context=array('extrafieldupdate'=>1);
5340
				$result=$this->call_trigger($trigger, $userused);
5341
				if ($result < 0) $error++;
5342
				// End call trigger
5343
			}
5344
5345
			if ($error)
5346
			{
5347
				dol_syslog(get_class($this) . "::".__METHOD__ . $this->error, LOG_ERR);
5348
				$this->db->rollback();
5349
				return -1;
5350
			}
5351
			else
5352
			{
5353
				$this->db->commit();
5354
				return 1;
5355
			}
5356
		}
5357
		else return 0;
5358
	}
5359
5360
5361
	/**
5362
	 * Return HTML string to put an input field into a page
5363
	 * Code very similar with showInputField of extra fields
5364
	 *
5365
	 * @param  array   		$val	       Array of properties for field to show
5366
	 * @param  string  		$key           Key of attribute
5367
	 * @param  string  		$value         Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value)
5368
	 * @param  string  		$moreparam     To add more parameters on html input tag
5369
	 * @param  string  		$keysuffix     Prefix string to add into name and id of field (can be used to avoid duplicate names)
5370
	 * @param  string  		$keyprefix     Suffix string to add into name and id of field (can be used to avoid duplicate names)
5371
	 * @param  string|int	$morecss       Value for css to define style/length of field. May also be a numeric.
5372
	 * @return string
5373
	 */
5374
	public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0)
5375
	{
5376
		global $conf,$langs,$form;
5377
5378
		if (! is_object($form))
5379
		{
5380
			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5381
			$form=new Form($this->db);
5382
		}
5383
5384
		$val=$this->fields[$key];
5385
5386
		$out='';
5387
        $type='';
5388
        $param = array();
5389
        $param['options']=array();
5390
        $size =$this->fields[$key]['size'];
5391
        // Because we work on extrafields
5392
        if(preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)){
5393
            $param['options']=array($reg[1].':'.$reg[2]=>'N');
5394
            $type ='link';
5395
        } elseif(preg_match('/^link:(.*):(.*)/i', $val['type'], $reg)) {
5396
            $param['options']=array($reg[1].':'.$reg[2]=>'N');
5397
            $type ='link';
5398
        } elseif(preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
5399
            $param['options']=array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4]=>'N');
5400
            $type ='sellist';
5401
        } elseif(preg_match('/varchar\((\d+)\)/', $val['type'], $reg)) {
5402
            $param['options']=array();
5403
            $type ='varchar';
5404
            $size=$reg[1];
5405
        } elseif(preg_match('/varchar/', $val['type'])) {
5406
            $param['options']=array();
5407
            $type ='varchar';
5408
        } elseif(is_array($this->fields[$key]['arrayofkeyval'])) {
5409
            $param['options']=$this->fields[$key]['arrayofkeyval'];
5410
            $type ='select';
5411
        } else {
5412
            $param['options']=array();
5413
            $type =$this->fields[$key]['type'];
5414
        }
5415
5416
		$label=$this->fields[$key]['label'];
5417
		//$elementtype=$this->fields[$key]['elementtype'];	// Seems not used
5418
		$default=$this->fields[$key]['default'];
5419
		$computed=$this->fields[$key]['computed'];
5420
		$unique=$this->fields[$key]['unique'];
5421
		$required=$this->fields[$key]['required'];
5422
5423
		$langfile=$this->fields[$key]['langfile'];
5424
		$list=$this->fields[$key]['list'];
5425
		$hidden=(in_array(abs($this->fields[$key]['visible']), array(0,2)) ? 1 : 0);
5426
5427
		$objectid = $this->id;
5428
5429
5430
		if ($computed)
5431
		{
5432
			if (! preg_match('/^search_/', $keyprefix)) return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
5433
			else return '';
5434
		}
5435
5436
5437
		// Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
5438
		if (empty($morecss) && ! empty($val['css']))
5439
		{
5440
		    $morecss = $val['css'];
5441
		}
5442
		elseif (empty($morecss))
5443
		{
5444
			if ($type == 'date')
5445
			{
5446
				$morecss = 'minwidth100imp';
5447
			}
5448
			elseif ($type == 'datetime')
5449
			{
5450
				$morecss = 'minwidth200imp';
5451
			}
5452
			elseif (in_array($type, array('int','integer','price')) || preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type))
5453
			{
5454
				$morecss = 'maxwidth75';
5455
			} elseif ($type == 'url') {
5456
				$morecss='minwidth400';
5457
			}
5458
			elseif ($type == 'boolean')
5459
			{
5460
				$morecss='';
5461
			}
5462
			else
5463
			{
5464
				if (round($size) < 12)
5465
				{
5466
					$morecss = 'minwidth100';
5467
				}
5468
				elseif (round($size) <= 48)
5469
				{
5470
					$morecss = 'minwidth200';
5471
				}
5472
				else
5473
				{
5474
					$morecss = 'minwidth400';
5475
				}
5476
			}
5477
		}
5478
5479
		if (in_array($type, array('date','datetime')))
5480
		{
5481
			$tmp=explode(',', $size);
5482
			$newsize=$tmp[0];
5483
5484
			$showtime = in_array($type, array('datetime')) ? 1 : 0;
5485
5486
			// Do not show current date when field not required (see selectDate() method)
5487
			if (!$required && $value == '') $value = '-1';
5488
5489
			// TODO Must also support $moreparam
5490
			$out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
5491
		}
5492
		elseif (in_array($type, array('int','integer')))
5493
		{
5494
			$tmp=explode(',', $size);
5495
			$newsize=$tmp[0];
5496
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$newsize.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5497
		}
5498
		elseif (in_array($type, array('real')))
5499
		{
5500
		    $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5501
		}
5502
		elseif (preg_match('/varchar/', $type))
5503
		{
5504
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$size.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5505
		}
5506
		elseif (in_array($type, array('mail', 'phone', 'url')))
5507
		{
5508
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5509
		}
5510
		elseif ($type == 'text')
5511
		{
5512
			if (! preg_match('/search_/', $keyprefix))		// If keyprefix is search_ or search_options_, we must just use a simple text field
5513
			{
5514
				require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5515
				$doleditor=new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
5516
				$out=$doleditor->Create(1);
5517
			}
5518
			else
5519
			{
5520
				$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5521
			}
5522
		}
5523
		elseif ($type == 'html')
5524
		{
5525
			if (! preg_match('/search_/', $keyprefix))		// If keyprefix is search_ or search_options_, we must just use a simple text field
5526
			{
5527
				require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5528
				$doleditor=new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, ! empty($conf->fckeditor->enabled) && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
5529
				$out=$doleditor->Create(1);
5530
			}
5531
			else
5532
			{
5533
				$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5534
			}
5535
		}
5536
		elseif ($type == 'boolean')
5537
		{
5538
			$checked='';
5539
			if (!empty($value)) {
5540
				$checked=' checked value="1" ';
5541
			} else {
5542
				$checked=' value="1" ';
5543
			}
5544
			$out='<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam?$moreparam:'').'>';
5545
		}
5546
		elseif ($type == 'price')
5547
		{
5548
			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5549
				$value=price($value);
5550
			}
5551
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> '.$langs->getCurrencySymbol($conf->currency);
5552
		}
5553
		elseif (preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type))
5554
		{
5555
			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5556
				$value=price($value);
5557
			}
5558
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> ';
5559
		}
5560
		elseif ($type == 'select')
5561
		{
5562
			$out = '';
5563
			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5564
			{
5565
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5566
				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5567
			}
5568
5569
			$out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5570
                if((! isset($this->fields[$key]['default'])) ||($this->fields[$key]['notnull']!=1))$out.='<option value="0">&nbsp;</option>';
5571
			foreach ($param['options'] as $key => $val)
5572
			{
5573
				if ((string) $key == '') continue;
5574
				list($val, $parent) = explode('|', $val);
5575
				$out.='<option value="'.$key.'"';
5576
				$out.= (((string) $value == (string) $key)?' selected':'');
5577
				$out.= (!empty($parent)?' parent="'.$parent.'"':'');
5578
				$out.='>'.$val.'</option>';
5579
			}
5580
			$out.='</select>';
5581
		}
5582
		elseif ($type == 'sellist')
5583
		{
5584
			$out = '';
5585
			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5586
			{
5587
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5588
				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5589
			}
5590
5591
			$out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5592
			if (is_array($param['options']))
5593
			{
5594
				$param_list=array_keys($param['options']);
5595
				$InfoFieldList = explode(":", $param_list[0]);
5596
				$parentName='';
5597
				$parentField='';
5598
				// 0 : tableName
5599
				// 1 : label field name
5600
				// 2 : key fields name (if differ of rowid)
5601
				// 3 : key field parent (for dependent lists)
5602
				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5603
				$keyList=(empty($InfoFieldList[2])?'rowid':$InfoFieldList[2].' as rowid');
5604
5605
5606
				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4]))
5607
				{
5608
					if (strpos($InfoFieldList[4], 'extra.') !== false)
5609
					{
5610
						$keyList='main.'.$InfoFieldList[2].' as rowid';
5611
					} else {
5612
						$keyList=$InfoFieldList[2].' as rowid';
5613
					}
5614
				}
5615
				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3]))
5616
				{
5617
					list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
5618
					$keyList.= ', '.$parentField;
5619
				}
5620
5621
				$fields_label = explode('|', $InfoFieldList[1]);
5622
				if (is_array($fields_label))
5623
				{
5624
					$keyList .=', ';
5625
					$keyList .= implode(', ', $fields_label);
5626
				}
5627
5628
				$sqlwhere='';
5629
				$sql = 'SELECT '.$keyList;
5630
				$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
5631
				if (!empty($InfoFieldList[4]))
5632
				{
5633
					// can use SELECT request
5634
					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5635
						$InfoFieldList[4]=str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
5636
					}
5637
5638
					// current object id can be use into filter
5639
					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5640
						$InfoFieldList[4]=str_replace('$ID$', $objectid, $InfoFieldList[4]);
5641
					} else {
5642
						$InfoFieldList[4]=str_replace('$ID$', '0', $InfoFieldList[4]);
5643
					}
5644
					//We have to join on extrafield table
5645
					if (strpos($InfoFieldList[4], 'extra')!==false)
5646
					{
5647
						$sql.= ' as main, '.MAIN_DB_PREFIX .$InfoFieldList[0].'_extrafields as extra';
5648
						$sqlwhere.= ' WHERE extra.fk_object=main.'.$InfoFieldList[2]. ' AND '.$InfoFieldList[4];
5649
					}
5650
					else
5651
					{
5652
						$sqlwhere.= ' WHERE '.$InfoFieldList[4];
5653
					}
5654
				}
5655
				else
5656
				{
5657
					$sqlwhere.= ' WHERE 1=1';
5658
				}
5659
				// Some tables may have field, some other not. For the moment we disable it.
5660
				if (in_array($InfoFieldList[0], array('tablewithentity')))
5661
				{
5662
					$sqlwhere.= ' AND entity = '.$conf->entity;
5663
				}
5664
				$sql.=$sqlwhere;
5665
				//print $sql;
5666
5667
				$sql .= ' ORDER BY ' . implode(', ', $fields_label);
5668
5669
				dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
5670
				$resql = $this->db->query($sql);
5671
				if ($resql)
5672
				{
5673
					$out.='<option value="0">&nbsp;</option>';
5674
					$num = $this->db->num_rows($resql);
5675
					$i = 0;
5676
					while ($i < $num)
5677
					{
5678
						$labeltoshow='';
5679
						$obj = $this->db->fetch_object($resql);
5680
5681
						// Several field into label (eq table:code|libelle:rowid)
5682
						$notrans = false;
5683
						$fields_label = explode('|', $InfoFieldList[1]);
5684
						if (is_array($fields_label))
5685
						{
5686
							$notrans = true;
5687
							foreach ($fields_label as $field_toshow)
5688
							{
5689
								$labeltoshow.= $obj->$field_toshow.' ';
5690
							}
5691
						}
5692
						else
5693
						{
5694
							$labeltoshow=$obj->{$InfoFieldList[1]};
5695
						}
5696
						$labeltoshow=dol_trunc($labeltoshow, 45);
5697
5698
						if ($value == $obj->rowid)
5699
						{
5700
							foreach ($fields_label as $field_toshow)
5701
							{
5702
								$translabel=$langs->trans($obj->$field_toshow);
5703
								if ($translabel!=$obj->$field_toshow) {
5704
									$labeltoshow=dol_trunc($translabel, 18).' ';
5705
								}else {
5706
									$labeltoshow=dol_trunc($obj->$field_toshow, 18).' ';
5707
								}
5708
							}
5709
							$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5710
						}
5711
						else
5712
						{
5713
							if (! $notrans)
5714
							{
5715
								$translabel=$langs->trans($obj->{$InfoFieldList[1]});
5716
								if ($translabel!=$obj->{$InfoFieldList[1]}) {
5717
									$labeltoshow=dol_trunc($translabel, 18);
5718
								}
5719
								else {
5720
									$labeltoshow=dol_trunc($obj->{$InfoFieldList[1]}, 18);
5721
								}
5722
							}
5723
							if (empty($labeltoshow)) $labeltoshow='(not defined)';
5724
							if ($value==$obj->rowid)
5725
							{
5726
								$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5727
							}
5728
5729
							if (!empty($InfoFieldList[3]) && $parentField)
5730
							{
5731
								$parent = $parentName.':'.$obj->{$parentField};
5732
							}
5733
5734
							$out.='<option value="'.$obj->rowid.'"';
5735
							$out.= ($value==$obj->rowid?' selected':'');
5736
							$out.= (!empty($parent)?' parent="'.$parent.'"':'');
5737
							$out.='>'.$labeltoshow.'</option>';
5738
						}
5739
5740
						$i++;
5741
					}
5742
					$this->db->free($resql);
5743
				}
5744
				else {
5745
					print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
5746
				}
5747
			}
5748
			$out.='</select>';
5749
		}
5750
		elseif ($type == 'checkbox')
5751
		{
5752
			$value_arr=explode(',', $value);
5753
			$out=$form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options'])?null:$param['options']), $value_arr, '', 0, '', 0, '100%');
5754
		}
5755
		elseif ($type == 'radio')
5756
		{
5757
			$out='';
5758
			foreach ($param['options'] as $keyopt => $val)
5759
			{
5760
				$out.='<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'');
5761
				$out.=' value="'.$keyopt.'"';
5762
				$out.=' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
5763
				$out.= ($value==$keyopt?'checked':'');
5764
				$out.='/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$val.'</label><br>';
5765
			}
5766
		}
5767
		elseif ($type == 'chkbxlst')
5768
		{
5769
			if (is_array($value)) {
5770
				$value_arr = $value;
5771
			}
5772
			else {
5773
				$value_arr = explode(',', $value);
5774
			}
5775
5776
			if (is_array($param['options'])) {
5777
				$param_list = array_keys($param['options']);
5778
				$InfoFieldList = explode(":", $param_list[0]);
5779
				$parentName='';
5780
				$parentField='';
5781
				// 0 : tableName
5782
				// 1 : label field name
5783
				// 2 : key fields name (if differ of rowid)
5784
				// 3 : key field parent (for dependent lists)
5785
				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5786
				$keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
5787
5788
				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3])) {
5789
					list ( $parentName, $parentField ) = explode('|', $InfoFieldList[3]);
5790
					$keyList .= ', ' . $parentField;
5791
				}
5792
				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4])) {
5793
					if (strpos($InfoFieldList[4], 'extra.') !== false) {
5794
						$keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
5795
					} else {
5796
						$keyList = $InfoFieldList[2] . ' as rowid';
5797
					}
5798
				}
5799
5800
				$fields_label = explode('|', $InfoFieldList[1]);
5801
				if (is_array($fields_label)) {
5802
					$keyList .= ', ';
5803
					$keyList .= implode(', ', $fields_label);
5804
				}
5805
5806
				$sqlwhere = '';
5807
				$sql = 'SELECT ' . $keyList;
5808
				$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
5809
				if (! empty($InfoFieldList[4])) {
5810
5811
					// can use SELECT request
5812
					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5813
						$InfoFieldList[4]=str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
5814
					}
5815
5816
					// current object id can be use into filter
5817
					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5818
						$InfoFieldList[4]=str_replace('$ID$', $objectid, $InfoFieldList[4]);
5819
					} else {
5820
						$InfoFieldList[4]=str_replace('$ID$', '0', $InfoFieldList[4]);
5821
					}
5822
5823
					// We have to join on extrafield table
5824
					if (strpos($InfoFieldList[4], 'extra') !== false) {
5825
						$sql .= ' as main, ' . MAIN_DB_PREFIX . $InfoFieldList[0] . '_extrafields as extra';
5826
						$sqlwhere .= ' WHERE extra.fk_object=main.' . $InfoFieldList[2] . ' AND ' . $InfoFieldList[4];
5827
					} else {
5828
						$sqlwhere .= ' WHERE ' . $InfoFieldList[4];
5829
					}
5830
				} else {
5831
					$sqlwhere .= ' WHERE 1=1';
5832
				}
5833
				// Some tables may have field, some other not. For the moment we disable it.
5834
				if (in_array($InfoFieldList[0], array ('tablewithentity')))
5835
				{
5836
					$sqlwhere .= ' AND entity = ' . $conf->entity;
5837
				}
5838
				// $sql.=preg_replace('/^ AND /','',$sqlwhere);
5839
				// print $sql;
5840
5841
				$sql .= $sqlwhere;
5842
				dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
5843
				$resql = $this->db->query($sql);
5844
				if ($resql) {
5845
					$num = $this->db->num_rows($resql);
5846
					$i = 0;
5847
5848
					$data=array();
5849
5850
					while ( $i < $num ) {
5851
						$labeltoshow = '';
5852
						$obj = $this->db->fetch_object($resql);
5853
5854
						$notrans = false;
5855
						// Several field into label (eq table:code|libelle:rowid)
5856
						$fields_label = explode('|', $InfoFieldList[1]);
5857
						if (is_array($fields_label)) {
5858
							$notrans = true;
5859
							foreach ($fields_label as $field_toshow) {
5860
								$labeltoshow .= $obj->$field_toshow . ' ';
5861
							}
5862
						} else {
5863
							$labeltoshow = $obj->{$InfoFieldList[1]};
5864
						}
5865
						$labeltoshow = dol_trunc($labeltoshow, 45);
5866
5867
						if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5868
							foreach ($fields_label as $field_toshow) {
5869
								$translabel = $langs->trans($obj->$field_toshow);
5870
								if ($translabel != $obj->$field_toshow) {
5871
									$labeltoshow = dol_trunc($translabel, 18) . ' ';
5872
								} else {
5873
									$labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
5874
								}
5875
							}
5876
5877
							$data[$obj->rowid]=$labeltoshow;
5878
						} else {
5879
							if (! $notrans) {
5880
								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
5881
								if ($translabel != $obj->{$InfoFieldList[1]}) {
5882
									$labeltoshow = dol_trunc($translabel, 18);
5883
								} else {
5884
									$labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
5885
								}
5886
							}
5887
							if (empty($labeltoshow))
5888
								$labeltoshow = '(not defined)';
5889
5890
								if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5891
									$data[$obj->rowid]=$labeltoshow;
5892
								}
5893
5894
								if (! empty($InfoFieldList[3]) && $parentField) {
5895
									$parent = $parentName . ':' . $obj->{$parentField};
5896
								}
5897
5898
								$data[$obj->rowid]=$labeltoshow;
5899
						}
5900
5901
						$i ++;
5902
					}
5903
					$this->db->free($resql);
5904
5905
					$out=$form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
5906
				} else {
5907
					print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
5908
				}
5909
			}
5910
		}
5911
		elseif ($type == 'link')
5912
		{
5913
			$param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
5914
			$showempty=(($required && $default != '')?0:1);
5915
			$out=$form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty);
5916
			if ($conf->global->MAIN_FEATURES_LEVEL >= 2)
5917
			{
5918
            			list($class,$classfile)=explode(':', $param_list[0]);
5919
            			if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) $url_path=dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
5920
            			else $url_path=dol_buildpath(dirname(dirname($classfile)).'/'.$class.'_card.php', 1);
5921
            			$out.='<a class="butActionNew" href="'.$url_path.'?action=create&backtopage='.$_SERVER['PHP_SELF'].'"><span class="fa fa-plus-circle valignmiddle"></span></a>';
5922
            			// TODO Add Javascript code to add input fields contents to new elements urls
5923
			}
5924
		}
5925
		elseif ($type == 'password')
5926
		{
5927
			// If prefix is 'search_', field is used as a filter, we use a common text field.
5928
			$out='<input type="'.($keyprefix=='search_'?'text':'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'>';
5929
		}
5930
		elseif ($type == 'array')
5931
		{
5932
			$newval = $val;
5933
			$newval['type'] = 'varchar(256)';
5934
5935
			$out='';
5936
5937
			$inputs = array();
5938
			if(! empty($value)) {
5939
				foreach($value as $option) {
0 ignored issues
show
Bug introduced by
The expression $value of type string is not traversable.
Loading history...
5940
					$out.= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5941
					$out.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
5942
				}
5943
			}
5944
5945
			$out.= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
5946
5947
			$newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5948
			$newInput.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
5949
5950
			if(! empty($conf->use_javascript_ajax)) {
5951
				$out.= '
5952
					<script>
5953
					$(document).ready(function() {
5954
						$("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
5955
							$("'.dol_escape_js($newInput).'").insertBefore(this);
5956
						});
5957
5958
						$(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
5959
							$(this).parent().remove();
5960
						});
5961
					});
5962
					</script>';
5963
			}
5964
		}
5965
		if (!empty($hidden)) {
5966
			$out='<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
5967
		}
5968
		/* Add comments
5969
		 if ($type == 'date') $out.=' (YYYY-MM-DD)';
5970
		 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
5971
		 */
5972
		return $out;
5973
	}
5974
5975
	/**
5976
	 * Return HTML string to show a field into a page
5977
	 * Code very similar with showOutputField of extra fields
5978
	 *
5979
	 * @param  array   $val		       Array of properties of field to show
5980
	 * @param  string  $key            Key of attribute
5981
	 * @param  string  $value          Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value)
5982
	 * @param  string  $moreparam      To add more parametes on html input tag
5983
	 * @param  string  $keysuffix      Prefix string to add into name and id of field (can be used to avoid duplicate names)
5984
	 * @param  string  $keyprefix      Suffix string to add into name and id of field (can be used to avoid duplicate names)
5985
	 * @param  mixed   $morecss        Value for css to define size. May also be a numeric.
5986
	 * @return string
5987
	 */
5988
	public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
5989
	{
5990
		global $conf,$langs,$form;
5991
5992
		if (! is_object($form))
5993
		{
5994
			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5995
			$form=new Form($this->db);
5996
		}
5997
5998
		$objectid = $this->id;
5999
		$label = $val['label'];
6000
		$type  = $val['type'];
6001
		$size  = $val['css'];
6002
6003
		// Convert var to be able to share same code than showOutputField of extrafields
6004
		if (preg_match('/varchar\((\d+)\)/', $type, $reg))
6005
		{
6006
			$type = 'varchar';		// convert varchar(xx) int varchar
6007
			$size = $reg[1];
6008
		}
6009
		elseif (preg_match('/varchar/', $type)) $type = 'varchar';		// convert varchar(xx) int varchar
6010
		if (is_array($val['arrayofkeyval'])) $type='select';
6011
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) $type='link';
6012
6013
		$default=$val['default'];
6014
		$computed=$val['computed'];
6015
		$unique=$val['unique'];
6016
		$required=$val['required'];
6017
		$param=$val['param'];
6018
		if (is_array($val['arrayofkeyval'])) $param['options'] = $val['arrayofkeyval'];
6019
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg))
6020
		{
6021
			$type='link';
6022
			$param['options']=array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
6023
		}
6024
		$langfile=$val['langfile'];
6025
		$list=$val['list'];
6026
		$help=$val['help'];
6027
		$hidden=(($val['visible'] == 0) ? 1 : 0);			// If zero, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller)
6028
6029
		if ($hidden) return '';
6030
6031
		// If field is a computed field, value must become result of compute
6032
		if ($computed)
6033
		{
6034
			// Make the eval of compute string
6035
			//var_dump($computed);
6036
			$value = dol_eval($computed, 1, 0);
6037
		}
6038
6039
		if (empty($morecss))
6040
		{
6041
			if ($type == 'date')
6042
			{
6043
				$morecss = 'minwidth100imp';
6044
			}
6045
			elseif ($type == 'datetime')
6046
			{
6047
				$morecss = 'minwidth200imp';
6048
			}
6049
			elseif (in_array($type, array('int','double','price')))
6050
			{
6051
				$morecss = 'maxwidth75';
6052
			}
6053
			elseif ($type == 'url')
6054
			{
6055
				$morecss='minwidth400';
6056
			}
6057
			elseif ($type == 'boolean')
6058
			{
6059
				$morecss='';
6060
			}
6061
			else
6062
			{
6063
				if (round($size) < 12)
6064
				{
6065
					$morecss = 'minwidth100';
6066
				}
6067
				elseif (round($size) <= 48)
6068
				{
6069
					$morecss = 'minwidth200';
6070
				}
6071
				else
6072
				{
6073
					$morecss = 'minwidth400';
6074
				}
6075
			}
6076
		}
6077
6078
		// Format output value differently according to properties of field
6079
		if ($key == 'ref' && method_exists($this, 'getNomUrl')) $value=$this->getNomUrl(1, '', 0, '', 1);
6080
		elseif ($key == 'status' && method_exists($this, 'getLibStatut')) $value=$this->getLibStatut(3);
6081
		elseif ($type == 'date')
6082
		{
6083
			if(! empty($value)) {
6084
				$value=dol_print_date($value, 'day');
6085
			} else {
6086
				$value='';
6087
			}
6088
		}
6089
		elseif ($type == 'datetime')
6090
		{
6091
			if(! empty($value)) {
6092
				$value=dol_print_date($value, 'dayhour');
6093
			} else {
6094
				$value='';
6095
			}
6096
		}
6097
		elseif ($type == 'double')
6098
		{
6099
			if (!empty($value)) {
6100
				$value=price($value);
6101
			}
6102
		}
6103
		elseif ($type == 'real')
6104
		{
6105
		    if (!empty($value)) {
6106
		        $value=price($value);
6107
		    }
6108
		}
6109
		elseif ($type == 'boolean')
6110
		{
6111
			$checked='';
6112
			if (!empty($value)) {
6113
				$checked=' checked ';
6114
			}
6115
			$value='<input type="checkbox" '.$checked.' '.($moreparam?$moreparam:'').' readonly disabled>';
6116
		}
6117
		elseif ($type == 'mail')
6118
		{
6119
			$value=dol_print_email($value, 0, 0, 0, 64, 1, 1);
6120
		}
6121
		elseif ($type == 'url')
6122
		{
6123
			$value=dol_print_url($value, '_blank', 32, 1);
6124
		}
6125
		elseif ($type == 'phone')
6126
		{
6127
			$value=dol_print_phone($value, '', 0, 0, '', '&nbsp;', 1);
6128
		}
6129
		elseif ($type == 'price')
6130
		{
6131
			$value=price($value, 0, $langs, 0, 0, -1, $conf->currency);
6132
		}
6133
		elseif ($type == 'select')
6134
		{
6135
			$value=$param['options'][$value];
6136
		}
6137
		elseif ($type == 'sellist')
6138
		{
6139
			$param_list=array_keys($param['options']);
6140
			$InfoFieldList = explode(":", $param_list[0]);
6141
6142
			$selectkey="rowid";
6143
			$keyList='rowid';
6144
6145
			if (count($InfoFieldList)>=3)
6146
			{
6147
				$selectkey = $InfoFieldList[2];
6148
				$keyList=$InfoFieldList[2].' as rowid';
6149
			}
6150
6151
			$fields_label = explode('|', $InfoFieldList[1]);
6152
			if(is_array($fields_label)) {
6153
				$keyList .=', ';
6154
				$keyList .= implode(', ', $fields_label);
6155
			}
6156
6157
			$sql = 'SELECT '.$keyList;
6158
			$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
6159
			if (strpos($InfoFieldList[4], 'extra')!==false)
6160
			{
6161
				$sql.= ' as main';
6162
			}
6163
			if ($selectkey=='rowid' && empty($value)) {
6164
				$sql.= " WHERE ".$selectkey."=0";
6165
			} elseif ($selectkey=='rowid') {
6166
				$sql.= " WHERE ".$selectkey."=".$this->db->escape($value);
6167
			}else {
6168
				$sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6169
			}
6170
6171
			//$sql.= ' AND entity = '.$conf->entity;
6172
6173
			dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
6174
			$resql = $this->db->query($sql);
6175
			if ($resql)
6176
			{
6177
				$value='';	// value was used, so now we reste it to use it to build final output
6178
6179
				$obj = $this->db->fetch_object($resql);
6180
6181
				// Several field into label (eq table:code|libelle:rowid)
6182
				$fields_label = explode('|', $InfoFieldList[1]);
6183
6184
				if(is_array($fields_label) && count($fields_label)>1)
6185
				{
6186
					foreach ($fields_label as $field_toshow)
6187
					{
6188
						$translabel='';
6189
						if (!empty($obj->$field_toshow)) {
6190
							$translabel=$langs->trans($obj->$field_toshow);
6191
						}
6192
						if ($translabel!=$field_toshow) {
6193
							$value.=dol_trunc($translabel, 18).' ';
6194
						}else {
6195
							$value.=$obj->$field_toshow.' ';
6196
						}
6197
					}
6198
				}
6199
				else
6200
				{
6201
					$translabel='';
6202
					if (!empty($obj->{$InfoFieldList[1]})) {
6203
						$translabel=$langs->trans($obj->{$InfoFieldList[1]});
6204
					}
6205
					if ($translabel!=$obj->{$InfoFieldList[1]}) {
6206
						$value=dol_trunc($translabel, 18);
6207
					}else {
6208
						$value=$obj->{$InfoFieldList[1]};
6209
					}
6210
				}
6211
			}
6212
			else dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
6213
		}
6214
		elseif ($type == 'radio')
6215
		{
6216
			$value=$param['options'][$value];
6217
		}
6218
		elseif ($type == 'checkbox')
6219
		{
6220
			$value_arr=explode(',', $value);
6221
			$value='';
6222
			if (is_array($value_arr) && count($value_arr)>0)
6223
			{
6224
				foreach ($value_arr as $keyval=>$valueval) {
6225
					$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$param['options'][$valueval].'</li>';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$toprint was never initialized. Although not strictly required by PHP, it is generally a good practice to add $toprint = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
6226
				}
6227
				$value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
0 ignored issues
show
Bug introduced by
The variable $toprint does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
6228
			}
6229
		}
6230
		elseif ($type == 'chkbxlst')
6231
		{
6232
			$value_arr = explode(',', $value);
6233
6234
			$param_list = array_keys($param['options']);
6235
			$InfoFieldList = explode(":", $param_list[0]);
6236
6237
			$selectkey = "rowid";
6238
			$keyList = 'rowid';
6239
6240
			if (count($InfoFieldList) >= 3) {
6241
				$selectkey = $InfoFieldList[2];
6242
				$keyList = $InfoFieldList[2] . ' as rowid';
6243
			}
6244
6245
			$fields_label = explode('|', $InfoFieldList[1]);
6246
			if (is_array($fields_label)) {
6247
				$keyList .= ', ';
6248
				$keyList .= implode(', ', $fields_label);
6249
			}
6250
6251
			$sql = 'SELECT ' . $keyList;
6252
			$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
6253
			if (strpos($InfoFieldList[4], 'extra') !== false) {
6254
				$sql .= ' as main';
6255
			}
6256
			// $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6257
			// $sql.= ' AND entity = '.$conf->entity;
6258
6259
			dol_syslog(get_class($this) . ':showOutputField:$type=chkbxlst', LOG_DEBUG);
6260
			$resql = $this->db->query($sql);
6261
			if ($resql) {
6262
				$value = ''; // value was used, so now we reste it to use it to build final output
6263
				$toprint=array();
6264
				while ( $obj = $this->db->fetch_object($resql) ) {
6265
6266
					// Several field into label (eq table:code|libelle:rowid)
6267
					$fields_label = explode('|', $InfoFieldList[1]);
6268
					if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
6269
						if (is_array($fields_label) && count($fields_label) > 1) {
6270
							foreach ($fields_label as $field_toshow) {
6271
								$translabel = '';
6272
								if (! empty($obj->$field_toshow)) {
6273
									$translabel = $langs->trans($obj->$field_toshow);
6274
								}
6275
								if ($translabel != $field_toshow) {
6276
									$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6277
								} else {
6278
									$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->$field_toshow.'</li>';
6279
								}
6280
							}
6281
						} else {
6282
							$translabel = '';
6283
							if (! empty($obj->{$InfoFieldList[1]})) {
6284
								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
6285
							}
6286
							if ($translabel != $obj->{$InfoFieldList[1]}) {
6287
								$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6288
							} else {
6289
								$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->{$InfoFieldList[1]}.'</li>';
6290
							}
6291
						}
6292
					}
6293
				}
6294
				$value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6295
			} else {
6296
				dol_syslog(get_class($this) . '::showOutputField error ' . $this->db->lasterror(), LOG_WARNING);
6297
			}
6298
		}
6299
		elseif ($type == 'link')
6300
		{
6301
			$out='';
6302
6303
			// only if something to display (perf)
6304
			if ($value)
6305
			{
6306
				$param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
6307
6308
				$InfoFieldList = explode(":", $param_list[0]);
6309
				$classname=$InfoFieldList[0];
6310
				$classpath=$InfoFieldList[1];
6311
				$getnomurlparam=(empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]);
6312
				if (! empty($classpath))
6313
				{
6314
					dol_include_once($InfoFieldList[1]);
6315
					if ($classname && class_exists($classname))
6316
					{
6317
						$object = new $classname($this->db);
6318
						$object->fetch($value);
6319
						$value=$object->getNomUrl($getnomurlparam);
6320
					}
6321
				}
6322
				else
6323
				{
6324
					dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6325
					return 'Error bad setup of extrafield';
6326
				}
6327
			}
6328
			else $value='';
6329
		}
6330
		elseif ($type == 'text' || $type == 'html')
6331
		{
6332
			$value=dol_htmlentitiesbr($value);
6333
		}
6334
		elseif ($type == 'password')
6335
		{
6336
			$value=preg_replace('/./i', '*', $value);
6337
		}
6338
		elseif ($type == 'array')
6339
		{
6340
			$value = implode('<br>', $value);
6341
		}
6342
6343
		//print $type.'-'.$size;
6344
		$out=$value;
6345
6346
		return $out;
6347
	}
6348
6349
6350
	/**
6351
	 * Function to show lines of extrafields with output datas
6352
	 *
6353
	 * @param 	Extrafields $extrafields    Extrafield Object
6354
	 * @param 	string      $mode           Show output (view) or input (edit) for extrafield
6355
	 * @param 	array       $params         Optional parameters. Example: array('style'=>'class="oddeven"', 'colspan'=>$colspan)
6356
	 * @param 	string      $keysuffix      Suffix string to add after name and id of field (can be used to avoid duplicate names)
6357
	 * @param 	string      $keyprefix      Prefix string to add before name and id of field (can be used to avoid duplicate names)
6358
	 * @param	string		$onetrtd		All fields in same tr td
6359
	 * @return 	string
6360
	 */
6361
	public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = 0)
6362
	{
6363
		global $db, $conf, $langs, $action, $form;
6364
6365
		if (! is_object($form)) $form=new Form($db);
6366
6367
		$out = '';
6368
6369
		if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0)
6370
		{
6371
			$out .= "\n";
6372
			$out .= '<!-- showOptionalsInput --> ';
6373
			$out .= "\n";
6374
6375
			$e = 0;
6376
			foreach($extrafields->attributes[$this->table_element]['label'] as $key=>$label)
6377
			{
6378
				// Show only the key field in params
6379
				if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) continue;
6380
6381
				// @TODO Add test also on 'enabled' (different than 'list' that is 'visibility')
6382
				$enabled = 1;
6383
6384
				$visibility = 1;
6385
				if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key]))
6386
				{
6387
				    $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1);
6388
				}
6389
6390
				$perms = 1;
6391
				if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key]))
6392
				{
6393
					$perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1);
6394
				}
6395
6396
				if (($mode == 'create' || $mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3) continue;	// <> -1 and <> 1 and <> 3 = not visible on forms, only on list
6397
				elseif($mode == 'view' && empty($visibility)) continue;
6398
				if (empty($perms)) continue;
6399
6400
				// Load language if required
6401
				if (! empty($extrafields->attributes[$this->table_element]['langfile'][$key])) $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
6402
6403
				$colspan='3';
6404
				if (is_array($params) && count($params)>0) {
6405
					if (array_key_exists('colspan', $params)) {
6406
						$colspan=$params['colspan'];
6407
					}
6408
				}
6409
6410
				switch($mode) {
6411
					case "view":
6412
						$value=$this->array_options["options_".$key.$keysuffix];
6413
						break;
6414
					case "edit":
6415
						$getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, 'none');				// GETPOST can get value from GET, POST or setup of default values.
6416
						// GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
6417
						if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix))
6418
						{
6419
							if (is_array($getposttemp)) {
6420
								// $getposttemp is an array but following code expects a comma separated string
6421
								$value = implode(",", $getposttemp);
6422
							} else {
6423
								$value = $getposttemp;
6424
							}
6425
						} else {
6426
							$value = $this->array_options["options_" . $key];			// No GET, no POST, no default value, so we take value of object.
6427
						}
6428
						//var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
6429
						break;
6430
				}
6431
6432
				if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate')
6433
				{
6434
					$out .= $extrafields->showSeparator($key, $this);
6435
				}
6436
				else
6437
				{
6438
					$csstyle='';
6439
					$class=(!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
6440
					if (is_array($params) && count($params)>0) {
6441
						if (array_key_exists('style', $params)) {
6442
							$csstyle=$params['style'];
6443
						}
6444
					}
6445
6446
					// add html5 elements
6447
					$domData  = ' data-element="extrafield"';
6448
					$domData .= ' data-targetelement="'.$this->element.'"';
6449
					$domData .= ' data-targetid="'.$this->id.'"';
6450
6451
					$html_id = !empty($this->id) ? 'extrarow-'.$this->element.'_'.$key.'_'.$this->id : '';
6452
6453
					$out .= '<tr id="'.$html_id.'" '.$csstyle.' class="'.$class.$this->element.'_extras_'.$key.'" '.$domData.' >';
6454
6455
					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0)
6456
					{
6457
						if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) { $colspan='0'; }
6458
					}
6459
6460
					if ($action == 'selectlines') { $colspan++; }
6461
6462
					// Convert date into timestamp format (value in memory must be a timestamp)
6463
					if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime')))
6464
					{
6465
						$datenotinstring = $this->array_options['options_' . $key];
6466
						if (! is_numeric($this->array_options['options_' . $key]))	// For backward compatibility
6467
						{
6468
							$datenotinstring = $this->db->jdate($datenotinstring);
6469
						}
6470
						$value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?dol_mktime(GETPOST($keyprefix.'options_'.$key.$keysuffix."hour", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."min", 'int', 3), 0, GETPOST($keyprefix.'options_'.$key.$keysuffix."month", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."day", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."year", 'int', 3)):$datenotinstring;
6471
					}
6472
					// Convert float submited string into real php numeric (value in memory must be a php numeric)
6473
					if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price','double')))
6474
					{
6475
						$value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?price2num(GETPOST($keyprefix.'options_'.$key.$keysuffix, 'alpha', 3)):$this->array_options['options_'.$key];
6476
					}
6477
6478
					$labeltoshow = $langs->trans($label);
6479
6480
					$out .= '<td class="titlefield';
6481
					if (GETPOST('action', 'none') == 'create') $out.='create';
6482
					if ($mode != 'view' && ! empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= ' fieldrequired';
6483
					$out .= '">';
6484
					if (! empty($extrafields->attributes[$object->table_element]['help'][$key])) $out .= $form->textwithpicto($labeltoshow, $extrafields->attributes[$object->table_element]['help'][$key]);
0 ignored issues
show
Bug introduced by
The variable $object does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
6485
					else $out .= $labeltoshow;
6486
					$out .= '</td>';
6487
6488
					$html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
6489
					$out .='<td id="'.$html_id.'" class="'.$this->element.'_extras_'.$key.'" '.($colspan?' colspan="'.$colspan.'"':'').'>';
6490
6491
					switch($mode) {
6492
						case "view":
6493
							$out .= $extrafields->showOutputField($key, $value);
0 ignored issues
show
Bug introduced by
The variable $value does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
6494
							break;
6495
						case "edit":
6496
							$out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
6497
							break;
6498
					}
6499
6500
					$out .= '</td>';
6501
6502
					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>';
6503
					else $out .= '</tr>';
6504
					$e++;
6505
				}
6506
			}
6507
			$out .= "\n";
6508
			// Add code to manage list depending on others
6509
			if (! empty($conf->use_javascript_ajax)) {
6510
				$out .= '
6511
				<script>
6512
				    jQuery(document).ready(function() {
6513
				    	function showOptions(child_list, parent_list)
6514
				    	{
6515
				    		var val = $("select[name=\"options_"+parent_list+"\"]").val();
6516
				    		var parentVal = parent_list + ":" + val;
6517
							if(val > 0) {
6518
					    		$("select[name=\""+child_list+"\"] option[parent]").hide();
6519
					    		$("select[name=\""+child_list+"\"] option[parent=\""+parentVal+"\"]").show();
6520
							} else {
6521
								$("select[name=\""+child_list+"\"] option").show();
6522
							}
6523
				    	}
6524
						function setListDependencies() {
6525
					    	jQuery("select option[parent]").parent().each(function() {
6526
					    		var child_list = $(this).attr("name");
6527
								var parent = $(this).find("option[parent]:first").attr("parent");
6528
								var infos = parent.split(":");
6529
								var parent_list = infos[0];
6530
								$("select[name=\""+parent_list+"\"]").change(function() {
6531
									showOptions(child_list, parent_list);
6532
								});
6533
					    	});
6534
						}
6535
6536
						setListDependencies();
6537
				    });
6538
				</script>'."\n";
6539
				$out .= '<!-- /showOptionalsInput --> '."\n";
6540
			}
6541
		}
6542
		return $out;
6543
	}
6544
6545
6546
	/**
6547
	 * Returns the rights used for this class
6548
	 * @return stdClass
6549
	 */
6550
	public function getRights()
6551
	{
6552
		global $user;
6553
6554
		$element = $this->element;
6555
		if ($element == 'facturerec') $element='facture';
6556
6557
		return $user->rights->{$element};
6558
	}
6559
6560
	/**
6561
	 * Function used to replace a thirdparty id with another one.
6562
	 * This function is meant to be called from replaceThirdparty with the appropiate tables
6563
	 * Column name fk_soc MUST be used to identify thirdparties
6564
	 *
6565
	 * @param  DoliDB 	   $db 			  Database handler
6566
	 * @param  int 		   $origin_id     Old thirdparty id (the thirdparty to delete)
6567
	 * @param  int 		   $dest_id       New thirdparty id (the thirdparty that will received element of the other)
6568
	 * @param  string[]    $tables        Tables that need to be changed
6569
	 * @param  int         $ignoreerrors  Ignore errors. Return true even if errors. We need this when replacement can fails like for categories (categorie of old thirdparty may already exists on new one)
6570
	 * @return bool						  True if success, False if error
6571
	 */
6572
	public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
6573
	{
6574
		foreach ($tables as $table)
6575
		{
6576
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id;
6577
6578
			if (! $db->query($sql))
6579
			{
6580
				if ($ignoreerrors) return true;		// TODO Not enough. If there is A-B on kept thirdarty and B-C on old one, we must get A-B-C after merge. Not A-B.
6581
				//$this->errors = $db->lasterror();
6582
				return false;
6583
			}
6584
		}
6585
6586
		return true;
6587
	}
6588
6589
	/**
6590
	 * Get buy price to use for margin calculation. This function is called when buy price is unknown.
6591
	 *	 Set buy price = sell price if ForceBuyingPriceIfNull configured,
6592
	 *   else if calculation MARGIN_TYPE = 'costprice' and costprice is defined, use costprice as buyprice
6593
	 *	 else if calculation MARGIN_TYPE = 'pmp' and pmp is calculated, use pmp as buyprice
6594
	 *	 else set min buy price as buy price
6595
	 *
6596
	 * @param float		$unitPrice		 Product unit price
6597
	 * @param float		$discountPercent Line discount percent
6598
	 * @param int		$fk_product		 Product id
6599
	 * @return	float                    <0 if KO, buyprice if OK
6600
	 */
6601
	public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
6602
	{
6603
		global $conf;
6604
6605
		$buyPrice = 0;
6606
6607
		if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
6608
		{
6609
			$buyPrice = $unitPrice * (1 - $discountPercent / 100);
6610
		}
6611
		else
6612
		{
6613
			// Get cost price for margin calculation
6614
			if (! empty($fk_product))
6615
			{
6616
				if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice')
6617
				{
6618
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6619
					$product = new Product($this->db);
6620
					$result = $product->fetch($fk_product);
6621
					if ($result <= 0)
6622
					{
6623
						$this->errors[] = 'ErrorProductIdDoesNotExists';
6624
						return -1;
6625
					}
6626
					if ($product->cost_price > 0)
6627
					{
6628
						$buyPrice = $product->cost_price;
6629
					}
6630
					elseif ($product->pmp > 0)
6631
					{
6632
						$buyPrice = $product->pmp;
6633
					}
6634
				}
6635
				elseif (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')
6636
				{
6637
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6638
					$product = new Product($this->db);
6639
					$result = $product->fetch($fk_product);
6640
					if ($result <= 0)
6641
					{
6642
						$this->errors[] = 'ErrorProductIdDoesNotExists';
6643
						return -1;
6644
					}
6645
					if ($product->pmp > 0)
6646
					{
6647
						$buyPrice = $product->pmp;
6648
					}
6649
				}
6650
6651
				if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1','pmp','costprice')))
6652
				{
6653
					require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6654
					$productFournisseur = new ProductFournisseur($this->db);
6655
					if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0)
6656
					{
6657
						$buyPrice = $productFournisseur->fourn_unitprice;
6658
					}
6659
					elseif ($result < 0)
6660
					{
6661
						$this->errors[] = $productFournisseur->error;
6662
						return -2;
6663
					}
6664
				}
6665
			}
6666
		}
6667
		return $buyPrice;
6668
	}
6669
6670
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6671
	/**
6672
	 *  Show photos of an object (nbmax maximum), into several columns
6673
	 *
6674
	 *  @param		string	$modulepart		'product', 'ticket', ...
6675
	 *  @param      string	$sdir        	Directory to scan (full absolute path)
6676
	 *  @param      int		$size        	0=original size, 1='small' use thumbnail if possible
6677
	 *  @param      int		$nbmax       	Nombre maximum de photos (0=pas de max)
6678
	 *  @param      int		$nbbyrow     	Number of image per line or -1 to use div. Used only if size=1.
6679
	 * 	@param		int		$showfilename	1=Show filename
6680
	 * 	@param		int		$showaction		1=Show icon with action links (resize, delete)
6681
	 * 	@param		int		$maxHeight		Max height of original image when size='small' (so we can use original even if small requested). If 0, always use 'small' thumb image.
6682
	 * 	@param		int		$maxWidth		Max width of original image when size='small'
6683
	 *  @param      int     $nolink         Do not add a href link to view enlarged imaged into a new tab
6684
	 *  @param      int     $notitle        Do not add title tag on image
6685
	 *  @param		int		$usesharelink	Use the public shared link of image (if not available, the 'nophoto' image will be shown instead)
6686
	 *  @return     string					Html code to show photo. Number of photos shown is saved in this->nbphoto
6687
	 */
6688
	public function show_photos($modulepart, $sdir, $size = 0, $nbmax = 0, $nbbyrow = 5, $showfilename = 0, $showaction = 0, $maxHeight = 120, $maxWidth = 160, $nolink = 0, $notitle = 0, $usesharelink = 0)
6689
	{
6690
        // phpcs:enable
6691
		global $conf,$user,$langs;
6692
6693
		include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php';
6694
		include_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';
6695
6696
		$sortfield='position_name';
6697
		$sortorder='asc';
6698
6699
		$dir = $sdir . '/';
6700
		$pdir = '/';
6701
		if ($modulepart == 'ticket')
6702
		{
6703
			$dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6704
			$pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6705
		}
6706
		else
6707
		{
6708
			$dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6709
			$pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6710
		}
6711
6712
		// For backward compatibility
6713
		if ($modulepart == 'product' && ! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))
6714
		{
6715
			$dir = $sdir . '/'. get_exdir($this->id, 2, 0, 0, $this, $modulepart) . $this->id ."/photos/";
6716
			$pdir = '/' . get_exdir($this->id, 2, 0, 0, $this, $modulepart) . $this->id ."/photos/";
6717
		}
6718
6719
		// Defined relative dir to DOL_DATA_ROOT
6720
		$relativedir = '';
6721
		if ($dir)
6722
		{
6723
			$relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
6724
			$relativedir = preg_replace('/^[\\/]/', '', $relativedir);
6725
			$relativedir = preg_replace('/[\\/]$/', '', $relativedir);
6726
		}
6727
6728
		$dirthumb = $dir.'thumbs/';
6729
		$pdirthumb = $pdir.'thumbs/';
6730
6731
		$return ='<!-- Photo -->'."\n";
6732
		$nbphoto=0;
6733
6734
		$filearray=dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC), 1);
6735
6736
		/*if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))    // For backward compatiblity, we scan also old dirs
6737
		 {
6738
		 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
6739
		 $filearray=array_merge($filearray, $filearrayold);
6740
		 }*/
6741
6742
		completeFileArrayWithDatabaseInfo($filearray, $relativedir);
6743
6744
		if (count($filearray))
6745
		{
6746
			if ($sortfield && $sortorder)
6747
			{
6748
				$filearray=dol_sort_array($filearray, $sortfield, $sortorder);
6749
			}
6750
6751
			foreach($filearray as $key => $val)
6752
			{
6753
				$photo='';
6754
				$file = $val['name'];
6755
6756
				//if (! utf8_check($file)) $file=utf8_encode($file);	// To be sure file is stored in UTF8 in memory
6757
6758
				//if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
6759
				if (image_format_supported($file) >= 0)
6760
				{
6761
					$nbphoto++;
6762
					$photo = $file;
6763
					$viewfilename = $file;
6764
6765
					if ($size == 1 || $size == 'small') {   // Format vignette
6766
6767
						// Find name of thumb file
6768
						$photo_vignette=basename(getImageFileNameForSize($dir.$file, '_small'));
6769
						if (! dol_is_file($dirthumb.$photo_vignette)) $photo_vignette='';
6770
6771
						// Get filesize of original file
6772
						$imgarray=dol_getImageSize($dir.$photo);
6773
6774
						if ($nbbyrow > 0)
6775
						{
6776
							if ($nbphoto == 1) $return.= '<table class="valigntop center centpercent" style="border: 0; padding: 2; border-spacing: 2px; border-collapse: separate;">';
6777
6778
							if ($nbphoto % $nbbyrow == 1) $return.= '<tr class="center valignmiddle" style="border: 1px">';
6779
							$return.= '<td style="width: '.ceil(100/$nbbyrow).'%" class="photo">';
6780
						}
6781
						elseif ($nbbyrow < 0) $return .= '<div class="inline-block">';
6782
6783
						$return.= "\n";
6784
6785
						$relativefile=preg_replace('/^\//', '', $pdir.$photo);
6786
						if (empty($nolink))
6787
						{
6788
							$urladvanced=getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
6789
							if ($urladvanced) $return.='<a href="'.$urladvanced.'">';
6790
							else $return.= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank">';
6791
						}
6792
6793
						// Show image (width height=$maxHeight)
6794
						// Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
6795
						$alt=$langs->transnoentitiesnoconv('File').': '.$relativefile;
6796
						$alt.=' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
6797
						if ($notitle) $alt='';
6798
6799
						if ($usesharelink)
6800
						{
6801
							if ($val['share'])
6802
							{
6803
								if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6804
								{
6805
									$return.= '<!-- Show original file (thumb not yet available with shared links) -->';
6806
									$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6807
								}
6808
								else {
6809
									$return.= '<!-- Show original file -->';
6810
									$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6811
								}
6812
							}
6813
							else
6814
							{
6815
								$return.= '<!-- Show nophoto file (because file is not shared) -->';
6816
								$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
6817
							}
6818
						}
6819
						else
6820
						{
6821
							if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6822
							{
6823
								$return.= '<!-- Show thumb -->';
6824
								$return.= '<img class="photo photowithmargin"  height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdirthumb.$photo_vignette).'" title="'.dol_escape_htmltag($alt).'">';
6825
							}
6826
							else {
6827
								$return.= '<!-- Show original file -->';
6828
								$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" title="'.dol_escape_htmltag($alt).'">';
6829
							}
6830
						}
6831
6832
						if (empty($nolink)) $return.= '</a>';
6833
						$return.="\n";
6834
6835
						if ($showfilename) $return.= '<br>'.$viewfilename;
6836
						if ($showaction)
6837
						{
6838
							$return.= '<br>';
6839
							// On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
6840
							if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight))
6841
							{
6842
								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=addthumb&amp;file='.urlencode($pdir.$viewfilename).'">'.img_picto($langs->trans('GenerateThumb'), 'refresh').'&nbsp;&nbsp;</a>';
6843
							}
6844
							// Special cas for product
6845
							if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6846
							{
6847
								// Link to resize
6848
								$return.= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&amp;file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
6849
6850
								// Link to delete
6851
								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6852
								$return.= img_delete().'</a>';
6853
							}
6854
						}
6855
						$return.= "\n";
6856
6857
						if ($nbbyrow > 0)
6858
						{
6859
							$return.= '</td>';
6860
							if (($nbphoto % $nbbyrow) == 0) $return.= '</tr>';
6861
						}
6862
						elseif ($nbbyrow < 0) $return.='</div>';
6863
					}
6864
6865
					if (empty($size)) {     // Format origine
6866
						$return.= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
6867
6868
						if ($showfilename) $return.= '<br>'.$viewfilename;
6869
						if ($showaction)
6870
						{
6871
							// Special case for product
6872
							if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6873
							{
6874
								// Link to resize
6875
								$return.= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&amp;file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
6876
6877
								// Link to delete
6878
								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6879
								$return.= img_delete().'</a>';
6880
							}
6881
						}
6882
					}
6883
6884
					// On continue ou on arrete de boucler ?
6885
					if ($nbmax && $nbphoto >= $nbmax) break;
6886
				}
6887
			}
6888
6889
			if ($size==1 || $size=='small')
6890
			{
6891
				if ($nbbyrow > 0)
6892
				{
6893
					// Ferme tableau
6894
					while ($nbphoto % $nbbyrow)
6895
					{
6896
						$return.= '<td style="width: '.ceil(100/$nbbyrow).'%">&nbsp;</td>';
6897
						$nbphoto++;
6898
					}
6899
6900
					if ($nbphoto) $return.= '</table>';
6901
				}
6902
			}
6903
		}
6904
6905
		$this->nbphoto = $nbphoto;
6906
6907
		return $return;
6908
	}
6909
6910
6911
	/**
6912
	 * Function test if type is array
6913
	 *
6914
	 * @param   array   $info   content informations of field
6915
	 * @return                  bool
6916
	 */
6917
	protected function isArray($info)
6918
	{
6919
		if(is_array($info))
6920
		{
6921
			if(isset($info['type']) && $info['type']=='array') return true;
6922
			else return false;
6923
		}
6924
		else return false;
6925
	}
6926
6927
	/**
6928
	 * Function test if type is null
6929
	 *
6930
	 * @param   array   $info   content informations of field
6931
	 * @return                  bool
6932
	 */
6933
	protected function isNull($info)
6934
	{
6935
		if(is_array($info))
6936
		{
6937
			if(isset($info['type']) && $info['type']=='null') return true;
6938
			else return false;
6939
		}
6940
		else return false;
6941
	}
6942
6943
	/**
6944
	 * Function test if type is date
6945
	 *
6946
	 * @param   array   $info   content informations of field
6947
	 * @return                  bool
6948
	 */
6949
	public function isDate($info)
6950
	{
6951
		if(isset($info['type']) && ($info['type']=='date' || $info['type']=='datetime' || $info['type']=='timestamp')) return true;
6952
		else return false;
6953
	}
6954
6955
	/**
6956
	 * Function test if type is integer
6957
	 *
6958
	 * @param   array   $info   content informations of field
6959
	 * @return                  bool
6960
	 */
6961
	public function isInt($info)
6962
	{
6963
		if(is_array($info))
6964
		{
6965
			if(isset($info['type']) && ($info['type']=='int' || preg_match('/^integer/i', $info['type']) ) ) return true;
6966
			else return false;
6967
		}
6968
		else return false;
6969
	}
6970
6971
	/**
6972
	 * Function test if type is float
6973
	 *
6974
	 * @param   array   $info   content informations of field
6975
	 * @return                  bool
6976
	 */
6977
	public function isFloat($info)
6978
	{
6979
		if(is_array($info))
6980
		{
6981
			if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) return true;
6982
			else return false;
6983
		}
6984
		else return false;
6985
	}
6986
6987
	/**
6988
	 * Function test if type is text
6989
	 *
6990
	 * @param   array   $info   content informations of field
6991
	 * @return                  bool
6992
	 */
6993
	public function isText($info)
6994
	{
6995
		if(is_array($info))
6996
		{
6997
			if(isset($info['type']) && $info['type']=='text') return true;
6998
			else return false;
6999
		}
7000
		else return false;
7001
	}
7002
7003
	/**
7004
	 * Function test if is indexed
7005
	 *
7006
	 * @param   array   $info   content informations of field
7007
	 * @return                  bool
7008
	 */
7009
	protected function isIndex($info)
7010
	{
7011
		if(is_array($info))
7012
		{
7013
			if(isset($info['index']) && $info['index']==true) return true;
7014
			else return false;
7015
		}
7016
		else return false;
7017
	}
7018
7019
	/**
7020
	 * Function to prepare the values to insert.
7021
	 * Note $this->${field} are set by the page that make the createCommon or the updateCommon.
7022
	 *
7023
	 * @return array
7024
	 */
7025
	protected function setSaveQuery()
7026
	{
7027
		global $conf;
7028
7029
		$queryarray=array();
7030
		foreach ($this->fields as $field=>$info)	// Loop on definition of fields
7031
		{
7032
			// Depending on field type ('datetime', ...)
7033
			if($this->isDate($info))
7034
			{
7035
				if(empty($this->{$field}))
7036
				{
7037
					$queryarray[$field] = null;
7038
				}
7039
				else
7040
				{
7041
					$queryarray[$field] = $this->db->idate($this->{$field});
7042
				}
7043
			}
7044
			elseif($this->isArray($info))
7045
			{
7046
				if(! empty($this->{$field})) {
7047
					if(! is_array($this->{$field})) {
7048
						$this->{$field} = array($this->{$field});
7049
					}
7050
					$queryarray[$field] = serialize($this->{$field});
7051
				} else {
7052
					$queryarray[$field] = null;
7053
				}
7054
			}
7055
			elseif($this->isInt($info))
7056
			{
7057
				if ($field == 'entity' && is_null($this->{$field})) $queryarray[$field]=$conf->entity;
7058
				else
7059
				{
7060
					$queryarray[$field] = (int) price2num($this->{$field});
7061
					if (empty($queryarray[$field])) $queryarray[$field]=0;		// May be reset to null later if property 'notnull' is -1 for this field.
7062
				}
7063
			}
7064
			elseif($this->isFloat($info))
7065
			{
7066
				$queryarray[$field] = (double) price2num($this->{$field});
7067
				if (empty($queryarray[$field])) $queryarray[$field]=0;
7068
			}
7069
			else
7070
			{
7071
				$queryarray[$field] = $this->{$field};
7072
			}
7073
7074
			if ($info['type'] == 'timestamp' && empty($queryarray[$field])) unset($queryarray[$field]);
7075
			if (! empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) $queryarray[$field] = null;
7076
		}
7077
7078
		return $queryarray;
7079
	}
7080
7081
	/**
7082
	 * Function to load data from a SQL pointer into properties of current object $this
7083
	 *
7084
	 * @param   stdClass    $obj    Contain data of object from database
7085
     * @return void
7086
	 */
7087
	protected function setVarsFromFetchObj(&$obj)
7088
	{
7089
		foreach ($this->fields as $field => $info)
7090
		{
7091
			if($this->isDate($info))
7092
			{
7093
				if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
7094
				else $this->{$field} = strtotime($obj->{$field});
7095
			}
7096
			elseif($this->isArray($info))
7097
			{
7098
				if(! empty($obj->{$field})) {
7099
					$this->{$field} = @unserialize($obj->{$field});
7100
					// Hack for data not in UTF8
7101
					if($this->{$field } === false) @unserialize(utf8_decode($obj->{$field}));
7102
				} else {
7103
					$this->{$field} = array();
7104
				}
7105
			}
7106
			elseif($this->isInt($info))
7107
			{
7108
				if ($field == 'rowid') $this->id = (int) $obj->{$field};
7109
				else $this->{$field} = (int) $obj->{$field};
7110
			}
7111
			elseif($this->isFloat($info))
7112
			{
7113
				$this->{$field} = (double) $obj->{$field};
7114
			}
7115
			elseif($this->isNull($info))
7116
			{
7117
				$val = $obj->{$field};
7118
				// zero is not null
7119
				$this->{$field} = (is_null($val) || (empty($val) && $val!==0 && $val!=='0') ? null : $val);
7120
			}
7121
			else
7122
			{
7123
				$this->{$field} = $obj->{$field};
7124
			}
7125
		}
7126
7127
		// If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
7128
		if (! isset($this->fields['ref']) && isset($this->id)) $this->ref = $this->id;
7129
	}
7130
7131
	/**
7132
	 * Function to concat keys of fields
7133
	 *
7134
	 * @return string
7135
	 */
7136
	protected function getFieldList()
7137
	{
7138
		$keys = array_keys($this->fields);
7139
		return implode(',', $keys);
7140
	}
7141
7142
	/**
7143
	 * Add quote to field value if necessary
7144
	 *
7145
	 * @param 	string|int	$value			Value to protect
7146
	 * @param	array		$fieldsentry	Properties of field
7147
	 * @return 	string
7148
	 */
7149
	protected function quote($value, $fieldsentry)
7150
	{
7151
		if (is_null($value)) return 'NULL';
7152
		elseif (preg_match('/^(int|double|real)/i', $fieldsentry['type'])) return $this->db->escape("$value");
7153
		else return "'".$this->db->escape($value)."'";
7154
	}
7155
7156
7157
	/**
7158
	 * Create object into database
7159
	 *
7160
	 * @param  User $user      User that creates
7161
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
7162
	 * @return int             <0 if KO, Id of created object if OK
7163
	 */
7164
	public function createCommon(User $user, $notrigger = false)
7165
	{
7166
		global $langs;
7167
7168
		$error = 0;
7169
7170
		$now=dol_now();
7171
7172
		$fieldvalues = $this->setSaveQuery();
7173
		if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) $fieldvalues['date_creation']=$this->db->idate($now);
7174
		if (array_key_exists('fk_user_creat', $fieldvalues) && ! ($fieldvalues['fk_user_creat'] > 0)) $fieldvalues['fk_user_creat']=$user->id;
7175
		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
7176
7177
		$keys=array();
7178
		$values = array();
7179
		foreach ($fieldvalues as $k => $v) {
7180
			$keys[$k] = $k;
7181
			$value = $this->fields[$k];
7182
			$values[$k] = $this->quote($v, $value);
7183
		}
7184
7185
		// Clean and check mandatory
7186
		foreach($keys as $key)
7187
		{
7188
			// If field is an implicit foreign key field
7189
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';
7190
			if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';
7191
7192
			//var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7193
			if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && ! isset($values[$key]) && is_null($val['default']))
0 ignored issues
show
Bug introduced by
The variable $val does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
7194
			{
7195
				$error++;
7196
				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7197
			}
7198
7199
			// If field is an implicit foreign key field
7200
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) $values[$key]='null';
7201
			if (! empty($this->fields[$key]['foreignkey']) && empty($values[$key])) $values[$key]='null';
7202
		}
7203
7204
		if ($error) return -1;
7205
7206
		$this->db->begin();
7207
7208
		if (! $error)
7209
		{
7210
			$sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
7211
			$sql.= ' ('.implode(", ", $keys).')';
7212
			$sql.= ' VALUES ('.implode(", ", $values).')';
7213
7214
			$res = $this->db->query($sql);
7215
			if ($res===false) {
7216
				$error++;
7217
				$this->errors[] = $this->db->lasterror();
7218
			}
7219
		}
7220
7221
		if (! $error)
7222
		{
7223
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
7224
		}
7225
7226
		// If we have a field ref with a default value of (PROV)
7227
		if (! $error)
7228
		{
7229
		    if (key_exists('ref', $this->fields) && $this->fields['ref']['notnull'] > 0 && ! is_null($this->fields['ref']['default']) && $this->fields['ref']['default'] == '(PROV)')
7230
		    {
7231
		        $sql="UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ref = '(PROV".$this->id.")' WHERE ref = '(PROV)' AND rowid = ".$this->id;
7232
		        $resqlupdate = $this->db->query($sql);
7233
		        if ($resqlupdate===false)
7234
		        {
7235
		            $error++;
7236
		            $this->errors[] = $this->db->lasterror();
7237
		        }
7238
		    }
7239
		}
7240
7241
		// Create extrafields
7242
		if (! $error)
7243
		{
7244
			$result=$this->insertExtraFields();
7245
			if ($result < 0) $error++;
7246
		}
7247
7248
		// Triggers
7249
		if (! $error && ! $notrigger)
7250
		{
7251
			// Call triggers
7252
			$result=$this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
7253
			if ($result < 0) { $error++; }
7254
			// End call triggers
7255
		}
7256
7257
		// Commit or rollback
7258
		if ($error) {
7259
			$this->db->rollback();
7260
			return -1;
7261
		} else {
7262
			$this->db->commit();
7263
			return $this->id;
7264
		}
7265
	}
7266
7267
7268
	/**
7269
	 * Load object in memory from the database
7270
	 *
7271
	 * @param	int    $id				Id object
7272
	 * @param	string $ref				Ref
7273
	 * @param	string	$morewhere		More SQL filters (' AND ...')
7274
	 * @return 	int         			<0 if KO, 0 if not found, >0 if OK
7275
	 */
7276
	public function fetchCommon($id, $ref = null, $morewhere = '')
7277
	{
7278
		if (empty($id) && empty($ref) && empty($morewhere)) return -1;
7279
7280
		$sql = 'SELECT '.$this->getFieldList();
7281
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
7282
7283
		if (!empty($id))  $sql.= ' WHERE rowid = '.$id;
7284
		elseif (!empty($ref)) $sql.= " WHERE ref = ".$this->quote($ref, $this->fields['ref']);
7285
		else $sql.=' WHERE 1 = 1';	// usage with empty id and empty ref is very rare
7286
		if ($morewhere)   $sql.= $morewhere;
7287
		$sql.=' LIMIT 1';	// This is a fetch, to be sure to get only one record
7288
7289
		$res = $this->db->query($sql);
7290
		if ($res)
7291
		{
7292
			$obj = $this->db->fetch_object($res);
7293
			if ($obj)
7294
			{
7295
				$this->setVarsFromFetchObj($obj);
7296
				return $this->id;
7297
			}
7298
			else
7299
			{
7300
				return 0;
7301
			}
7302
		}
7303
		else
7304
		{
7305
			$this->error = $this->db->lasterror();
7306
			$this->errors[] = $this->error;
7307
			return -1;
7308
		}
7309
	}
7310
7311
	/**
7312
	 * Load object in memory from the database
7313
	 *
7314
	 * @param	string	$morewhere		More SQL filters (' AND ...')
7315
	 * @return 	int         			<0 if KO, 0 if not found, >0 if OK
7316
	 */
7317
	public function fetchLinesCommon($morewhere = '')
7318
	{
7319
		$objectlineclassname = get_class($this).'Line';
7320
		if (! class_exists($objectlineclassname))
7321
		{
7322
			$this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
7323
			return -1;
7324
		}
7325
7326
		$objectline = new $objectlineclassname($this->db);
7327
7328
		$sql = 'SELECT '.$objectline->getFieldList();
7329
		$sql.= ' FROM '.MAIN_DB_PREFIX.$objectline->table_element;
7330
		$sql.=' WHERE fk_'.$this->element.' = '.$this->id;
7331
		if ($morewhere)   $sql.= $morewhere;
7332
7333
		$resql = $this->db->query($sql);
7334
		if ($resql)
7335
		{
7336
			$num_rows = $this->db->num_rows($resql);
7337
			$i = 0;
7338
			while ($i < $num_rows)
7339
			{
7340
				$obj = $this->db->fetch_object($resql);
7341
				if ($obj)
7342
				{
7343
					$newline = new $objectlineclassname($this->db);
7344
					$newline->setVarsFromFetchObj($obj);
7345
7346
					$this->lines[] = $newline;
7347
				}
7348
				$i++;
7349
			}
7350
7351
			return 1;
7352
		}
7353
		else
7354
		{
7355
			$this->error = $this->db->lasterror();
7356
			$this->errors[] = $this->error;
7357
			return -1;
7358
		}
7359
	}
7360
7361
	/**
7362
	 * Update object into database
7363
	 *
7364
	 * @param  User $user      	User that modifies
7365
	 * @param  bool $notrigger 	false=launch triggers after, true=disable triggers
7366
	 * @return int             	<0 if KO, >0 if OK
7367
	 */
7368
	public function updateCommon(User $user, $notrigger = false)
7369
	{
7370
		global $conf, $langs;
7371
7372
		$error = 0;
7373
7374
		$now=dol_now();
7375
7376
		$fieldvalues = $this->setSaveQuery();
7377
		if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) $fieldvalues['date_modification']=$this->db->idate($now);
7378
		if (array_key_exists('fk_user_modif', $fieldvalues) && ! ($fieldvalues['fk_user_modif'] > 0)) $fieldvalues['fk_user_modif']=$user->id;
7379
		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
7380
7381
		$keys=array();
7382
		$values = array();
7383
		foreach ($fieldvalues as $k => $v) {
7384
			$keys[$k] = $k;
7385
			$value = $this->fields[$k];
7386
			$values[$k] = $this->quote($v, $value);
7387
			$tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$tmp was never initialized. Although not strictly required by PHP, it is generally a good practice to add $tmp = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
7388
		}
7389
7390
		// Clean and check mandatory
7391
		foreach($keys as $key)
7392
		{
7393
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';		// This is an implicit foreign key field
7394
			if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';					// This is an explicit foreign key field
7395
7396
			//var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7397
			/*
7398
			if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
7399
			{
7400
				$error++;
7401
				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7402
			}*/
7403
		}
7404
7405
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET '.implode(',', $tmp).' WHERE rowid='.$this->id ;
0 ignored issues
show
Bug introduced by
The variable $tmp does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
7406
7407
		$this->db->begin();
7408
		if (! $error)
7409
		{
7410
			$res = $this->db->query($sql);
7411
			if ($res===false)
7412
			{
7413
				$error++;
7414
				$this->errors[] = $this->db->lasterror();
7415
			}
7416
		}
7417
7418
		// Update extrafield
7419
		if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
7420
		{
7421
			$result=$this->insertExtraFields();
7422
			if ($result < 0)
7423
			{
7424
				$error++;
7425
			}
7426
		}
7427
7428
		// Triggers
7429
		if (! $error && ! $notrigger)
7430
		{
7431
			// Call triggers
7432
			$result=$this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
7433
			if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail
7434
			// End call triggers
7435
		}
7436
7437
		// Commit or rollback
7438
		if ($error) {
7439
			$this->db->rollback();
7440
			return -1;
7441
		} else {
7442
			$this->db->commit();
7443
			return $this->id;
7444
		}
7445
	}
7446
7447
	/**
7448
	 * Delete object in database
7449
	 *
7450
	 * @param 	User 	$user       			User that deletes
7451
	 * @param 	bool 	$notrigger  			false=launch triggers after, true=disable triggers
7452
	 * @param	int		$forcechilddeletion		0=no, 1=Force deletion of children
7453
	 * @return 	int             				<=0 if KO, >0 if OK
7454
	 */
7455
	public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0)
7456
	{
7457
		$error=0;
7458
7459
		$this->db->begin();
7460
7461
		if ($forcechilddeletion)
7462
		{
7463
			foreach($this->childtables as $table)
7464
			{
7465
				$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$table.' WHERE '.$this->fk_element.' = '.$this->id;
7466
				$resql = $this->db->query($sql);
7467
				if (! $resql)
7468
				{
7469
					$this->error=$this->db->lasterror();
7470
					$this->errors[]=$this->error;
7471
					$this->db->rollback();
7472
					return -1;
7473
				}
7474
			}
7475
		}
7476
		elseif (! empty($this->fk_element) && ! empty($this->childtables))	// If object has childs linked with a foreign key field, we check all child tables.
7477
		{
7478
			$objectisused = $this->isObjectUsed($this->id);
7479
			if (! empty($objectisused))
7480
			{
7481
				dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
7482
				$this->error='ErrorRecordHasChildren';
7483
				$this->errors[]=$this->error;
7484
				$this->db->rollback();
7485
				return 0;
7486
			}
7487
		}
7488
7489
		if (! $error) {
7490
			if (! $notrigger) {
7491
				// Call triggers
7492
				$result=$this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
7493
				if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail
7494
				// End call triggers
7495
			}
7496
		}
7497
7498
		if (! $error && ! empty($this->isextrafieldmanaged))
7499
		{
7500
			$sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element."_extrafields";
7501
			$sql.= " WHERE fk_object=" . $this->id;
7502
7503
			$resql = $this->db->query($sql);
7504
			if (! $resql)
7505
			{
7506
				$this->errors[] = $this->db->lasterror();
7507
				$error++;
7508
			}
7509
		}
7510
7511
		if (! $error)
7512
		{
7513
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
7514
7515
			$res = $this->db->query($sql);
7516
			if($res===false) {
7517
				$error++;
7518
				$this->errors[] = $this->db->lasterror();
7519
			}
7520
		}
7521
7522
		// Commit or rollback
7523
		if ($error) {
7524
			$this->db->rollback();
7525
			return -1;
7526
		} else {
7527
			$this->db->commit();
7528
			return 1;
7529
		}
7530
	}
7531
7532
	/**
7533
	 *  Delete a line of object in database
7534
	 *
7535
	 *	@param  User	$user       User that delete
7536
	 *  @param	int		$idline		Id of line to delete
7537
	 *  @param 	bool 	$notrigger  false=launch triggers after, true=disable triggers
7538
	 *  @return int         		>0 if OK, <0 if KO
7539
	 */
7540
	public function deleteLineCommon(User $user, $idline, $notrigger = false)
7541
	{
7542
		global $conf;
7543
7544
		$error=0;
7545
7546
		$tmpforobjectclass = get_class($this);
7547
		$tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
7548
7549
		// Call trigger
7550
		$result=$this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
7551
		if ($result < 0) return -1;
7552
		// End call triggers
7553
7554
		$this->db->begin();
7555
7556
		$sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element_line;
7557
		$sql.= " WHERE rowid=".$idline;
7558
7559
		dol_syslog(get_class($this)."::deleteLineCommon", LOG_DEBUG);
7560
		$resql = $this->db->query($sql);
7561
		if (! $resql)
7562
		{
7563
			$this->error="Error ".$this->db->lasterror();
7564
			$error++;
7565
		}
7566
7567
		if (empty($error)) {
7568
			// Remove extrafields
7569
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
7570
			{
7571
				$tmpobjectline = new $tmpforobjectlineclass($this->db);
7572
				$tmpobjectline->id= $idline;
7573
				$result=$tmpobjectline->deleteExtraFields();
7574
				if ($result < 0)
7575
				{
7576
					$error++;
7577
					$this->error="Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
7578
				}
7579
			}
7580
		}
7581
7582
		if (empty($error)) {
7583
			$this->db->commit();
7584
			return 1;
7585
		} else {
7586
			dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
7587
			$this->db->rollback();
7588
			return -1;
7589
		}
7590
	}
7591
7592
	/**
7593
	 * Initialise object with example values
7594
	 * Id must be 0 if object instance is a specimen
7595
	 *
7596
	 * @return void
7597
	 */
7598
	public function initAsSpecimenCommon()
7599
	{
7600
	    global $user;
7601
7602
		$this->id = 0;
7603
		if (array_key_exists('label', $this->fields)) $this->label='This is label';
7604
		if (array_key_exists('note_public', $this->fields)) $this->note_public='Public note';
7605
		if (array_key_exists('note_private', $this->fields)) $this->note_private='Private note';
7606
		if (array_key_exists('date_creation', $this->fields)) $this->date_creation=(dol_now()-3600*24);
7607
		if (array_key_exists('date_modification', $this->fields)) $this->date_modification=(dol_now()-3600*24);
7608
		if (array_key_exists('fk_user_creat', $this->fields)) $this->fk_user_creat=$user->id;
7609
		if (array_key_exists('fk_user_modif', $this->fields)) $this->fk_user_modif=$user->id;
7610
		if (array_key_exists('date', $this->fields)) $this->date=dol_now();
7611
		// ...
7612
	}
7613
7614
7615
	/* Part for comments */
7616
7617
	/**
7618
	 * Load comments linked with current task
7619
	 *	@return boolean	1 if ok
7620
	 */
7621
	public function fetchComments()
7622
	{
7623
		require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
7624
7625
		$comment = new Comment($this->db);
7626
		$result=$comment->fetchAllFor($this->element, $this->id);
7627
		if ($result<0) {
7628
			$this->errors=array_merge($this->errors, $comment->errors);
7629
			return -1;
7630
		} else {
7631
			$this->comments = $comment->comments;
7632
		}
7633
		return count($this->comments);
7634
	}
7635
7636
    /**
7637
     * Return nb comments already posted
7638
     *
7639
     * @return int
7640
     */
7641
    public function getNbComments()
7642
    {
7643
        return count($this->comments);
7644
    }
7645
7646
    /**
7647
     * Trim object parameters
7648
     * @param string[] $parameters array of parameters to trim
7649
     *
7650
     * @return void
7651
     */
7652
    public function trimParameters($parameters)
7653
    {
7654
        if (!is_array($parameters)) return;
7655
        foreach ($parameters as $parameter) {
7656
            if (isset($this->$parameter)) {
7657
                $this->$parameter = trim($this->$parameter);
7658
            }
7659
        }
7660
    }
7661
}
7662