Completed
Branch develop (68bb95)
by
unknown
53:54
created

CommonObject::__clone()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 0
dl 0
loc 12
rs 9.8666
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
	public $name;
403
	public $lastname;
404
	public $firstname;
405
	public $civility_id;
406
407
	// Dates
408
	public $date_creation;			// Date creation
409
	public $date_validation;		// Date validation
410
	public $date_modification;		// Date last change (tms field)
411
412
413
414
	// No constructor as it is an abstract class
415
416
	/**
417
	 * Check an object id/ref exists
418
	 * If you don't need/want to instantiate object and just need to know if object exists, use this method instead of fetch
419
	 *
420
	 *  @param	string	$element   	String of element ('product', 'facture', ...)
421
	 *  @param	int		$id      	Id of object
422
	 *  @param  string	$ref     	Ref of object to check
423
	 *  @param	string	$ref_ext	Ref ext of object to check
424
	 *  @return int     			<0 if KO, 0 if OK but not found, >0 if OK and exists
425
	 */
426
	public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
427
	{
428
		global $db,$conf;
429
430
		$sql = "SELECT rowid, ref, ref_ext";
431
		$sql.= " FROM ".MAIN_DB_PREFIX.$element;
432
		$sql.= " WHERE entity IN (".getEntity($element).")" ;
433
434
		if ($id > 0) $sql.= " AND rowid = ".$db->escape($id);
435
		elseif ($ref) $sql.= " AND ref = '".$db->escape($ref)."'";
436
		elseif ($ref_ext) $sql.= " AND ref_ext = '".$db->escape($ref_ext)."'";
437
		else {
438
			$error='ErrorWrongParameters';
439
			dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
440
			return -1;
441
		}
442
		if ($ref || $ref_ext) $sql.= " AND entity = ".$conf->entity;
443
444
		dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
445
		$resql = $db->query($sql);
446
		if ($resql)
447
		{
448
			$num=$db->num_rows($resql);
449
			if ($num > 0) return 1;
450
			else return 0;
451
		}
452
		return -1;
453
	}
454
455
	/**
456
	 * Method to output saved errors
457
	 *
458
	 * @return	string		String with errors
459
	 */
460
	public function errorsToString()
461
	{
462
		return $this->error.(is_array($this->errors)?(($this->error!=''?', ':'').join(', ', $this->errors)):'');
463
	}
464
465
	/**
466
	 * Return customer ref for screen output.
467
	 *
468
	 * @param  string      $objref        Customer ref
469
	 * @return string                     Customer ref formated
470
	 */
471
	public function getFormatedCustomerRef($objref)
472
	{
473
	    global $hookmanager;
474
475
	    $parameters=array('objref'=>$objref);
476
	    $action='';
477
	    $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
478
	    if ($reshook > 0)
479
	    {
480
	        return $hookmanager->resArray['objref'];
481
	    }
482
	    return $objref.(isset($hookmanager->resArray['objref'])?$hookmanager->resArray['objref']:'');
483
	}
484
485
	/**
486
	 * Return supplier ref for screen output.
487
	 *
488
	 * @param  string      $objref        Supplier ref
489
	 * @return string                     Supplier ref formated
490
	 */
491
	public function getFormatedSupplierRef($objref)
492
	{
493
	    global $hookmanager;
494
495
	    $parameters=array('objref'=>$objref);
496
	    $action='';
497
	    $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
498
	    if ($reshook > 0)
499
	    {
500
	        return $hookmanager->resArray['objref'];
501
	    }
502
	    return $objref.(isset($hookmanager->resArray['objref'])?$hookmanager->resArray['objref']:'');
503
	}
504
505
	/**
506
	 *	Return full name (civility+' '+name+' '+lastname)
507
	 *
508
	 *	@param	Translate	$langs			Language object for translation of civility (used only if option is 1)
509
	 *	@param	int			$option			0=No option, 1=Add civility
510
	 * 	@param	int			$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname, 3=Firstname if defined else lastname
511
	 * 	@param	int			$maxlen			Maximum length
512
	 * 	@return	string						String with full name
513
	 */
514
	public function getFullName($langs, $option = 0, $nameorder = -1, $maxlen = 0)
515
	{
516
		//print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n";
517
		$lastname=$this->lastname;
518
		$firstname=$this->firstname;
519
		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:'')))));
520
521
		$ret='';
522
		if ($option && $this->civility_id)
523
		{
524
			if ($langs->transnoentitiesnoconv("Civility".$this->civility_id)!="Civility".$this->civility_id) $ret.=$langs->transnoentitiesnoconv("Civility".$this->civility_id).' ';
525
			else $ret.=$this->civility_id.' ';
526
		}
527
528
		$ret.=dolGetFirstLastname($firstname, $lastname, $nameorder);
529
530
		return dol_trunc($ret, $maxlen);
531
	}
532
533
	/**
534
	 * 	Return full address of contact
535
	 *
536
	 * 	@param		int			$withcountry		1=Add country into address string
537
	 *  @param		string		$sep				Separator to use to build string
538
	 *  @param		int		    $withregion			1=Add region into address string
539
	 *	@return		string							Full address string
540
	 */
541
	public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0)
542
	{
543
		if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country)))
544
		{
545
			require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
546
			$tmparray=getCountry($this->country_id, 'all');
547
			$this->country_code=$tmparray['code'];
548
			$this->country     =$tmparray['label'];
549
		}
550
551
        if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_cpde)))
552
    	{
553
    		require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
554
    		$tmparray=getState($this->state_id, 'all', 0, 1);
555
			$this->state_code   =$tmparray['code'];
556
			$this->state        =$tmparray['label'];
557
			$this->region_code  =$tmparray['region_code'];
558
			$this->region       =$tmparray['region'];
559
        }
560
561
		return dol_format_address($this, $withcountry, $sep);
562
	}
563
564
565
	/**
566
	 * 	Return full address for banner
567
	 *
568
	 * 	@param		string		$htmlkey            HTML id to make banner content unique
569
	 *  @param      Object      $object				Object (thirdparty, thirdparty of contact for contact, null for a member)
570
	 *	@return		string							Full address string
571
	 */
572
	public function getBannerAddress($htmlkey, $object)
573
	{
574
		global $conf, $langs;
575
576
		$countriesusingstate=array('AU','US','IN','GB','ES','UK','TR');    // See also option MAIN_FORCE_STATE_INTO_ADDRESS
577
578
		$contactid=0;
579
		$thirdpartyid=0;
580
		if ($this->element == 'societe')
581
		{
582
			$thirdpartyid=$this->id;
583
		}
584
		if ($this->element == 'contact')
585
		{
586
			$contactid=$this->id;
587
			$thirdpartyid=$object->fk_soc;
588
		}
589
		if ($this->element == 'user')
590
		{
591
			$contactid=$this->contact_id;
592
			$thirdpartyid=$object->fk_soc;
593
		}
594
595
		$out='<!-- BEGIN part to show address block -->';
596
597
		$outdone=0;
598
		$coords = $this->getFullAddress(1, ', ', $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT);
599
		if ($coords)
600
		{
601
			if (! empty($conf->use_javascript_ajax))
602
			{
603
				$namecoords = $this->getFullName($langs, 1).'<br>'.$coords;
604
				// hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile
605
				$out.='<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">';
606
				$out.=img_picto($langs->trans("Address"), 'object_address.png');
607
				$out.='</a> ';
608
			}
609
			$out.=dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); $outdone++;
610
			$outdone++;
611
		}
612
613
		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
614
				&& empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state)
615
		{
616
            if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) {
617
                $out.=($outdone?' - ':'').$this->region.' - '.$this->state;
618
            }
619
            else {
620
                $out.=($outdone?' - ':'').$this->state;
621
            }
622
			$outdone++;
623
		}
624
625
		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>':'');
626
		if (! empty($this->phone) && empty($this->phone_pro)) {		// For objects that store pro phone into ->phone
627
			$out.=dol_print_phone($this->phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro")); $outdone++;
628
		}
629
		if (! empty($this->phone_pro)) {
630
			$out.=dol_print_phone($this->phone_pro, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro")); $outdone++;
631
		}
632
		if (! empty($this->phone_mobile)) {
633
			$out.=dol_print_phone($this->phone_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'mobile', $langs->trans("PhoneMobile")); $outdone++;
634
		}
635
		if (! empty($this->phone_perso)) {
636
			$out.=dol_print_phone($this->phone_perso, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePerso")); $outdone++;
637
		}
638
		if (! empty($this->office_phone)) {
639
			$out.=dol_print_phone($this->office_phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro")); $outdone++;
640
		}
641
		if (! empty($this->user_mobile)) {
642
			$out.=dol_print_phone($this->user_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'mobile', $langs->trans("PhoneMobile")); $outdone++;
643
		}
644
		if (! empty($this->fax)) {
645
			$out.=dol_print_phone($this->fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', '&nbsp;', 'fax', $langs->trans("Fax")); $outdone++;
646
		}
647
		if (! empty($this->office_fax)) {
648
			$out.=dol_print_phone($this->office_fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', '&nbsp;', 'fax', $langs->trans("Fax")); $outdone++;
649
		}
650
651
		$out.='<div style="clear: both;"></div>';
652
		$outdone=0;
653
		if (! empty($this->email))
654
		{
655
			$out.=dol_print_email($this->email, $this->id, $object->id, 'AC_EMAIL', 0, 0, 1);
656
			$outdone++;
657
		}
658
		if (! empty($this->url))
659
		{
660
            //$out.=dol_print_url($this->url,'_goout',0,1);//steve changed to blank
661
		    $out.=dol_print_url($this->url, '_blank', 0, 1);
662
			$outdone++;
663
		}
664
		$out.='<div style="clear: both;">';
665
		if (! empty($conf->socialnetworks->enabled))
666
		{
667
			if ($this->skype) $out.=dol_print_socialnetworks($this->skype, $this->id, $object->id, 'skype');
668
			$outdone++;
669
			if ($this->jabberid) $out.=dol_print_socialnetworks($this->jabberid, $this->id, $object->id, 'jabber');
670
			$outdone++;
671
			if ($this->twitter) $out.=dol_print_socialnetworks($this->twitter, $this->id, $object->id, 'twitter');
672
			$outdone++;
673
			if ($this->facebook) $out.=dol_print_socialnetworks($this->facebook, $this->id, $object->id, 'facebook');
674
			$outdone++;
675
			if ($this->linkedin) $out.=dol_print_socialnetworks($this->linkedin, $this->id, $object->id, 'linkedin');
676
			$outdone++;
677
		}
678
		$out.='</div>';
679
680
		$out.='<!-- END Part to show address block -->';
681
682
		return $out;
683
	}
684
685
	/**
686
	 * Return the link of last main doc file for direct public download.
687
	 *
688
	 * @param	string	$modulepart			Module related to document
689
	 * @param	int		$initsharekey		Init the share key if it was not yet defined
690
	 * @param	int		$relativelink		0=Return full external link, 1=Return link relative to root of file
691
	 * @return	string						Link or empty string if there is no download link
692
	 */
693
	public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
694
	{
695
		global $user, $dolibarr_main_url_root;
696
697
		if (empty($this->last_main_doc))
698
		{
699
			return '';		// No way to known which document name to use
700
		}
701
702
		include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
703
		$ecmfile=new EcmFiles($this->db);
704
		$result = $ecmfile->fetch(0, '', $this->last_main_doc);
705
		if ($result < 0)
706
		{
707
			$this->error = $ecmfile->error;
708
			$this->errors = $ecmfile->errors;
709
			return -1;
710
		}
711
712
		if (empty($ecmfile->id))
713
		{
714
			// Add entry into index
715
			if ($initsharekey)
716
			{
717
				require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
718
				// TODO We can't, we dont' have full path of file, only last_main_doc adn ->element, so we must rebuild full path first
719
				/*
720
				$ecmfile->filepath = $rel_dir;
721
				$ecmfile->filename = $filename;
722
				$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
723
				$ecmfile->fullpath_orig = '';
724
				$ecmfile->gen_or_uploaded = 'generated';
725
				$ecmfile->description = '';    // indexed content
726
				$ecmfile->keyword = '';        // keyword content
727
				$ecmfile->share = getRandomPassword(true);
728
				$result = $ecmfile->create($user);
729
				if ($result < 0)
730
				{
731
					$this->error = $ecmfile->error;
732
					$this->errors = $ecmfile->errors;
733
				}
734
				*/
735
			}
736
			else return '';
737
		}
738
		elseif (empty($ecmfile->share))
739
		{
740
			// Add entry into index
741
			if ($initsharekey)
742
			{
743
				require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
744
				$ecmfile->share = getRandomPassword(true);
745
				$ecmfile->update($user);
746
			}
747
			else return '';
748
		}
749
		// Define $urlwithroot
750
		$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
751
		// This is to use external domain name found into config file
752
		//if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
753
		//else
754
		$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;
755
		//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
756
757
		$forcedownload=0;
758
759
		$paramlink='';
760
		//if (! empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart;		// For sharing with hash (so public files), modulepart is not required.
761
		//if (! empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; 					// For sharing with hash (so public files), entity is not required.
762
		//$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath);								// No need of name of file for public link, we will use the hash
763
		if (! empty($ecmfile->share)) $paramlink.=($paramlink?'&':'').'hashp='.$ecmfile->share;			// Hash for public share
764
		if ($forcedownload) $paramlink.=($paramlink?'&':'').'attachment=1';
765
766
		if ($relativelink)
767
		{
768
			$linktoreturn='document.php'.($paramlink?'?'.$paramlink:'');
769
		}
770
		else
771
		{
772
			$linktoreturn=$urlwithroot.'/document.php'.($paramlink?'?'.$paramlink:'');
773
		}
774
775
		// Here $ecmfile->share is defined
776
		return $linktoreturn;
777
	}
778
779
780
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
781
	/**
782
	 *  Add a link between element $this->element and a contact
783
	 *
784
	 *  @param	int		$fk_socpeople       Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
785
	 *  @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
786
	 *  @param  string	$source             external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
787
	 *  @param  int		$notrigger			Disable all triggers
788
	 *  @return int                 		<0 if KO, >0 if OK
789
	 */
790
	public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
791
	{
792
        // phpcs:enable
793
		global $user,$langs;
794
795
796
		dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
797
798
		// Check parameters
799
		if ($fk_socpeople <= 0)
800
		{
801
			$langs->load("errors");
802
			$this->error=$langs->trans("ErrorWrongValueForParameterX", "1");
803
			dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
804
			return -1;
805
		}
806
		if (! $type_contact)
807
		{
808
			$langs->load("errors");
809
			$this->error=$langs->trans("ErrorWrongValueForParameterX", "2");
810
			dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
811
			return -2;
812
		}
813
814
		$id_type_contact=0;
815
		if (is_numeric($type_contact))
816
		{
817
			$id_type_contact=$type_contact;
818
		}
819
		else
820
		{
821
			// We look for id type_contact
822
			$sql = "SELECT tc.rowid";
823
			$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
824
			$sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
825
			$sql.= " AND tc.source='".$this->db->escape($source)."'";
826
			$sql.= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
827
			//print $sql;
828
			$resql=$this->db->query($sql);
829
			if ($resql)
830
			{
831
				$obj = $this->db->fetch_object($resql);
832
				if ($obj) $id_type_contact=$obj->rowid;
833
			}
834
		}
835
836
		if ($id_type_contact == 0)
837
		{
838
			$this->error='CODE_NOT_VALID_FOR_THIS_ELEMENT';
839
			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");
840
			return -3;
841
		}
842
843
		$datecreate = dol_now();
844
845
		// Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
846
		$TListeContacts=$this->liste_contact(-1, $source);
847
		$already_added=false;
848
		if(!empty($TListeContacts)) {
849
			foreach($TListeContacts as $array_contact) {
850
				if($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
851
					$already_added=true;
852
					break;
853
				}
854
			}
855
		}
856
857
		if(!$already_added) {
858
859
			$this->db->begin();
860
861
			// Insert into database
862
			$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact";
863
			$sql.= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
864
			$sql.= " VALUES (".$this->id.", ".$fk_socpeople." , " ;
865
			$sql.= "'".$this->db->idate($datecreate)."'";
866
			$sql.= ", 4, ". $id_type_contact;
867
			$sql.= ")";
868
869
			$resql=$this->db->query($sql);
870
			if ($resql)
871
			{
872
				if (! $notrigger)
873
				{
874
					$result=$this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
875
					if ($result < 0)
876
					{
877
						$this->db->rollback();
878
						return -1;
879
					}
880
				}
881
882
				$this->db->commit();
883
				return 1;
884
			}
885
			else
886
			{
887
				if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
888
				{
889
					$this->error=$this->db->errno();
890
					$this->db->rollback();
891
					echo 'err rollback';
892
					return -2;
893
				}
894
				else
895
				{
896
					$this->error=$this->db->error();
897
					$this->db->rollback();
898
					return -1;
899
				}
900
			}
901
		} else return 0;
902
	}
903
904
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
905
	/**
906
	 *    Copy contact from one element to current
907
	 *
908
	 *    @param    CommonObject    $objFrom    Source element
909
	 *    @param    string          $source     Nature of contact ('internal' or 'external')
910
	 *    @return   int                         >0 if OK, <0 if KO
911
	 */
912
	public function copy_linked_contact($objFrom, $source = 'internal')
913
	{
914
        // phpcs:enable
915
		$contacts = $objFrom->liste_contact(-1, $source);
916
		foreach($contacts as $contact)
917
		{
918
			if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0)
919
			{
920
				$this->error=$this->db->lasterror();
921
				return -1;
922
			}
923
		}
924
		return 1;
925
	}
926
927
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
928
	/**
929
	 *      Update a link to contact line
930
	 *
931
	 *      @param	int		$rowid              Id of line contact-element
932
	 * 		@param	int		$statut	            New status of link
933
	 *      @param  int		$type_contact_id    Id of contact type (not modified if 0)
934
	 *      @param  int		$fk_socpeople	    Id of soc_people to update (not modified if 0)
935
	 *      @return int                 		<0 if KO, >= 0 if OK
936
	 */
937
	public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
938
	{
939
        // phpcs:enable
940
		// Insert into database
941
		$sql = "UPDATE ".MAIN_DB_PREFIX."element_contact set";
942
		$sql.= " statut = ".$statut;
943
		if ($type_contact_id) $sql.= ", fk_c_type_contact = '".$type_contact_id ."'";
944
		if ($fk_socpeople) $sql.= ", fk_socpeople = '".$fk_socpeople ."'";
945
		$sql.= " where rowid = ".$rowid;
946
		$resql=$this->db->query($sql);
947
		if ($resql)
948
		{
949
			return 0;
950
		}
951
		else
952
		{
953
			$this->error=$this->db->lasterror();
954
			return -1;
955
		}
956
	}
957
958
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
959
	/**
960
	 *    Delete a link to contact line
961
	 *
962
	 *    @param	int		$rowid			Id of contact link line to delete
963
	 *    @param	int		$notrigger		Disable all triggers
964
	 *    @return   int						>0 if OK, <0 if KO
965
	 */
966
	public function delete_contact($rowid, $notrigger = 0)
967
	{
968
        // phpcs:enable
969
		global $user;
970
971
972
		$this->db->begin();
973
974
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
975
		$sql.= " WHERE rowid =".$rowid;
976
977
		dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
978
		if ($this->db->query($sql))
979
		{
980
			if (! $notrigger)
981
			{
982
				$result=$this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
983
				if ($result < 0) { $this->db->rollback(); return -1; }
984
			}
985
986
			$this->db->commit();
987
			return 1;
988
		}
989
		else
990
		{
991
			$this->error=$this->db->lasterror();
992
			$this->db->rollback();
993
			return -1;
994
		}
995
	}
996
997
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
998
	/**
999
	 *    Delete all links between an object $this and all its contacts
1000
	 *
1001
	 *	  @param	string	$source		'' or 'internal' or 'external'
1002
	 *	  @param	string	$code		Type of contact (code or id)
1003
	 *    @return   int					>0 if OK, <0 if KO
1004
	 */
1005
	public function delete_linked_contact($source = '', $code = '')
1006
	{
1007
        // phpcs:enable
1008
		$temp = array();
1009
		$typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1010
1011
		foreach($typeContact as $key => $value)
1012
		{
1013
			array_push($temp, $key);
1014
		}
1015
		$listId = implode(",", $temp);
1016
1017
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1018
		$sql.= " WHERE element_id = ".$this->id;
1019
		if ($listId)
1020
			$sql.= " AND fk_c_type_contact IN (".$listId.")";
1021
1022
		dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1023
		if ($this->db->query($sql))
1024
		{
1025
			return 1;
1026
		}
1027
		else
1028
		{
1029
			$this->error=$this->db->lasterror();
1030
			return -1;
1031
		}
1032
	}
1033
1034
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1035
	/**
1036
	 *    Get array of all contacts for an object
1037
	 *
1038
	 *    @param	int			$statut		Status of links to get (-1=all)
1039
	 *    @param	string		$source		Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)
1040
	 *    @param	int         $list       0:Return array contains all properties, 1:Return array contains just id
1041
	 *    @param    string      $code       Filter on this code of contact type ('SHIPPING', 'BILLING', ...)
1042
	 *    @return	array|int		        Array of contacts, -1 if error
1043
	 */
1044
	public function liste_contact($statut = -1, $source = 'external', $list = 0, $code = '')
1045
	{
1046
        // phpcs:enable
1047
		global $langs;
1048
1049
		$tab=array();
1050
1051
		$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
1052
		if ($source == 'internal') $sql.=", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
1053
		if ($source == 'external' || $source == 'thirdparty') $sql.=", t.fk_soc as socid, t.statut as statuscontact";
1054
		$sql.= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1055
		$sql.= ", tc.source, tc.element, tc.code, tc.libelle";
1056
		$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
1057
		$sql.= ", ".MAIN_DB_PREFIX."element_contact ec";
1058
		if ($source == 'internal') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
1059
		if ($source == 'external'|| $source == 'thirdparty') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
1060
		$sql.= " WHERE ec.element_id =".$this->id;
1061
		$sql.= " AND ec.fk_c_type_contact=tc.rowid";
1062
		$sql.= " AND tc.element='".$this->db->escape($this->element)."'";
1063
		if ($code) $sql.= " AND tc.code = '".$this->db->escape($code)."'";
1064
		if ($source == 'internal') $sql.= " AND tc.source = 'internal'";
1065
		if ($source == 'external' || $source == 'thirdparty') $sql.= " AND tc.source = 'external'";
1066
		$sql.= " AND tc.active=1";
1067
		if ($statut >= 0) $sql.= " AND ec.statut = '".$statut."'";
1068
		$sql.=" ORDER BY t.lastname ASC";
1069
1070
		dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1071
		$resql=$this->db->query($sql);
1072
		if ($resql)
1073
		{
1074
			$num=$this->db->num_rows($resql);
1075
			$i=0;
1076
			while ($i < $num)
1077
			{
1078
				$obj = $this->db->fetch_object($resql);
1079
1080
				if (! $list)
1081
				{
1082
					$transkey="TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1083
					$libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1084
					$tab[$i]=array('source'=>$obj->source,'socid'=>$obj->socid,'id'=>$obj->id,
1085
								   'nom'=>$obj->lastname,      // For backward compatibility
1086
								   'civility'=>$obj->civility, 'lastname'=>$obj->lastname, 'firstname'=>$obj->firstname, 'email'=>$obj->email, 'login'=>$obj->login, 'photo'=>$obj->photo, 'statuscontact'=>$obj->statuscontact,
1087
								   'rowid'=>$obj->rowid, 'code'=>$obj->code, 'libelle'=>$libelle_type, 'status'=>$obj->statuslink, 'fk_c_type_contact'=>$obj->fk_c_type_contact);
1088
				}
1089
				else
1090
				{
1091
					$tab[$i]=$obj->id;
1092
				}
1093
1094
				$i++;
1095
			}
1096
1097
			return $tab;
1098
		}
1099
		else
1100
		{
1101
			$this->error=$this->db->lasterror();
1102
			dol_print_error($this->db);
1103
			return -1;
1104
		}
1105
	}
1106
1107
1108
	/**
1109
	 * 		Update status of a contact linked to object
1110
	 *
1111
	 * 		@param	int		$rowid		Id of link between object and contact
1112
	 * 		@return	int					<0 if KO, >=0 if OK
1113
	 */
1114
	public function swapContactStatus($rowid)
1115
	{
1116
		$sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1117
		$sql.= " tc.code, tc.libelle";
1118
		//$sql.= ", s.fk_soc";
1119
		$sql.= " FROM (".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as tc)";
1120
		//$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
1121
		$sql.= " WHERE ec.rowid =".$rowid;
1122
		$sql.= " AND ec.fk_c_type_contact=tc.rowid";
1123
		$sql.= " AND tc.element = '".$this->db->escape($this->element)."'";
1124
1125
		dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1126
		$resql=$this->db->query($sql);
1127
		if ($resql)
1128
		{
1129
			$obj = $this->db->fetch_object($resql);
1130
			$newstatut = ($obj->statut == 4) ? 5 : 4;
1131
			$result = $this->update_contact($rowid, $newstatut);
1132
			$this->db->free($resql);
1133
			return $result;
1134
		}
1135
		else
1136
		{
1137
			$this->error=$this->db->error();
1138
			dol_print_error($this->db);
1139
			return -1;
1140
		}
1141
	}
1142
1143
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1144
	/**
1145
	 *      Return array with list of possible values for type of contacts
1146
	 *
1147
	 *      @param	string	$source     'internal', 'external' or 'all'
1148
	 *      @param	string	$order		Sort order by : 'position', 'code', 'rowid'...
1149
	 *      @param  int		$option     0=Return array id->label, 1=Return array code->label
1150
	 *      @param  int		$activeonly 0=all status of contact, 1=only the active
1151
	 *		@param	string	$code		Type of contact (Example: 'CUSTOMER', 'SERVICE')
1152
	 *      @return array       		Array list of type of contacts (id->label if option=0, code->label if option=1)
1153
	 */
1154
	public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1155
	{
1156
        // phpcs:enable
1157
		global $langs;
1158
1159
		if (empty($order)) $order='position';
1160
		if ($order == 'position') $order.=',code';
1161
1162
		$tab = array();
1163
		$sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1164
		$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
1165
		$sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
1166
		if ($activeonly == 1) $sql.= " AND tc.active=1"; // only the active types
1167
		if (! empty($source) && $source != 'all') $sql.= " AND tc.source='".$this->db->escape($source)."'";
1168
		if (! empty($code)) $sql.= " AND tc.code='".$this->db->escape($code)."'";
1169
		$sql.= $this->db->order($order, 'ASC');
1170
1171
		//print "sql=".$sql;
1172
		$resql=$this->db->query($sql);
1173
		if ($resql)
1174
		{
1175
			$num=$this->db->num_rows($resql);
1176
			$i=0;
1177
			while ($i < $num)
1178
			{
1179
				$obj = $this->db->fetch_object($resql);
1180
1181
				$transkey="TypeContact_".$this->element."_".$source."_".$obj->code;
1182
				$libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1183
				if (empty($option)) $tab[$obj->rowid]=$libelle_type;
1184
				else $tab[$obj->code]=$libelle_type;
1185
				$i++;
1186
			}
1187
			return $tab;
1188
		}
1189
		else
1190
		{
1191
			$this->error=$this->db->lasterror();
1192
			//dol_print_error($this->db);
1193
			return null;
1194
		}
1195
	}
1196
1197
	/**
1198
	 *      Return id of contacts for a source and a contact code.
1199
	 *      Example: contact client de facturation ('external', 'BILLING')
1200
	 *      Example: contact client de livraison ('external', 'SHIPPING')
1201
	 *      Example: contact interne suivi paiement ('internal', 'SALESREPFOLL')
1202
	 *
1203
	 *		@param	string	$source		'external' or 'internal'
1204
	 *		@param	string	$code		'BILLING', 'SHIPPING', 'SALESREPFOLL', ...
1205
	 *		@param	int		$status		limited to a certain status
1206
	 *      @return array       		List of id for such contacts
1207
	 */
1208
	public function getIdContact($source, $code, $status = 0)
1209
	{
1210
		global $conf;
1211
1212
		$result=array();
1213
		$i=0;
1214
		//cas particulier pour les expeditions
1215
		if($this->element=='shipping' && $this->origin_id != 0) {
1216
			$id=$this->origin_id;
1217
			$element='commande';
1218
        } elseif($this->element=='reception' && $this->origin_id != 0) {
1219
            $id=$this->origin_id;
1220
            $element='order_supplier';
1221
		} else {
1222
			$id=$this->id;
1223
			$element=$this->element;
1224
		}
1225
1226
		$sql = "SELECT ec.fk_socpeople";
1227
		$sql.= " FROM ".MAIN_DB_PREFIX."element_contact as ec,";
1228
		if ($source == 'internal') $sql.= " ".MAIN_DB_PREFIX."user as c,";
1229
		if ($source == 'external') $sql.= " ".MAIN_DB_PREFIX."socpeople as c,";
1230
		$sql.= " ".MAIN_DB_PREFIX."c_type_contact as tc";
1231
		$sql.= " WHERE ec.element_id = ".$id;
1232
		$sql.= " AND ec.fk_socpeople = c.rowid";
1233
		if ($source == 'internal') $sql.= " AND c.entity IN (".getEntity('user').")";
1234
		if ($source == 'external') $sql.= " AND c.entity IN (".getEntity('societe').")";
1235
		$sql.= " AND ec.fk_c_type_contact = tc.rowid";
1236
		$sql.= " AND tc.element = '".$element."'";
1237
		$sql.= " AND tc.source = '".$source."'";
1238
		$sql.= " AND tc.code = '".$code."'";
1239
		$sql.= " AND tc.active = 1";
1240
		if ($status) $sql.= " AND ec.statut = ".$status;
1241
1242
		dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1243
		$resql=$this->db->query($sql);
1244
		if ($resql)
1245
		{
1246
			while ($obj = $this->db->fetch_object($resql))
1247
			{
1248
				$result[$i]=$obj->fk_socpeople;
1249
				$i++;
1250
			}
1251
		}
1252
		else
1253
		{
1254
			$this->error=$this->db->error();
1255
			return null;
1256
		}
1257
1258
		return $result;
1259
	}
1260
1261
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1262
	/**
1263
	 *		Load object contact with id=$this->contactid into $this->contact
1264
	 *
1265
	 *		@param	int		$contactid      Id du contact. Use this->contactid if empty.
1266
	 *		@return	int						<0 if KO, >0 if OK
1267
	 */
1268
	public function fetch_contact($contactid = null)
1269
	{
1270
        // phpcs:enable
1271
		if (empty($contactid)) $contactid=$this->contactid;
1272
1273
		if (empty($contactid)) return 0;
1274
1275
		require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1276
		$contact = new Contact($this->db);
1277
		$result=$contact->fetch($contactid);
1278
		$this->contact = $contact;
1279
		return $result;
1280
	}
1281
1282
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1283
	/**
1284
	 *    	Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty
1285
	 *
1286
	 *		@param		int		$force_thirdparty_id	Force thirdparty id
1287
	 *		@return		int								<0 if KO, >0 if OK
1288
	 */
1289
	public function fetch_thirdparty($force_thirdparty_id = 0)
1290
	{
1291
        // phpcs:enable
1292
		global $conf;
1293
1294
		if (empty($this->socid) && empty($this->fk_soc) && empty($this->fk_thirdparty) && empty($force_thirdparty_id))
1295
			return 0;
1296
1297
		require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1298
1299
		$idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : $this->fk_thirdparty);
1300
		if ($force_thirdparty_id)
1301
			$idtofetch = $force_thirdparty_id;
1302
1303
		if ($idtofetch) {
1304
			$thirdparty = new Societe($this->db);
1305
			$result = $thirdparty->fetch($idtofetch);
1306
			$this->thirdparty = $thirdparty;
1307
1308
			// Use first price level if level not defined for third party
1309
			if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1310
				$this->thirdparty->price_level = 1;
1311
			}
1312
1313
			return $result;
1314
		} else
1315
			return -1;
1316
	}
1317
1318
1319
	/**
1320
	 * Looks for an object with ref matching the wildcard provided
1321
	 * It does only work when $this->table_ref_field is set
1322
	 *
1323
	 * @param string $ref Wildcard
1324
	 * @return int >1 = OK, 0 = Not found or table_ref_field not defined, <0 = KO
1325
	 */
1326
	public function fetchOneLike($ref)
1327
	{
1328
		if (!$this->table_ref_field) {
1329
			return 0;
1330
		}
1331
1332
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE '.$this->table_ref_field.' LIKE "'.$this->db->escape($ref).'" LIMIT 1';
1333
1334
		$query = $this->db->query($sql);
1335
1336
		if (!$this->db->num_rows($query)) {
1337
			return 0;
1338
		}
1339
1340
		$result = $this->db->fetch_object($query);
1341
1342
		return $this->fetch($result->rowid);
1343
	}
1344
1345
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1346
	/**
1347
	 *	Load data for barcode into properties ->barcode_type*
1348
	 *	Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
1349
	 *  if it is not defined, ->element must be defined to know default barcode type.
1350
	 *
1351
	 *	@return		int			<0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
1352
	 */
1353
	public function fetch_barcode()
1354
	{
1355
        // phpcs:enable
1356
		global $conf;
1357
1358
		dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1359
1360
		$idtype=$this->barcode_type;
1361
		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
1362
		{
1363
			if ($this->element == 'product')      $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1364
			elseif ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1365
			else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1366
		}
1367
1368
		if ($idtype > 0)
1369
		{
1370
			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
1371
			{
1372
				$sql = "SELECT rowid, code, libelle as label, coder";
1373
				$sql.= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1374
				$sql.= " WHERE rowid = ".$idtype;
1375
				dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1376
				$resql = $this->db->query($sql);
1377
				if ($resql)
1378
				{
1379
					$obj = $this->db->fetch_object($resql);
1380
					$this->barcode_type       = $obj->rowid;
1381
					$this->barcode_type_code  = $obj->code;
1382
					$this->barcode_type_label = $obj->label;
1383
					$this->barcode_type_coder = $obj->coder;
1384
					return 1;
1385
				}
1386
				else
1387
				{
1388
					dol_print_error($this->db);
1389
					return -1;
1390
				}
1391
			}
1392
		}
1393
		return 0;
1394
	}
1395
1396
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1397
	/**
1398
	 *		Load the project with id $this->fk_project into this->project
1399
	 *
1400
	 *		@return		int			<0 if KO, >=0 if OK
1401
	 */
1402
	public function fetch_projet()
1403
	{
1404
        // phpcs:enable
1405
		include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1406
1407
		if (empty($this->fk_project) && ! empty($this->fk_projet)) $this->fk_project = $this->fk_projet;	// For backward compatibility
1408
		if (empty($this->fk_project)) return 0;
1409
1410
		$project = new Project($this->db);
1411
		$result = $project->fetch($this->fk_project);
1412
1413
		$this->projet = $project;	// deprecated
1414
		$this->project = $project;
1415
		return $result;
1416
	}
1417
1418
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1419
	/**
1420
	 *		Load the product with id $this->fk_product into this->product
1421
	 *
1422
	 *		@return		int			<0 if KO, >=0 if OK
1423
	 */
1424
	public function fetch_product()
1425
	{
1426
        // phpcs:enable
1427
		include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1428
1429
		if (empty($this->fk_product)) return 0;
1430
1431
		$product = new Product($this->db);
1432
		$result = $product->fetch($this->fk_product);
1433
1434
		$this->product = $product;
1435
		return $result;
1436
	}
1437
1438
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1439
	/**
1440
	 *		Load the user with id $userid into this->user
1441
	 *
1442
	 *		@param	int		$userid 		Id du contact
1443
	 *		@return	int						<0 if KO, >0 if OK
1444
	 */
1445
	public function fetch_user($userid)
1446
	{
1447
        // phpcs:enable
1448
		$user = new User($this->db);
1449
		$result=$user->fetch($userid);
1450
		$this->user = $user;
1451
		return $result;
1452
	}
1453
1454
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1455
	/**
1456
	 *	Read linked origin object
1457
	 *
1458
	 *	@return		void
1459
	 */
1460
	public function fetch_origin()
1461
	{
1462
        // phpcs:enable
1463
		if ($this->origin == 'shipping') $this->origin = 'expedition';
1464
		if ($this->origin == 'delivery') $this->origin = 'livraison';
1465
        if ($this->origin == 'order_supplier') $this->origin = 'commandeFournisseur';
1466
1467
		$origin = $this->origin;
1468
1469
		$classname = ucfirst($origin);
1470
		$this->$origin = new $classname($this->db);
1471
		$this->$origin->fetch($this->origin_id);
1472
	}
1473
1474
	/**
1475
     *  Load object from specific field
1476
     *
1477
     *  @param	string	$table		Table element or element line
1478
     *  @param	string	$field		Field selected
1479
     *  @param	string	$key		Import key
1480
     *  @param	string	$element	Element name
1481
     *	@return	int					<0 if KO, >0 if OK
1482
     */
1483
	public function fetchObjectFrom($table, $field, $key, $element = null)
1484
	{
1485
		global $conf;
1486
1487
		$result=false;
1488
1489
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$table;
1490
		$sql.= " WHERE ".$field." = '".$key."'";
1491
		if (! empty($element)) {
1492
			$sql.= " AND entity IN (".getEntity($element).")";
1493
		} else {
1494
			$sql.= " AND entity = ".$conf->entity;
1495
		}
1496
1497
		dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1498
		$resql = $this->db->query($sql);
1499
		if ($resql)
1500
		{
1501
			$row = $this->db->fetch_row($resql);
1502
			// Test for avoid error -1
1503
			if ($row[0] > 0) {
1504
				$result = $this->fetch($row[0]);
1505
			}
1506
		}
1507
1508
		return $result;
1509
	}
1510
1511
	/**
1512
	 *	Getter generic. Load value from a specific field
1513
	 *
1514
	 *	@param	string	$table		Table of element or element line
1515
	 *	@param	int		$id			Element id
1516
	 *	@param	string	$field		Field selected
1517
	 *	@return	int					<0 if KO, >0 if OK
1518
	 */
1519
	public function getValueFrom($table, $id, $field)
1520
	{
1521
		$result=false;
1522
		if (!empty($id) && !empty($field) && !empty($table)) {
1523
			$sql = "SELECT ".$field." FROM ".MAIN_DB_PREFIX.$table;
1524
			$sql.= " WHERE rowid = ".$id;
1525
1526
			dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1527
			$resql = $this->db->query($sql);
1528
			if ($resql)
1529
			{
1530
				$row = $this->db->fetch_row($resql);
1531
				$result = $row[0];
1532
			}
1533
		}
1534
		return $result;
1535
	}
1536
1537
	/**
1538
	 *	Setter generic. Update a specific field into database.
1539
	 *  Warning: Trigger is run only if param trigkey is provided.
1540
	 *
1541
	 *	@param	string		$field			Field to update
1542
	 *	@param	mixed		$value			New value
1543
	 *	@param	string		$table			To force other table element or element line (should not be used)
1544
	 *	@param	int			$id				To force other object id (should not be used)
1545
	 *	@param	string		$format			Data format ('text', 'date'). 'text' is used if not defined
1546
	 *	@param	string		$id_field		To force rowid field name. 'rowid' is used if not defined
1547
	 *	@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'
1548
	 *  @param  string      $trigkey    	Trigger key to run (in most cases something like 'XXX_MODIFY')
1549
	 *  @param	string		$fk_user_field	Name of field to save user id making change
1550
	 *	@return	int							<0 if KO, >0 if OK
1551
	 *  @see updateExtraField()
1552
	 */
1553
	public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
1554
	{
1555
		global $user,$langs,$conf;
1556
1557
		if (empty($table)) 	  $table=$this->table_element;
1558
		if (empty($id))    	  $id=$this->id;
1559
		if (empty($format))   $format='text';
1560
		if (empty($id_field)) $id_field='rowid';
1561
1562
		$error=0;
1563
1564
		$this->db->begin();
1565
1566
		// Special case
1567
		if ($table == 'product' && $field == 'note_private') $field='note';
1568
		if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) $fk_user_field = 'fk_user_mod';
1569
1570
		$sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET ";
1571
1572
		if ($format == 'text') $sql.= $field." = '".$this->db->escape($value)."'";
1573
		elseif ($format == 'int') $sql.= $field." = ".$this->db->escape($value);
1574
		elseif ($format == 'date') $sql.= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
1575
1576
		if ($fk_user_field)
1577
		{
1578
			if (! empty($fuser) && is_object($fuser)) $sql.=", ".$fk_user_field." = ".$fuser->id;
1579
			elseif (empty($fuser) || $fuser != 'none') $sql.=", ".$fk_user_field." = ".$user->id;
1580
		}
1581
1582
		$sql.= " WHERE ".$id_field." = ".$id;
1583
1584
		dol_syslog(get_class($this)."::".__FUNCTION__."", LOG_DEBUG);
1585
		$resql = $this->db->query($sql);
1586
		if ($resql)
1587
		{
1588
			if ($trigkey)
1589
			{
1590
				// call trigger with updated object values
1591
				if (empty($this->fields) && method_exists($this, 'fetch'))
1592
				{
1593
					$result = $this->fetch($id);
1594
				}
1595
				else
1596
				{
1597
					$result = $this->fetchCommon($id);
1598
				}
1599
				if ($result >= 0) $result=$this->call_trigger($trigkey, (! empty($fuser) && is_object($fuser)) ? $fuser : $user);   // This may set this->errors
1600
				if ($result < 0) $error++;
1601
			}
1602
1603
			if (! $error)
1604
			{
1605
				if (property_exists($this, $field)) $this->$field = $value;
1606
				$this->db->commit();
1607
				return 1;
1608
			}
1609
			else
1610
			{
1611
				$this->db->rollback();
1612
				return -2;
1613
			}
1614
		}
1615
		else
1616
		{
1617
			$this->error=$this->db->lasterror();
1618
			$this->db->rollback();
1619
			return -1;
1620
		}
1621
	}
1622
1623
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1624
	/**
1625
	 *      Load properties id_previous and id_next by comparing $fieldid with $this->ref
1626
	 *
1627
	 *      @param	string	$filter		Optional filter. Example: " AND (t.field1 = 'aa' OR t.field2 = 'bb')"
1628
	 *	 	@param  string	$fieldid   	Name of field to use for the select MAX and MIN
1629
	 *		@param	int		$nodbprefix	Do not include DB prefix to forge table name
1630
	 *      @return int         		<0 if KO, >0 if OK
1631
	 */
1632
	public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
1633
	{
1634
        // phpcs:enable
1635
		global $conf, $user;
1636
1637
		if (! $this->table_element)
1638
		{
1639
			dol_print_error('', get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
1640
			return -1;
1641
		}
1642
		if ($fieldid == 'none') return 1;
1643
1644
		// Security on socid
1645
		$socid = 0;
1646
		if ($user->societe_id > 0) $socid = $user->societe_id;
1647
1648
		// this->ismultientitymanaged contains
1649
		// 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
1650
		$alias = 's';
1651
		if ($this->element == 'societe') $alias = 'te';
1652
1653
		$sql = "SELECT MAX(te.".$fieldid.")";
1654
		$sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1655
		if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1656
			$sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1657
		}
1658
		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
1659
		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
1660
		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
1661
		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";
1662
		$sql.= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1663
		if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1664
		if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1665
		if (! empty($filter))
1666
		{
1667
			if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1668
			$sql.=$filter;
1669
		}
1670
		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
1671
		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
1672
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1673
			if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1674
				if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1675
					$sql.= " AND te.entity IS NOT NULL"; // Show all users
1676
				} else {
1677
					$sql.= " AND ug.fk_user = te.rowid";
1678
					$sql.= " AND ug.entity IN (".getEntity($this->element).")";
1679
				}
1680
			} else {
1681
				$sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1682
			}
1683
		}
1684
		if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1685
		if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1686
		if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1687
		//print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1688
1689
		$result = $this->db->query($sql);
1690
		if (! $result)
1691
		{
1692
			$this->error=$this->db->lasterror();
1693
			return -1;
1694
		}
1695
		$row = $this->db->fetch_row($result);
1696
		$this->ref_previous = $row[0];
1697
1698
1699
		$sql = "SELECT MIN(te.".$fieldid.")";
1700
		$sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1701
		if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1702
			$sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1703
		}
1704
		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
1705
		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
1706
		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
1707
		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";
1708
		$sql.= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1709
		if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1710
		if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1711
		if (! empty($filter))
1712
		{
1713
			if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1714
			$sql.=$filter;
1715
		}
1716
		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
1717
		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
1718
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1719
			if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1720
				if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1721
					$sql.= " AND te.entity IS NOT NULL"; // Show all users
1722
				} else {
1723
					$sql.= " AND ug.fk_user = te.rowid";
1724
					$sql.= " AND ug.entity IN (".getEntity($this->element).")";
1725
				}
1726
			} else {
1727
				$sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1728
			}
1729
		}
1730
		if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1731
		if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1732
		if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1733
		//print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1734
		// 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
1735
1736
		$result = $this->db->query($sql);
1737
		if (! $result)
1738
		{
1739
			$this->error=$this->db->lasterror();
1740
			return -2;
1741
		}
1742
		$row = $this->db->fetch_row($result);
1743
		$this->ref_next = $row[0];
1744
1745
		return 1;
1746
	}
1747
1748
1749
	/**
1750
	 *      Return list of id of contacts of object
1751
	 *
1752
	 *      @param	string	$source     Source of contact: external (llx_socpeople) or internal (llx_user) or thirdparty (llx_societe)
1753
	 *      @return array				Array of id of contacts (if source=external or internal)
1754
	 * 									Array of id of third parties with at least one contact on object (if source=thirdparty)
1755
	 */
1756
	public function getListContactId($source = 'external')
1757
	{
1758
		$contactAlreadySelected = array();
1759
		$tab = $this->liste_contact(-1, $source);
1760
		$num=count($tab);
1761
		$i = 0;
1762
		while ($i < $num)
1763
		{
1764
			if ($source == 'thirdparty') $contactAlreadySelected[$i] = $tab[$i]['socid'];
1765
			else  $contactAlreadySelected[$i] = $tab[$i]['id'];
1766
			$i++;
1767
		}
1768
		return $contactAlreadySelected;
1769
	}
1770
1771
1772
	/**
1773
	 *	Link element with a project
1774
	 *
1775
	 *	@param     	int		$projectid		Project id to link element to
1776
	 *	@return		int						<0 if KO, >0 if OK
1777
	 */
1778
	public function setProject($projectid)
1779
	{
1780
		if (! $this->table_element)
1781
		{
1782
			dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR);
1783
			return -1;
1784
		}
1785
1786
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1787
		if ($this->table_element == 'actioncomm')
1788
		{
1789
			if ($projectid) $sql.= ' SET fk_project = '.$projectid;
1790
			else $sql.= ' SET fk_project = NULL';
1791
			$sql.= ' WHERE id = '.$this->id;
1792
		}
1793
		else
1794
		{
1795
			if ($projectid) $sql.= ' SET fk_projet = '.$projectid;
1796
			else $sql.= ' SET fk_projet = NULL';
1797
			$sql.= ' WHERE rowid = '.$this->id;
1798
		}
1799
1800
		dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
1801
		if ($this->db->query($sql))
1802
		{
1803
			$this->fk_project = $projectid;
1804
			return 1;
1805
		}
1806
		else
1807
		{
1808
			dol_print_error($this->db);
1809
			return -1;
1810
		}
1811
	}
1812
1813
	/**
1814
	 *  Change the payments methods
1815
	 *
1816
	 *  @param		int		$id		Id of new payment method
1817
	 *  @return		int				>0 if OK, <0 if KO
1818
	 */
1819
	public function setPaymentMethods($id)
1820
	{
1821
		dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
1822
		if ($this->statut >= 0 || $this->element == 'societe')
1823
		{
1824
			// TODO uniformize field name
1825
			$fieldname = 'fk_mode_reglement';
1826
			if ($this->element == 'societe') $fieldname = 'mode_reglement';
1827
			if (get_class($this) == 'Fournisseur') $fieldname = 'mode_reglement_supplier';
1828
1829
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1830
			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
1831
			$sql .= ' WHERE rowid='.$this->id;
1832
1833
			if ($this->db->query($sql))
1834
			{
1835
				$this->mode_reglement_id = $id;
1836
				// for supplier
1837
				if (get_class($this) == 'Fournisseur') $this->mode_reglement_supplier_id = $id;
1838
				return 1;
1839
			}
1840
			else
1841
			{
1842
				dol_syslog(get_class($this).'::setPaymentMethods Erreur '.$sql.' - '.$this->db->error());
1843
				$this->error=$this->db->error();
1844
				return -1;
1845
			}
1846
		}
1847
		else
1848
		{
1849
			dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
1850
			$this->error='Status of the object is incompatible '.$this->statut;
1851
			return -2;
1852
		}
1853
	}
1854
1855
	/**
1856
	 *  Change the multicurrency code
1857
	 *
1858
	 *  @param		string	$code	multicurrency code
1859
	 *  @return		int				>0 if OK, <0 if KO
1860
	 */
1861
	public function setMulticurrencyCode($code)
1862
	{
1863
		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...
1864
		if ($this->statut >= 0 || $this->element == 'societe')
1865
		{
1866
			$fieldname = 'multicurrency_code';
1867
1868
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1869
			$sql .= ' SET '.$fieldname." = '".$this->db->escape($code)."'";
1870
			$sql .= ' WHERE rowid='.$this->id;
1871
1872
			if ($this->db->query($sql))
1873
			{
1874
				$this->multicurrency_code = $code;
1875
1876
				list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
1877
				if ($rate) $this->setMulticurrencyRate($rate, 2);
1878
1879
				return 1;
1880
			}
1881
			else
1882
			{
1883
				dol_syslog(get_class($this).'::setMulticurrencyCode Erreur '.$sql.' - '.$this->db->error());
1884
				$this->error=$this->db->error();
1885
				return -1;
1886
			}
1887
		}
1888
		else
1889
		{
1890
			dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
1891
			$this->error='Status of the object is incompatible '.$this->statut;
1892
			return -2;
1893
		}
1894
	}
1895
1896
	/**
1897
	 *  Change the multicurrency rate
1898
	 *
1899
	 *  @param		double	$rate	multicurrency rate
1900
	 *  @param		int		$mode	mode 1 : amounts in company currency will be recalculated, mode 2 : amounts in foreign currency
1901
	 *  @return		int				>0 if OK, <0 if KO
1902
	 */
1903
	public function setMulticurrencyRate($rate, $mode = 1)
1904
	{
1905
		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...
1906
		if ($this->statut >= 0 || $this->element == 'societe')
1907
		{
1908
			$fieldname = 'multicurrency_tx';
1909
1910
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1911
			$sql .= ' SET '.$fieldname.' = '.$rate;
1912
			$sql .= ' WHERE rowid='.$this->id;
1913
1914
			if ($this->db->query($sql))
1915
			{
1916
				$this->multicurrency_tx = $rate;
1917
1918
				// Update line price
1919
				if (!empty($this->lines))
1920
				{
1921
					foreach ($this->lines as &$line)
1922
					{
1923
						if($mode == 1) {
1924
							$line->subprice = 0;
1925
						}
1926
1927
						switch ($this->element) {
1928
							case 'propal':
1929
								$this->updateline(
1930
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1931
									($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1932
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start,
1933
									$line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1934
								);
1935
								break;
1936
							case 'commande':
1937
								$this->updateline(
1938
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1939
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end,
1940
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1941
									$line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1942
								);
1943
								break;
1944
							case 'facture':
1945
								$this->updateline(
1946
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1947
									$line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,
1948
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1949
									$line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice
1950
								);
1951
								break;
1952
							case 'supplier_proposal':
1953
								$this->updateline(
1954
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1955
									($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1956
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options,
1957
									$line->ref_fourn, $line->multicurrency_subprice
1958
								);
1959
								break;
1960
							case 'order_supplier':
1961
								$this->updateline(
1962
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1963
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false,
1964
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1965
								);
1966
								break;
1967
							case 'invoice_supplier':
1968
								$this->updateline(
1969
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx,
1970
									$line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false,
1971
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1972
								);
1973
								break;
1974
							default:
1975
								dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
1976
								break;
1977
						}
1978
					}
1979
				}
1980
1981
				return 1;
1982
			}
1983
			else
1984
			{
1985
				dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
1986
				$this->error=$this->db->error();
1987
				return -1;
1988
			}
1989
		}
1990
		else
1991
		{
1992
			dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
1993
			$this->error='Status of the object is incompatible '.$this->statut;
1994
			return -2;
1995
		}
1996
	}
1997
1998
	/**
1999
	 *  Change the payments terms
2000
	 *
2001
	 *  @param		int		$id		Id of new payment terms
2002
	 *  @return		int				>0 if OK, <0 if KO
2003
	 */
2004
	public function setPaymentTerms($id)
2005
	{
2006
		dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
2007
		if ($this->statut >= 0 || $this->element == 'societe')
2008
		{
2009
			// TODO uniformize field name
2010
			$fieldname = 'fk_cond_reglement';
2011
			if ($this->element == 'societe') $fieldname = 'cond_reglement';
2012
			if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
2013
2014
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2015
			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
2016
			$sql .= ' WHERE rowid='.$this->id;
2017
2018
			if ($this->db->query($sql))
2019
			{
2020
				$this->cond_reglement_id = $id;
2021
				// for supplier
2022
				if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
2023
				$this->cond_reglement = $id;	// for compatibility
2024
				return 1;
2025
			}
2026
			else
2027
			{
2028
				dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
2029
				$this->error=$this->db->error();
2030
				return -1;
2031
			}
2032
		}
2033
		else
2034
		{
2035
			dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2036
			$this->error='Status of the object is incompatible '.$this->statut;
2037
			return -2;
2038
		}
2039
	}
2040
2041
	/**
2042
	 *	Define delivery address
2043
	 *  @deprecated
2044
	 *
2045
	 *	@param      int		$id		Address id
2046
	 *	@return     int				<0 si ko, >0 si ok
2047
	 */
2048
	public function setDeliveryAddress($id)
2049
	{
2050
		$fieldname = 'fk_delivery_address';
2051
		if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
2052
2053
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
2054
		$sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
2055
2056
		if ($this->db->query($sql))
2057
		{
2058
			$this->fk_delivery_address = $id;
2059
			return 1;
2060
		}
2061
		else
2062
		{
2063
			$this->error=$this->db->error();
2064
			dol_syslog(get_class($this).'::setDeliveryAddress Erreur '.$sql.' - '.$this->error);
2065
			return -1;
2066
		}
2067
	}
2068
2069
2070
	/**
2071
	 *  Change the shipping method
2072
	 *
2073
	 *  @param      int     $shipping_method_id     Id of shipping method
2074
     *  @param      bool    $notrigger              false=launch triggers after, true=disable triggers
2075
     *  @param      User	$userused               Object user
2076
	 *
2077
	 *  @return     int              1 if OK, 0 if KO
2078
	 */
2079
	public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null)
2080
	{
2081
        global $user;
2082
2083
        if (empty($userused)) $userused=$user;
2084
2085
        $error = 0;
2086
2087
		if (! $this->table_element) {
2088
			dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR);
2089
			return -1;
2090
		}
2091
2092
        $this->db->begin();
2093
2094
		if ($shipping_method_id<0) $shipping_method_id='NULL';
2095
		dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2096
2097
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2098
		$sql.= " SET fk_shipping_method = ".$shipping_method_id;
2099
		$sql.= " WHERE rowid=".$this->id;
2100
        $resql = $this->db->query($sql);
2101
		if (! $resql) {
2102
			dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2103
			$this->error = $this->db->lasterror();
2104
			$error++;
2105
        } else {
2106
            if (!$notrigger)
2107
            {
2108
                // Call trigger
2109
                $this->context=array('shippingmethodupdate'=>1);
2110
                $result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2111
                if ($result < 0) $error++;
2112
                // End call trigger
2113
            }
2114
        }
2115
        if ($error)
2116
        {
2117
            $this->db->rollback();
2118
            return -1;
2119
        } else {
2120
            $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...
2121
            $this->db->commit();
2122
            return 1;
2123
        }
2124
	}
2125
2126
2127
	/**
2128
	 *  Change the warehouse
2129
	 *
2130
	 *  @param      int     $warehouse_id     Id of warehouse
2131
	 *  @return     int              1 if OK, 0 if KO
2132
	 */
2133
	public function setWarehouse($warehouse_id)
2134
	{
2135
		if (! $this->table_element) {
2136
			dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR);
2137
			return -1;
2138
		}
2139
		if ($warehouse_id<0) $warehouse_id='NULL';
2140
		dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2141
2142
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2143
		$sql.= " SET fk_warehouse = ".$warehouse_id;
2144
		$sql.= " WHERE rowid=".$this->id;
2145
2146
		if ($this->db->query($sql)) {
2147
			$this->warehouse_id = ($warehouse_id=='NULL')?null:$warehouse_id;
2148
			return 1;
2149
		} else {
2150
			dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2151
			$this->error=$this->db->error();
2152
			return 0;
2153
		}
2154
	}
2155
2156
2157
	/**
2158
	 *		Set last model used by doc generator
2159
	 *
2160
	 *		@param		User	$user		User object that make change
2161
	 *		@param		string	$modelpdf	Modele name
2162
	 *		@return		int					<0 if KO, >0 if OK
2163
	 */
2164
	public function setDocModel($user, $modelpdf)
2165
	{
2166
		if (! $this->table_element)
2167
		{
2168
			dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR);
2169
			return -1;
2170
		}
2171
2172
		$newmodelpdf=dol_trunc($modelpdf, 255);
2173
2174
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2175
		$sql.= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2176
		$sql.= " WHERE rowid = ".$this->id;
2177
		// if ($this->element == 'facture') $sql.= " AND fk_statut < 2";
2178
		// if ($this->element == 'propal')  $sql.= " AND fk_statut = 0";
2179
2180
		dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2181
		$resql=$this->db->query($sql);
2182
		if ($resql)
2183
		{
2184
			$this->modelpdf=$modelpdf;
2185
			return 1;
2186
		}
2187
		else
2188
		{
2189
			dol_print_error($this->db);
2190
			return 0;
2191
		}
2192
	}
2193
2194
2195
	/**
2196
	 *  Change the bank account
2197
	 *
2198
	 *  @param		int		$fk_account		Id of bank account
2199
	 *  @param      bool    $notrigger      false=launch triggers after, true=disable triggers
2200
	 *  @param      User	$userused		Object user
2201
	 *  @return		int				1 if OK, 0 if KO
2202
	 */
2203
	public function setBankAccount($fk_account, $notrigger = false, $userused = null)
2204
	{
2205
        global $user;
2206
2207
        if (empty($userused)) $userused=$user;
2208
2209
        $error = 0;
2210
2211
		if (! $this->table_element) {
2212
			dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR);
2213
			return -1;
2214
		}
2215
        $this->db->begin();
2216
2217
		if ($fk_account<0) $fk_account='NULL';
2218
		dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2219
2220
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2221
		$sql.= " SET fk_account = ".$fk_account;
2222
		$sql.= " WHERE rowid=".$this->id;
2223
2224
        $resql = $this->db->query($sql);
2225
        if (! $resql)
2226
        {
2227
            dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2228
            $this->error = $this->db->lasterror();
2229
            $error++;
2230
        }
2231
        else
2232
        {
2233
            if (!$notrigger)
2234
            {
2235
                // Call trigger
2236
                $this->context=array('bankaccountupdate'=>1);
2237
                $result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2238
                if ($result < 0) $error++;
2239
                // End call trigger
2240
            }
2241
        }
2242
        if ($error)
2243
        {
2244
            $this->db->rollback();
2245
            return -1;
2246
        }
2247
        else
2248
        {
2249
            $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...
2250
            $this->db->commit();
2251
            return 1;
2252
        }
2253
    }
2254
2255
2256
	// TODO: Move line related operations to CommonObjectLine?
2257
2258
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2259
	/**
2260
	 *  Save a new position (field rang) for details lines.
2261
	 *  You can choose to set position for lines with already a position or lines without any position defined.
2262
	 *
2263
	 * 	@param		boolean		$renum			   True to renum all already ordered lines, false to renum only not already ordered lines.
2264
	 * 	@param		string		$rowidorder		   ASC or DESC
2265
	 * 	@param		boolean		$fk_parent_line    Table with fk_parent_line field or not
2266
	 * 	@return		int                            <0 if KO, >0 if OK
2267
	 */
2268
	public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
2269
	{
2270
        // phpcs:enable
2271
		if (! $this->table_element_line)
2272
		{
2273
			dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR);
2274
			return -1;
2275
		}
2276
		if (! $this->fk_element)
2277
		{
2278
			dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR);
2279
			return -1;
2280
		}
2281
2282
		// Count number of lines to reorder (according to choice $renum)
2283
		$nl=0;
2284
		$sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2285
		$sql.= ' WHERE '.$this->fk_element.'='.$this->id;
2286
		if (! $renum) $sql.= ' AND rang = 0';
2287
		if ($renum) $sql.= ' AND rang <> 0';
2288
2289
		dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
2290
		$resql = $this->db->query($sql);
2291
		if ($resql)
2292
		{
2293
			$row = $this->db->fetch_row($resql);
2294
			$nl = $row[0];
2295
		}
2296
		else dol_print_error($this->db);
2297
		if ($nl > 0)
2298
		{
2299
			// The goal of this part is to reorder all lines, with all children lines sharing the same
2300
			// counter that parents.
2301
			$rows=array();
2302
2303
			// We first search all lines that are parent lines (for multilevel details lines)
2304
			$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2305
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2306
			if ($fk_parent_line) $sql.= ' AND fk_parent_line IS NULL';
2307
			$sql.= ' ORDER BY rang ASC, rowid '.$rowidorder;
2308
2309
			dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
2310
			$resql = $this->db->query($sql);
2311
			if ($resql)
2312
			{
2313
				$i=0;
2314
				$num = $this->db->num_rows($resql);
2315
				while ($i < $num)
2316
				{
2317
					$row = $this->db->fetch_row($resql);
2318
					$rows[] = $row[0];	// Add parent line into array rows
2319
					$childrens = $this->getChildrenOfLine($row[0]);
2320
					if (! empty($childrens))
2321
					{
2322
						foreach($childrens as $child)
2323
						{
2324
							array_push($rows, $child);
2325
						}
2326
					}
2327
					$i++;
2328
				}
2329
2330
				// Now we set a new number for each lines (parent and children with children included into parent tree)
2331
				if (! empty($rows))
2332
				{
2333
					foreach($rows as $key => $row)
2334
					{
2335
						$this->updateRangOfLine($row, ($key+1));
2336
					}
2337
				}
2338
			}
2339
			else
2340
			{
2341
				dol_print_error($this->db);
2342
			}
2343
		}
2344
		return 1;
2345
	}
2346
2347
	/**
2348
	 * 	Get children of line
2349
	 *
2350
	 * 	@param	int		$id		Id of parent line
2351
	 * 	@return	array			Array with list of children lines id
2352
	 */
2353
	public function getChildrenOfLine($id)
2354
	{
2355
		$rows=array();
2356
2357
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2358
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2359
		$sql.= ' AND fk_parent_line = '.$id;
2360
		$sql.= ' ORDER BY rang ASC';
2361
2362
		dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG);
2363
		$resql = $this->db->query($sql);
2364
		if ($resql)
2365
		{
2366
			$i=0;
2367
			$num = $this->db->num_rows($resql);
2368
			while ($i < $num)
2369
			{
2370
				$row = $this->db->fetch_row($resql);
2371
				$rows[$i] = $row[0];
2372
				$i++;
2373
			}
2374
		}
2375
2376
		return $rows;
2377
	}
2378
2379
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2380
	/**
2381
	 * 	Update a line to have a lower rank
2382
	 *
2383
	 * 	@param 	int			$rowid				Id of line
2384
	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2385
	 * 	@return	void
2386
	 */
2387
	public function line_up($rowid, $fk_parent_line = true)
2388
	{
2389
        // phpcs:enable
2390
		$this->line_order(false, 'ASC', $fk_parent_line);
2391
2392
		// Get rang of line
2393
		$rang = $this->getRangOfLine($rowid);
2394
2395
		// Update position of line
2396
		$this->updateLineUp($rowid, $rang);
2397
	}
2398
2399
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2400
	/**
2401
	 * 	Update a line to have a higher rank
2402
	 *
2403
	 * 	@param	int			$rowid				Id of line
2404
	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2405
	 * 	@return	void
2406
	 */
2407
	public function line_down($rowid, $fk_parent_line = true)
2408
	{
2409
        // phpcs:enable
2410
		$this->line_order(false, 'ASC', $fk_parent_line);
2411
2412
		// Get rang of line
2413
		$rang = $this->getRangOfLine($rowid);
2414
2415
		// Get max value for rang
2416
		$max = $this->line_max();
2417
2418
		// Update position of line
2419
		$this->updateLineDown($rowid, $rang, $max);
2420
	}
2421
2422
	/**
2423
	 * 	Update position of line (rang)
2424
	 *
2425
	 * 	@param	int		$rowid		Id of line
2426
	 * 	@param	int		$rang		Position
2427
	 * 	@return	void
2428
	 */
2429
	public function updateRangOfLine($rowid, $rang)
2430
	{
2431
		$fieldposition = 'rang';
2432
		if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2433
2434
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2435
		$sql.= ' WHERE rowid = '.$rowid;
2436
2437
		dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
2438
		if (! $this->db->query($sql))
2439
		{
2440
			dol_print_error($this->db);
2441
		}
2442
	}
2443
2444
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2445
	/**
2446
	 * 	Update position of line with ajax (rang)
2447
	 *
2448
	 * 	@param	array	$rows	Array of rows
2449
	 * 	@return	void
2450
	 */
2451
	public function line_ajaxorder($rows)
2452
	{
2453
        // phpcs:enable
2454
		$num = count($rows);
2455
		for ($i = 0 ; $i < $num ; $i++)
2456
		{
2457
			$this->updateRangOfLine($rows[$i], ($i+1));
2458
		}
2459
	}
2460
2461
	/**
2462
	 * 	Update position of line up (rang)
2463
	 *
2464
	 * 	@param	int		$rowid		Id of line
2465
	 * 	@param	int		$rang		Position
2466
	 * 	@return	void
2467
	 */
2468
	public function updateLineUp($rowid, $rang)
2469
	{
2470
		if ($rang > 1)
2471
		{
2472
			$fieldposition = 'rang';
2473
			if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2474
2475
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang ;
2476
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2477
			$sql.= ' AND rang = '.($rang - 1);
2478
			if ($this->db->query($sql) )
2479
			{
2480
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang - 1);
2481
				$sql.= ' WHERE rowid = '.$rowid;
2482
				if (! $this->db->query($sql) )
2483
				{
2484
					dol_print_error($this->db);
2485
				}
2486
			}
2487
			else
2488
			{
2489
				dol_print_error($this->db);
2490
			}
2491
		}
2492
	}
2493
2494
	/**
2495
	 * 	Update position of line down (rang)
2496
	 *
2497
	 * 	@param	int		$rowid		Id of line
2498
	 * 	@param	int		$rang		Position
2499
	 * 	@param	int		$max		Max
2500
	 * 	@return	void
2501
	 */
2502
	public function updateLineDown($rowid, $rang, $max)
2503
	{
2504
		if ($rang < $max)
2505
		{
2506
			$fieldposition = 'rang';
2507
			if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2508
2509
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2510
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2511
			$sql.= ' AND rang = '.($rang+1);
2512
			if ($this->db->query($sql) )
2513
			{
2514
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang+1);
2515
				$sql.= ' WHERE rowid = '.$rowid;
2516
				if (! $this->db->query($sql) )
2517
				{
2518
					dol_print_error($this->db);
2519
				}
2520
			}
2521
			else
2522
			{
2523
				dol_print_error($this->db);
2524
			}
2525
		}
2526
	}
2527
2528
	/**
2529
	 * 	Get position of line (rang)
2530
	 *
2531
	 * 	@param		int		$rowid		Id of line
2532
	 *  @return		int     			Value of rang in table of lines
2533
	 */
2534
	public function getRangOfLine($rowid)
2535
	{
2536
		$sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2537
		$sql.= ' WHERE rowid ='.$rowid;
2538
2539
		dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
2540
		$resql = $this->db->query($sql);
2541
		if ($resql)
2542
		{
2543
			$row = $this->db->fetch_row($resql);
2544
			return $row[0];
2545
		}
2546
	}
2547
2548
	/**
2549
	 * 	Get rowid of the line relative to its position
2550
	 *
2551
	 * 	@param		int		$rang		Rang value
2552
	 *  @return     int     			Rowid of the line
2553
	 */
2554
	public function getIdOfLine($rang)
2555
	{
2556
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2557
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2558
		$sql.= ' AND rang = '.$rang;
2559
		$resql = $this->db->query($sql);
2560
		if ($resql)
2561
		{
2562
			$row = $this->db->fetch_row($resql);
2563
			return $row[0];
2564
		}
2565
	}
2566
2567
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2568
	/**
2569
	 * 	Get max value used for position of line (rang)
2570
	 *
2571
	 * 	@param		int		$fk_parent_line		Parent line id
2572
	 *  @return     int  			   			Max value of rang in table of lines
2573
	 */
2574
	public function line_max($fk_parent_line = 0)
2575
	{
2576
        // phpcs:enable
2577
		// Search the last rang with fk_parent_line
2578
		if ($fk_parent_line)
2579
		{
2580
			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2581
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2582
			$sql.= ' AND fk_parent_line = '.$fk_parent_line;
2583
2584
			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2585
			$resql = $this->db->query($sql);
2586
			if ($resql)
2587
			{
2588
				$row = $this->db->fetch_row($resql);
2589
				if (! empty($row[0]))
2590
				{
2591
					return $row[0];
2592
				}
2593
				else
2594
				{
2595
					return $this->getRangOfLine($fk_parent_line);
2596
				}
2597
			}
2598
		}
2599
		// If not, search the last rang of element
2600
		else
2601
		{
2602
			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2603
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2604
2605
			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2606
			$resql = $this->db->query($sql);
2607
			if ($resql)
2608
			{
2609
				$row = $this->db->fetch_row($resql);
2610
				return $row[0];
2611
			}
2612
		}
2613
	}
2614
2615
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2616
	/**
2617
	 *  Update external ref of element
2618
	 *
2619
	 *  @param      string		$ref_ext	Update field ref_ext
2620
	 *  @return     int      		   		<0 if KO, >0 if OK
2621
	 */
2622
	public function update_ref_ext($ref_ext)
2623
	{
2624
        // phpcs:enable
2625
		if (! $this->table_element)
2626
		{
2627
			dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
2628
			return -1;
2629
		}
2630
2631
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2632
		$sql.= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
2633
		$sql.= " WHERE ".(isset($this->table_rowid)?$this->table_rowid:'rowid')." = ". $this->id;
2634
2635
		dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
2636
		if ($this->db->query($sql))
2637
		{
2638
			$this->ref_ext = $ref_ext;
2639
			return 1;
2640
		}
2641
		else
2642
		{
2643
			$this->error=$this->db->error();
2644
			return -1;
2645
		}
2646
	}
2647
2648
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2649
	/**
2650
	 *  Update note of element
2651
	 *
2652
	 *  @param      string		$note		New value for note
2653
	 *  @param		string		$suffix		'', '_public' or '_private'
2654
	 *  @return     int      		   		<0 if KO, >0 if OK
2655
	 */
2656
	public function update_note($note, $suffix = '')
2657
	{
2658
        // phpcs:enable
2659
		global $user;
2660
2661
		if (! $this->table_element)
2662
		{
2663
			$this->error='update_note was called on objet with property table_element not defined';
2664
			dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
2665
			return -1;
2666
		}
2667
		if (! in_array($suffix, array('','_public','_private')))
2668
		{
2669
			$this->error='update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
2670
			dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
2671
			return -2;
2672
		}
2673
		// Special cas
2674
		//var_dump($this->table_element);exit;
2675
		if ($this->table_element == 'product') $suffix='';
2676
2677
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2678
		$sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL");
2679
		$sql.= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))?"fk_user_mod":"fk_user_modif")." = ".$user->id;
2680
		$sql.= " WHERE rowid =". $this->id;
2681
2682
		dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
2683
		if ($this->db->query($sql))
2684
		{
2685
			if ($suffix == '_public') $this->note_public = $note;
2686
			elseif ($suffix == '_private') $this->note_private = $note;
2687
			else
2688
			{
2689
				$this->note = $note;      // deprecated
2690
				$this->note_private = $note;
2691
			}
2692
			return 1;
2693
		}
2694
		else
2695
		{
2696
			$this->error=$this->db->lasterror();
2697
			return -1;
2698
		}
2699
	}
2700
2701
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2702
	/**
2703
	 * 	Update public note (kept for backward compatibility)
2704
	 *
2705
	 * @param      string		$note		New value for note
2706
	 * @return     int      		   		<0 if KO, >0 if OK
2707
	 * @deprecated
2708
	 * @see update_note()
2709
	 */
2710
	public function update_note_public($note)
2711
	{
2712
        // phpcs:enable
2713
		return $this->update_note($note, '_public');
2714
	}
2715
2716
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2717
	/**
2718
	 *	Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
2719
	 *  Must be called at end of methods addline or updateline.
2720
	 *
2721
	 *	@param	int		$exclspec          	>0 = Exclude special product (product_type=9)
2722
	 *  @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
2723
	 *  @param	int		$nodatabaseupdate	1=Do not update database. Update only properties of object.
2724
	 *  @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.
2725
	 *	@return	int    			           	<0 if KO, >0 if OK
2726
	 */
2727
	public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null)
2728
	{
2729
        // phpcs:enable
2730
		global $conf, $hookmanager, $action;
2731
2732
		// Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
2733
		$MODULE = "";
2734
		if ($this->element == 'propal')
2735
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
2736
		elseif ($this->element == 'order')
2737
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
2738
		elseif ($this->element == 'facture')
2739
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
2740
		elseif ($this->element == 'facture_fourn')
2741
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
2742
		elseif ($this->element == 'order_supplier')
2743
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
2744
		elseif ($this->element == 'supplier_proposal')
2745
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
2746
2747
		if (! empty($MODULE)) {
2748
			if (! empty($conf->global->$MODULE)) {
2749
				$modsactivated = explode(',', $conf->global->$MODULE);
2750
				foreach ($modsactivated as $mod) {
2751
					if ($conf->$mod->enabled)
2752
						return 1; // update was disabled by specific setup
2753
				}
2754
			}
2755
		}
2756
2757
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2758
2759
		if ($roundingadjust == '-1') $roundingadjust='auto';	// For backward compatibility
2760
2761
		$forcedroundingmode=$roundingadjust;
2762
		if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode=$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
2763
		elseif ($forcedroundingmode == 'auto') $forcedroundingmode='0';
2764
2765
		$error=0;
2766
2767
		$multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
2768
2769
		// Define constants to find lines to sum
2770
		$fieldtva='total_tva';
2771
		$fieldlocaltax1='total_localtax1';
2772
		$fieldlocaltax2='total_localtax2';
2773
		$fieldup='subprice';
2774
		if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
2775
		{
2776
			$fieldtva='tva';
2777
			$fieldup='pu_ht';
2778
		}
2779
		if ($this->element == 'expensereport')
2780
		{
2781
			$fieldup='value_unit';
2782
		}
2783
2784
		$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,';
2785
		$sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
2786
			if ($this->table_element_line == 'facturedet') $sql.= ', situation_percent';
2787
			$sql.= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
2788
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2789
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2790
		if ($exclspec)
2791
		{
2792
			$product_field='product_type';
2793
			if ($this->table_element_line == 'contratdet') $product_field='';    // contratdet table has no product_type field
2794
			if ($product_field) $sql.= ' AND '.$product_field.' <> 9';
2795
		}
2796
		$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
2797
2798
		dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2799
		$resql = $this->db->query($sql);
2800
		if ($resql)
2801
		{
2802
			$this->total_ht  = 0;
2803
			$this->total_tva = 0;
2804
			$this->total_localtax1 = 0;
2805
			$this->total_localtax2 = 0;
2806
			$this->total_ttc = 0;
2807
			$total_ht_by_vats  = array();
2808
			$total_tva_by_vats = array();
2809
			$total_ttc_by_vats = array();
2810
			$this->multicurrency_total_ht	= 0;
2811
			$this->multicurrency_total_tva	= 0;
2812
			$this->multicurrency_total_ttc	= 0;
2813
2814
			$num = $this->db->num_rows($resql);
2815
			$i = 0;
2816
			while ($i < $num)
2817
			{
2818
				$obj = $this->db->fetch_object($resql);
2819
2820
				// Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
2821
				$parameters=array('fk_element' => $obj->rowid);
2822
				$reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2823
2824
				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'
2825
				{
2826
					$localtax_array=array($obj->localtax1_type,$obj->localtax1_tx,$obj->localtax2_type,$obj->localtax2_tx);
2827
					$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 2727 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...
2828
					$diff=price2num($tmpcal[1] - $obj->total_tva, 'MT', 1);
2829
					if ($diff)
2830
					{
2831
						$sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid;
2832
						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);
2833
								$resqlfix=$this->db->query($sqlfix);
2834
								if (! $resqlfix) dol_print_error($this->db, 'Failed to update line');
2835
								$obj->total_tva = $tmpcal[1];
2836
								$obj->total_ttc = $tmpcal[2];
2837
						//
2838
					}
2839
				}
2840
2841
				$this->total_ht        += $obj->total_ht;		// The field visible at end of line detail
2842
				$this->total_tva       += $obj->total_tva;
2843
				$this->total_localtax1 += $obj->total_localtax1;
2844
				$this->total_localtax2 += $obj->total_localtax2;
2845
				$this->total_ttc       += $obj->total_ttc;
2846
				$this->multicurrency_total_ht        += $obj->multicurrency_total_ht;		// The field visible at end of line detail
2847
				$this->multicurrency_total_tva       += $obj->multicurrency_total_tva;
2848
				$this->multicurrency_total_ttc       += $obj->multicurrency_total_ttc;
2849
2850
				if (! isset($total_ht_by_vats[$obj->vatrate]))  $total_ht_by_vats[$obj->vatrate]=0;
2851
				if (! isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate]=0;
2852
				if (! isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate]=0;
2853
				$total_ht_by_vats[$obj->vatrate]  += $obj->total_ht;
2854
				$total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
2855
				$total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
2856
2857
				if ($forcedroundingmode == '1')	// Check if we need adjustement onto line for vat. TODO This works on the company currency but not on multicurrency
2858
				{
2859
					$tmpvat=price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
2860
					$diff=price2num($total_tva_by_vats[$obj->vatrate]-$tmpvat, 'MT', 1);
2861
					//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";
2862
					if ($diff)
2863
					{
2864
						if (abs($diff) > 0.1) { dol_syslog('A rounding difference was detected into TOTAL but is too high to be corrected', LOG_WARNING); exit; }
2865
						$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;
2866
						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);
2867
								$resqlfix=$this->db->query($sqlfix);
2868
								if (! $resqlfix) dol_print_error($this->db, 'Failed to update line');
2869
								$this->total_tva -= $diff;
2870
								$this->total_ttc -= $diff;
2871
								$total_tva_by_vats[$obj->vatrate] -= $diff;
2872
								$total_ttc_by_vats[$obj->vatrate] -= $diff;
2873
					}
2874
				}
2875
2876
				$i++;
2877
			}
2878
2879
			// Add revenue stamp to total
2880
			$this->total_ttc       			+= isset($this->revenuestamp)?$this->revenuestamp:0;
2881
			$this->multicurrency_total_ttc  += isset($this->revenuestamp)?($this->revenuestamp * $multicurrency_tx):0;
2882
2883
			// Situations totals
2884
			if ($this->situation_cycle_ref && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE )
2885
			{
2886
				$prev_sits = $this->get_prev_sits();
2887
2888
				foreach ($prev_sits as $sit) {				// $sit is an object Facture loaded with a fetch.
2889
					$this->total_ht -= $sit->total_ht;
2890
					$this->total_tva -= $sit->total_tva;
2891
					$this->total_localtax1 -= $sit->total_localtax1;
2892
					$this->total_localtax2 -= $sit->total_localtax2;
2893
					$this->total_ttc -= $sit->total_ttc;
2894
					$this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
2895
					$this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
2896
					$this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
2897
				}
2898
			}
2899
2900
			$this->db->free($resql);
2901
2902
			// Now update global field total_ht, total_ttc and tva
2903
			$fieldht='total_ht';
2904
			$fieldtva='tva';
2905
			$fieldlocaltax1='localtax1';
2906
			$fieldlocaltax2='localtax2';
2907
			$fieldttc='total_ttc';
2908
			// Specific code for backward compatibility with old field names
2909
			if ($this->element == 'facture' || $this->element == 'facturerec')             $fieldht='total';
2910
			if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='total_tva';
2911
			if ($this->element == 'propal')                                                $fieldttc='total';
2912
			if ($this->element == 'expensereport')                                         $fieldtva='total_tva';
2913
			if ($this->element == 'supplier_proposal')                                     $fieldttc='total';
2914
2915
			if (empty($nodatabaseupdate))
2916
			{
2917
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2918
				$sql .= " ".$fieldht."='".price2num($this->total_ht)."',";
2919
				$sql .= " ".$fieldtva."='".price2num($this->total_tva)."',";
2920
				$sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',";
2921
				$sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',";
2922
				$sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'";
2923
						$sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'";
2924
						$sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'";
2925
						$sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'";
2926
				$sql .= ' WHERE rowid = '.$this->id;
2927
2928
2929
				dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2930
				$resql=$this->db->query($sql);
2931
				if (! $resql)
2932
				{
2933
					$error++;
2934
					$this->error=$this->db->lasterror();
2935
					$this->errors[]=$this->db->lasterror();
2936
				}
2937
			}
2938
2939
			if (! $error)
2940
			{
2941
				return 1;
2942
			}
2943
			else
2944
			{
2945
				return -1;
2946
			}
2947
		}
2948
		else
2949
		{
2950
			dol_print_error($this->db, 'Bad request in update_price');
2951
			return -1;
2952
		}
2953
	}
2954
2955
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2956
	/**
2957
	 *	Add objects linked in llx_element_element.
2958
	 *
2959
	 *	@param		string	$origin		Linked element type
2960
	 *	@param		int		$origin_id	Linked element id
2961
	 *	@return		int					<=0 if KO, >0 if OK
2962
	 *	@see		fetchObjectLinked(), updateObjectLinked(), deleteObjectLinked()
2963
	 */
2964
	public function add_object_linked($origin = null, $origin_id = null)
2965
	{
2966
        // phpcs:enable
2967
		$origin = (! empty($origin) ? $origin : $this->origin);
2968
		$origin_id = (! empty($origin_id) ? $origin_id : $this->origin_id);
2969
2970
		// Special case
2971
		if ($origin == 'order') $origin='commande';
2972
		if ($origin == 'invoice') $origin='facture';
2973
		if ($origin == 'invoice_template') $origin='facturerec';
2974
    	if ($origin == 'supplierorder') $origin='order_supplier';
2975
		$this->db->begin();
2976
2977
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
2978
		$sql.= "fk_source";
2979
		$sql.= ", sourcetype";
2980
		$sql.= ", fk_target";
2981
		$sql.= ", targettype";
2982
		$sql.= ") VALUES (";
2983
		$sql.= $origin_id;
2984
		$sql.= ", '".$this->db->escape($origin)."'";
2985
		$sql.= ", ".$this->id;
2986
		$sql.= ", '".$this->db->escape($this->element)."'";
2987
		$sql.= ")";
2988
2989
		dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG);
2990
		if ($this->db->query($sql))
2991
	  	{
2992
	  		$this->db->commit();
2993
	  		return 1;
2994
	  	}
2995
	  	else
2996
	  	{
2997
	  		$this->error=$this->db->lasterror();
2998
	  		$this->db->rollback();
2999
	  		return 0;
3000
	  	}
3001
	}
3002
3003
	/**
3004
	 *	Fetch array of objects linked to current object (object of enabled modules only). Links are loaded into
3005
	 *		this->linkedObjectsIds array and
3006
	 *		this->linkedObjects array if $loadalsoobjects = 1
3007
	 *  Possible usage for parameters:
3008
	 *  - all parameters empty -> we look all link to current object (current object can be source or target)
3009
	 *  - source id+type -> will get target list linked to source
3010
	 *  - target id+type -> will get source list linked to target
3011
	 *  - source id+type + target type -> will get target list of the type
3012
	 *  - target id+type + target source -> will get source list of the type
3013
	 *
3014
	 *	@param	int		$sourceid			Object source id (if not defined, id of object)
3015
	 *	@param  string	$sourcetype			Object source type (if not defined, element name of object)
3016
	 *	@param  int		$targetid			Object target id (if not defined, id of object)
3017
	 *	@param  string	$targettype			Object target type (if not defined, elemennt name of object)
3018
	 *	@param  string	$clause				'OR' or 'AND' clause used when both source id and target id are provided
3019
	 *  @param  int		$alsosametype		0=Return only links to object that differs from source type. 1=Include also link to objects of same type.
3020
	 *  @param  string	$orderby			SQL 'ORDER BY' clause
3021
	 *  @param	int		$loadalsoobjects	Load also array this->linkedObjects (Use 0 to increase performances)
3022
	 *	@return int							<0 if KO, >0 if OK
3023
	 *  @see	add_object_linked(), updateObjectLinked(), deleteObjectLinked()
3024
	 */
3025
	public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
3026
	{
3027
		global $conf;
3028
3029
		$this->linkedObjectsIds=array();
3030
		$this->linkedObjects=array();
3031
3032
		$justsource=false;
3033
		$justtarget=false;
3034
		$withtargettype=false;
3035
		$withsourcetype=false;
3036
3037
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid))
3038
		{
3039
			$justsource=true;  // the source (id and type) is a search criteria
3040
			if (! empty($targettype)) $withtargettype=true;
3041
		}
3042
		if (! empty($targetid) && ! empty($targettype) && empty($sourceid))
3043
		{
3044
			$justtarget=true;  // the target (id and type) is a search criteria
3045
			if (! empty($sourcetype)) $withsourcetype=true;
3046
		}
3047
3048
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3049
		$targetid = (! empty($targetid) ? $targetid : $this->id);
3050
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3051
		$targettype = (! empty($targettype) ? $targettype : $this->element);
3052
3053
		/*if (empty($sourceid) && empty($targetid))
3054
		 {
3055
		 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
3056
		 return -1;
3057
		 }*/
3058
3059
		// Links between objects are stored in table element_element
3060
		$sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype';
3061
		$sql.= ' FROM '.MAIN_DB_PREFIX.'element_element';
3062
		$sql.= " WHERE ";
3063
		if ($justsource || $justtarget)
3064
		{
3065
			if ($justsource)
3066
			{
3067
				$sql.= "fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."'";
3068
				if ($withtargettype) $sql.= " AND targettype = '".$targettype."'";
3069
			}
3070
			elseif ($justtarget)
3071
			{
3072
				$sql.= "fk_target = ".$targetid." AND targettype = '".$targettype."'";
3073
				if ($withsourcetype) $sql.= " AND sourcetype = '".$sourcetype."'";
3074
			}
3075
		}
3076
		else
3077
		{
3078
			$sql.= "(fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."')";
3079
			$sql.= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$targettype."')";
3080
		}
3081
		$sql .= ' ORDER BY '.$orderby;
3082
3083
		dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3084
		$resql = $this->db->query($sql);
3085
		if ($resql)
3086
		{
3087
			$num = $this->db->num_rows($resql);
3088
			$i = 0;
3089
			while ($i < $num)
3090
			{
3091
				$obj = $this->db->fetch_object($resql);
3092
				if ($justsource || $justtarget)
3093
				{
3094
					if ($justsource)
3095
					{
3096
						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3097
					}
3098
					elseif ($justtarget)
3099
					{
3100
						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3101
					}
3102
				}
3103
				else
3104
				{
3105
					if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype)
3106
					{
3107
						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3108
					}
3109
					if ($obj->fk_target == $targetid && $obj->targettype == $targettype)
3110
					{
3111
						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3112
					}
3113
				}
3114
				$i++;
3115
			}
3116
3117
			if (! empty($this->linkedObjectsIds))
3118
			{
3119
				$tmparray = $this->linkedObjectsIds;
3120
				foreach($tmparray as $objecttype => $objectids)       // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
3121
				{
3122
					// Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
3123
					$module = $element = $subelement = $objecttype;
3124
					if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
3125
						&& preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs))
3126
					{
3127
						$module = $element = $regs[1];
3128
						$subelement = $regs[2];
3129
					}
3130
3131
					$classpath = $element.'/class';
3132
					// To work with non standard classpath or module name
3133
					if ($objecttype == 'facture')			{
3134
						$classpath = 'compta/facture/class';
3135
					}
3136
					elseif ($objecttype == 'facturerec')			{
3137
						$classpath = 'compta/facture/class'; $module = 'facture';
3138
					}
3139
					elseif ($objecttype == 'propal')			{
3140
						$classpath = 'comm/propal/class';
3141
					}
3142
					elseif ($objecttype == 'supplier_proposal')			{
3143
						$classpath = 'supplier_proposal/class';
3144
					}
3145
					elseif ($objecttype == 'shipping')			{
3146
						$classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon';
3147
					}
3148
					elseif ($objecttype == 'delivery')			{
3149
						$classpath = 'livraison/class'; $subelement = 'livraison'; $module = 'livraison_bon';
3150
					}
3151
					elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier')	{
3152
						$classpath = 'fourn/class'; $module = 'fournisseur';
3153
					}
3154
					elseif ($objecttype == 'fichinter')			{
3155
						$classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter';
3156
					}
3157
					elseif ($objecttype == 'subscription')			{
3158
						$classpath = 'adherents/class'; $module = 'adherent';
3159
					}
3160
3161
					// Set classfile
3162
					$classfile = strtolower($subelement); $classname = ucfirst($subelement);
3163
3164
					if ($objecttype == 'order') {
3165
						$classfile = 'commande'; $classname = 'Commande';
3166
					}
3167
					elseif ($objecttype == 'invoice_supplier') {
3168
						$classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur';
3169
					}
3170
					elseif ($objecttype == 'order_supplier')   {
3171
						$classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur';
3172
					}
3173
					elseif ($objecttype == 'supplier_proposal')   {
3174
						$classfile = 'supplier_proposal'; $classname = 'SupplierProposal';
3175
					}
3176
					elseif ($objecttype == 'facturerec')   {
3177
						$classfile = 'facture-rec'; $classname = 'FactureRec';
3178
					}
3179
					elseif ($objecttype == 'subscription')   {
3180
						$classfile = 'subscription'; $classname = 'Subscription';
3181
					}
3182
3183
					// Here $module, $classfile and $classname are set
3184
					if ($conf->$module->enabled && (($element != $this->element) || $alsosametype))
3185
					{
3186
						if ($loadalsoobjects)
3187
						{
3188
							dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
3189
							//print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
3190
							if (class_exists($classname))
3191
							{
3192
								foreach($objectids as $i => $objectid)	// $i is rowid into llx_element_element
3193
								{
3194
									$object = new $classname($this->db);
3195
									$ret = $object->fetch($objectid);
3196
									if ($ret >= 0)
3197
									{
3198
										$this->linkedObjects[$objecttype][$i] = $object;
3199
									}
3200
								}
3201
							}
3202
						}
3203
					}
3204
					else
3205
					{
3206
						unset($this->linkedObjectsIds[$objecttype]);
3207
					}
3208
				}
3209
			}
3210
			return 1;
3211
		}
3212
		else
3213
		{
3214
			dol_print_error($this->db);
3215
			return -1;
3216
		}
3217
	}
3218
3219
	/**
3220
	 *	Update object linked of a current object
3221
	 *
3222
	 *	@param	int		$sourceid		Object source id
3223
	 *	@param  string	$sourcetype		Object source type
3224
	 *	@param  int		$targetid		Object target id
3225
	 *	@param  string	$targettype		Object target type
3226
	 *	@return							int	>0 if OK, <0 if KO
3227
	 *	@see	add_object_linked(), fetObjectLinked(), deleteObjectLinked()
3228
	 */
3229
	public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '')
3230
	{
3231
		$updatesource=false;
3232
		$updatetarget=false;
3233
3234
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource=true;
3235
		elseif (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $updatetarget=true;
3236
3237
		$sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET ";
3238
		if ($updatesource)
3239
		{
3240
			$sql.= "fk_source = ".$sourceid;
3241
			$sql.= ", sourcetype = '".$this->db->escape($sourcetype)."'";
3242
			$sql.= " WHERE fk_target = ".$this->id;
3243
			$sql.= " AND targettype = '".$this->db->escape($this->element)."'";
3244
		}
3245
		elseif ($updatetarget)
3246
		{
3247
			$sql.= "fk_target = ".$targetid;
3248
			$sql.= ", targettype = '".$this->db->escape($targettype)."'";
3249
			$sql.= " WHERE fk_source = ".$this->id;
3250
			$sql.= " AND sourcetype = '".$this->db->escape($this->element)."'";
3251
		}
3252
3253
		dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG);
3254
		if ($this->db->query($sql))
3255
		{
3256
			return 1;
3257
		}
3258
		else
3259
		{
3260
			$this->error=$this->db->lasterror();
3261
			return -1;
3262
		}
3263
	}
3264
3265
	/**
3266
	 *	Delete all links between an object $this
3267
	 *
3268
	 *	@param	int		$sourceid		Object source id
3269
	 *	@param  string	$sourcetype		Object source type
3270
	 *	@param  int		$targetid		Object target id
3271
	 *	@param  string	$targettype		Object target type
3272
	 *  @param	int		$rowid			Row id of line to delete. If defined, other parameters are not used.
3273
	 *	@return     					int	>0 if OK, <0 if KO
3274
	 *	@see	add_object_linked, updateObjectLinked, fetchObjectLinked
3275
	 */
3276
	public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = '')
3277
	{
3278
		$deletesource=false;
3279
		$deletetarget=false;
3280
3281
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource=true;
3282
		elseif (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $deletetarget=true;
3283
3284
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3285
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3286
		$targetid = (! empty($targetid) ? $targetid : $this->id);
3287
		$targettype = (! empty($targettype) ? $targettype : $this->element);
3288
3289
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element";
3290
		$sql.= " WHERE";
3291
		if ($rowid > 0)
3292
		{
3293
			$sql.=" rowid = ".$rowid;
3294
		}
3295
		else
3296
		{
3297
			if ($deletesource)
3298
			{
3299
				$sql.= " fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3300
				$sql.= " AND fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."'";
3301
			}
3302
			elseif ($deletetarget)
3303
			{
3304
				$sql.= " fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'";
3305
				$sql.= " AND fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."'";
3306
			}
3307
			else
3308
			{
3309
				$sql.= " (fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."')";
3310
				$sql.= " OR";
3311
				$sql.= " (fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."')";
3312
			}
3313
		}
3314
3315
		dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG);
3316
		if ($this->db->query($sql))
3317
		{
3318
			return 1;
3319
		}
3320
		else
3321
		{
3322
			$this->error=$this->db->lasterror();
3323
			$this->errors[]=$this->error;
3324
			return -1;
3325
		}
3326
	}
3327
3328
	/**
3329
	 *      Set status of an object
3330
	 *
3331
	 *      @param	int		$status			Status to set
3332
	 *      @param	int		$elementId		Id of element to force (use this->id by default)
3333
	 *      @param	string	$elementType	Type of element to force (use this->table_element by default)
3334
	 *      @param	string	$trigkey		Trigger key to use for trigger
3335
	 *      @return int						<0 if KO, >0 if OK
3336
	 */
3337
	public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '')
3338
	{
3339
		global $user,$langs,$conf;
3340
3341
		$savElementId=$elementId;  // To be used later to know if we were using the method using the id of this or not.
3342
3343
		$elementId = (!empty($elementId)?$elementId:$this->id);
3344
		$elementTable = (!empty($elementType)?$elementType:$this->table_element);
3345
3346
		$this->db->begin();
3347
3348
		$fieldstatus="fk_statut";
3349
		if ($elementTable == 'facture_rec') $fieldstatus="suspended";
3350
		if ($elementTable == 'mailing') $fieldstatus="statut";
3351
		if ($elementTable == 'cronjob') $fieldstatus="status";
3352
		if ($elementTable == 'user') $fieldstatus="statut";
3353
		if ($elementTable == 'expensereport') $fieldstatus="fk_statut";
3354
		if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status";
3355
3356
		$sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable;
3357
		$sql.= " SET ".$fieldstatus." = ".$status;
3358
		// If status = 1 = validated, update also fk_user_valid
3359
		if ($status == 1 && $elementTable == 'expensereport') $sql.=", fk_user_valid = ".$user->id;
3360
		$sql.= " WHERE rowid=".$elementId;
3361
3362
		dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
3363
		if ($this->db->query($sql))
3364
		{
3365
			$error = 0;
3366
3367
			// Try autoset of trigkey
3368
			if (empty($trigkey))
3369
			{
3370
				if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_SIGN';   // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
3371
				if ($this->element == 'supplier_proposal' && $status == 3) $trigkey='SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
3372
				if ($this->element == 'supplier_proposal' && $status == 4) $trigkey='SUPPLIER_PROPOSAL_CLOSE';  // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
3373
				if ($this->element == 'fichinter' && $status == 3) $trigkey='FICHINTER_CLASSIFY_DONE';
3374
				if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED';
3375
				if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED';
3376
			}
3377
3378
			if ($trigkey)
3379
			{
3380
				// Appel des triggers
3381
				include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
3382
				$interface=new Interfaces($this->db);
3383
				$result=$interface->run_triggers($trigkey, $this, $user, $langs, $conf);
3384
				if ($result < 0) {
3385
					$error++; $this->errors=$interface->errors;
3386
				}
3387
				// Fin appel triggers
3388
			}
3389
3390
			if (! $error)
3391
			{
3392
				$this->db->commit();
3393
3394
				if (empty($savElementId))    // If the element we update was $this (so $elementId is null)
3395
				{
3396
					$this->statut = $status;
3397
					$this->status = $status;
3398
				}
3399
3400
				return 1;
3401
			}
3402
			else
3403
			{
3404
				$this->db->rollback();
3405
				dol_syslog(get_class($this)."::setStatus ".$this->error, LOG_ERR);
3406
				return -1;
3407
			}
3408
		}
3409
		else
3410
		{
3411
			$this->error=$this->db->lasterror();
3412
			$this->db->rollback();
3413
			return -1;
3414
		}
3415
	}
3416
3417
3418
	/**
3419
	 *  Load type of canvas of an object if it exists
3420
	 *
3421
	 *  @param      int		$id     Record id
3422
	 *  @param      string	$ref    Record ref
3423
	 *  @return		int				<0 if KO, 0 if nothing done, >0 if OK
3424
	 */
3425
	public function getCanvas($id = 0, $ref = '')
3426
	{
3427
		global $conf;
3428
3429
		if (empty($id) && empty($ref)) return 0;
3430
		if (! empty($conf->global->MAIN_DISABLE_CANVAS)) return 0;    // To increase speed. Not enabled by default.
3431
3432
		// Clean parameters
3433
		$ref = trim($ref);
3434
3435
		$sql = "SELECT rowid, canvas";
3436
		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
3437
		$sql.= " WHERE entity IN (".getEntity($this->element).")";
3438
		if (! empty($id))  $sql.= " AND rowid = ".$id;
3439
		if (! empty($ref)) $sql.= " AND ref = '".$this->db->escape($ref)."'";
3440
3441
		$resql = $this->db->query($sql);
3442
		if ($resql)
3443
		{
3444
			$obj = $this->db->fetch_object($resql);
3445
			if ($obj)
3446
			{
3447
				$this->canvas   = $obj->canvas;
3448
				return 1;
3449
			}
3450
			else return 0;
3451
		}
3452
		else
3453
		{
3454
			dol_print_error($this->db);
3455
			return -1;
3456
		}
3457
	}
3458
3459
3460
	/**
3461
	 * 	Get special code of a line
3462
	 *
3463
	 * 	@param	int		$lineid		Id of line
3464
	 * 	@return	int					Special code
3465
	 */
3466
	public function getSpecialCode($lineid)
3467
	{
3468
		$sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line;
3469
		$sql.= ' WHERE rowid = '.$lineid;
3470
		$resql = $this->db->query($sql);
3471
		if ($resql)
3472
		{
3473
			$row = $this->db->fetch_row($resql);
3474
			return $row[0];
3475
		}
3476
	}
3477
3478
	/**
3479
	 *  Function to check if an object is used by others.
3480
	 *  Check is done into this->childtables. There is no check into llx_element_element.
3481
	 *
3482
	 *  @param	int		$id			Force id of object
3483
	 *  @return	int					<0 if KO, 0 if not used, >0 if already used
3484
	 */
3485
	public function isObjectUsed($id = 0)
3486
	{
3487
		global $langs;
3488
3489
		if (empty($id)) $id=$this->id;
3490
3491
		// Check parameters
3492
		if (! isset($this->childtables) || ! is_array($this->childtables) || count($this->childtables) == 0)
3493
		{
3494
			dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
3495
			return -1;
3496
		}
3497
3498
		$arraytoscan = $this->childtables;
3499
		// For backward compatibility, we check if array is old format array('table1', 'table2', ...)
3500
		$tmparray=array_keys($this->childtables);
3501
		if (is_numeric($tmparray[0]))
3502
		{
3503
			$arraytoscan = array_flip($this->childtables);
3504
		}
3505
3506
		// Test if child exists
3507
		$haschild=0;
3508
		foreach($arraytoscan as $table => $elementname)
3509
		{
3510
			//print $id.'-'.$table.'-'.$elementname.'<br>';
3511
			// Check if third party can be deleted
3512
			$sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table;
3513
			$sql.= " WHERE ".$this->fk_element." = ".$id;
3514
			$resql=$this->db->query($sql);
3515
			if ($resql)
3516
			{
3517
				$obj=$this->db->fetch_object($resql);
3518
				if ($obj->nb > 0)
3519
				{
3520
					$langs->load("errors");
3521
					//print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
3522
					$haschild += $obj->nb;
3523
					if (is_numeric($elementname))	// old usage
3524
					{
3525
						$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $table);
3526
					}
3527
					else	// new usage: $elementname=Translation key
3528
					{
3529
						$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $langs->transnoentitiesnoconv($elementname));
3530
					}
3531
					break;    // We found at least one, we stop here
3532
				}
3533
			}
3534
			else
3535
			{
3536
				$this->errors[]=$this->db->lasterror();
3537
				return -1;
3538
			}
3539
		}
3540
		if ($haschild > 0)
3541
		{
3542
			$this->errors[]="ErrorRecordHasChildren";
3543
			return $haschild;
3544
		}
3545
		else return 0;
3546
	}
3547
3548
	/**
3549
	 *  Function to say how many lines object contains
3550
	 *
3551
	 *	@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
3552
	 *  @return	int						<0 if KO, 0 if no predefined products, nb of lines with predefined products if found
3553
	 */
3554
	public function hasProductsOrServices($predefined = -1)
3555
	{
3556
		$nb=0;
3557
3558
		foreach($this->lines as $key => $val)
3559
		{
3560
			$qualified=0;
3561
			if ($predefined == -1) $qualified=1;
3562
			if ($predefined == 1 && $val->fk_product > 0) $qualified=1;
3563
			if ($predefined == 0 && $val->fk_product <= 0) $qualified=1;
3564
			if ($predefined == 2 && $val->fk_product > 0 && $val->product_type==0) $qualified=1;
3565
			if ($predefined == 3 && $val->fk_product > 0 && $val->product_type==1) $qualified=1;
3566
			if ($qualified) $nb++;
3567
		}
3568
		dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
3569
		return $nb;
3570
	}
3571
3572
	/**
3573
	 * Function that returns the total amount HT of discounts applied for all lines.
3574
	 *
3575
	 * @return 	float
3576
	 */
3577
	public function getTotalDiscount()
3578
	{
3579
		$total_discount=0.00;
3580
3581
		$sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
3582
		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element."det";
3583
		$sql.= " WHERE ".$this->fk_element." = ".$this->id;
3584
3585
		dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
3586
		$resql = $this->db->query($sql);
3587
		if ($resql)
3588
		{
3589
			$num=$this->db->num_rows($resql);
3590
			$i=0;
3591
			while ($i < $num)
3592
			{
3593
				$obj = $this->db->fetch_object($resql);
3594
3595
				$pu_ht = $obj->pu_ht;
3596
				$qty= $obj->qty;
3597
				$total_ht = $obj->total_ht;
3598
3599
				$total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
3600
				$total_discount += $total_discount_line;
3601
3602
				$i++;
3603
			}
3604
		}
3605
3606
		//print $total_discount; exit;
3607
		return price2num($total_discount);
3608
	}
3609
3610
3611
	/**
3612
	 * Return into unit=0, the calculated total of weight and volume of all lines * qty
3613
	 * Calculate by adding weight and volume of each product line, so properties ->volume/volume_units/weight/weight_units must be loaded on line.
3614
	 *
3615
	 * @return  array                           array('weight'=>...,'volume'=>...)
3616
	 */
3617
	public function getTotalWeightVolume()
3618
	{
3619
		$totalWeight = 0;
3620
		$totalVolume = 0;
3621
		// defined for shipment only
3622
		$totalOrdered = '';
3623
		// defined for shipment only
3624
		$totalToShip = '';
3625
3626
		foreach ($this->lines as $line)
3627
		{
3628
			if (isset($line->qty_asked))
3629
			{
3630
				if (empty($totalOrdered)) $totalOrdered=0;  // Avoid warning because $totalOrdered is ''
3631
				$totalOrdered+=$line->qty_asked;    // defined for shipment only
3632
			}
3633
			if (isset($line->qty_shipped))
3634
			{
3635
				if (empty($totalToShip)) $totalToShip=0;    // Avoid warning because $totalToShip is ''
3636
				$totalToShip+=$line->qty_shipped;   // defined for shipment only
3637
            }elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty))
3638
            {
3639
                if (empty($totalToShip)) $totalToShip=0;
3640
                $totalToShip+=$line->qty;   // defined for reception only
3641
			}
3642
3643
			// Define qty, weight, volume, weight_units, volume_units
3644
			if ($this->element == 'shipping') {
3645
				// for shipments
3646
				$qty = $line->qty_shipped ? $line->qty_shipped : 0;
3647
			}
3648
			else {
3649
				$qty = $line->qty ? $line->qty : 0;
3650
			}
3651
3652
			$weight = $line->weight ? $line->weight : 0;
3653
            ($weight==0 && !empty($line->product->weight))? $weight=$line->product->weight: 0;
3654
			$volume = $line->volume ? $line->volume : 0;
3655
			($volume==0 && !empty($line->product->volume))? $volume=$line->product->volume: 0;
3656
3657
			$weight_units=$line->weight_units;
3658
			($weight_units==0 && !empty($line->product->weight_units))? $weight_units=$line->product->weight_units: 0;
3659
			$volume_units=$line->volume_units;
3660
			($volume_units==0 && !empty($line->product->volume_units))? $volume_units=$line->product->volume_units: 0;
3661
3662
			$weightUnit=0;
3663
			$volumeUnit=0;
3664
			if (! empty($weight_units)) $weightUnit = $weight_units;
3665
			if (! empty($volume_units)) $volumeUnit = $volume_units;
3666
3667
			if (empty($totalWeight)) $totalWeight=0;  // Avoid warning because $totalWeight is ''
3668
			if (empty($totalVolume)) $totalVolume=0;  // Avoid warning because $totalVolume is ''
3669
3670
			//var_dump($line->volume_units);
3671
			if ($weight_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3672
			{
3673
				$trueWeightUnit=pow(10, $weightUnit);
3674
				$totalWeight += $weight * $qty * $trueWeightUnit;
3675
			}
3676
			else {
3677
		if ($weight_units == 99) {
3678
			// conversion 1 Pound = 0.45359237 KG
3679
			$trueWeightUnit = 0.45359237;
3680
			$totalWeight += $weight * $qty * $trueWeightUnit;
3681
		} elseif ($weight_units == 98) {
3682
			// conversion 1 Ounce = 0.0283495 KG
3683
			$trueWeightUnit = 0.0283495;
3684
			$totalWeight += $weight * $qty * $trueWeightUnit;
3685
		}
3686
		else
3687
					$totalWeight += $weight * $qty;   // This may be wrong if we mix different units
3688
			}
3689
			if ($volume_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3690
			{
3691
				//print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
3692
				$trueVolumeUnit=pow(10, $volumeUnit);
3693
				//print $line->volume;
3694
				$totalVolume += $volume * $qty * $trueVolumeUnit;
3695
			}
3696
			else
3697
			{
3698
				$totalVolume += $volume * $qty;   // This may be wrong if we mix different units
3699
			}
3700
		}
3701
3702
		return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
3703
	}
3704
3705
3706
	/**
3707
	 *	Set extra parameters
3708
	 *
3709
	 *	@return	int      <0 if KO, >0 if OK
3710
	 */
3711
	public function setExtraParameters()
3712
	{
3713
		$this->db->begin();
3714
3715
		$extraparams = (! empty($this->extraparams) ? json_encode($this->extraparams) : null);
3716
3717
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3718
		$sql.= " SET extraparams = ".(! empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
3719
		$sql.= " WHERE rowid = ".$this->id;
3720
3721
		dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
3722
		$resql = $this->db->query($sql);
3723
		if (! $resql)
3724
		{
3725
			$this->error=$this->db->lasterror();
3726
			$this->db->rollback();
3727
			return -1;
3728
		}
3729
		else
3730
		{
3731
			$this->db->commit();
3732
			return 1;
3733
		}
3734
	}
3735
3736
3737
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3738
	/**
3739
	 *    Return incoterms informations
3740
	 *    TODO Use a cache for label get
3741
	 *
3742
	 *    @return	string	incoterms info
3743
	 */
3744
	public function display_incoterms()
3745
	{
3746
        // phpcs:enable
3747
		$out = '';
3748
		$this->libelle_incoterms = '';
3749
		if (!empty($this->fk_incoterms))
3750
		{
3751
			$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3752
			$result = $this->db->query($sql);
3753
			if ($result)
3754
			{
3755
				$res = $this->db->fetch_object($result);
3756
				$out .= $res->code;
3757
			}
3758
		}
3759
3760
		$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...
3761
3762
		return $out;
3763
	}
3764
3765
	/**
3766
	 *    Return incoterms informations for pdf display
3767
	 *
3768
	 *    @return	string		incoterms info
3769
	 */
3770
	public function getIncotermsForPDF()
3771
	{
3772
		$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3773
		$resql = $this->db->query($sql);
3774
		if ($resql)
3775
		{
3776
			$num = $this->db->num_rows($resql);
3777
			if ($num > 0)
3778
			{
3779
				$res = $this->db->fetch_object($resql);
3780
				return 'Incoterm : '.$res->code.' - '.$this->location_incoterms;
3781
			}
3782
			else
3783
			{
3784
				return '';
3785
			}
3786
		}
3787
		else
3788
		{
3789
			$this->errors[] = $this->db->lasterror();
3790
			return false;
3791
		}
3792
	}
3793
3794
	/**
3795
	 *    Define incoterms values of current object
3796
	 *
3797
	 *    @param	int		$id_incoterm     Id of incoterm to set or '' to remove
3798
	 * 	  @param 	string  $location		 location of incoterm
3799
	 *    @return	int     		<0 if KO, >0 if OK
3800
	 */
3801
	public function setIncoterms($id_incoterm, $location)
3802
	{
3803
		if ($this->id && $this->table_element)
3804
		{
3805
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3806
			$sql.= " SET fk_incoterms = ".($id_incoterm > 0 ? $id_incoterm : "null");
3807
			$sql.= ", location_incoterms = ".($id_incoterm > 0 ? "'".$this->db->escape($location)."'" : "null");
3808
			$sql.= " WHERE rowid = " . $this->id;
3809
			dol_syslog(get_class($this).'::setIncoterms', LOG_DEBUG);
3810
			$resql=$this->db->query($sql);
3811
			if ($resql)
3812
			{
3813
				$this->fk_incoterms = $id_incoterm;
3814
				$this->location_incoterms = $location;
3815
3816
				$sql = 'SELECT libelle FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3817
				$res = $this->db->query($sql);
3818
				if ($res)
3819
				{
3820
					$obj = $this->db->fetch_object($res);
3821
					$this->libelle_incoterms = $obj->libelle;
3822
				}
3823
				return 1;
3824
			}
3825
			else
3826
			{
3827
				$this->errors[] = $this->db->lasterror();
3828
				return -1;
3829
			}
3830
		}
3831
		else return -1;
3832
	}
3833
3834
3835
	// --------------------
3836
	// TODO: All functions here must be redesigned and moved as they are not business functions but output functions
3837
	// --------------------
3838
3839
	/* This is to show add lines */
3840
3841
	/**
3842
	 *	Show add free and predefined products/services form
3843
	 *
3844
	 *  @param	int		        $dateSelector       1=Show also date range input fields
3845
	 *  @param	Societe			$seller				Object thirdparty who sell
3846
	 *  @param	Societe			$buyer				Object thirdparty who buy
3847
	 *	@return	void
3848
	 */
3849
	public function formAddObjectLine($dateSelector, $seller, $buyer)
3850
	{
3851
		global $conf,$user,$langs,$object,$hookmanager;
3852
		global $form,$bcnd,$var;
3853
3854
		// Line extrafield
3855
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3856
		$extrafieldsline = new ExtraFields($this->db);
3857
		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3858
3859
		// Output template part (modules that overwrite templates must declare this into descriptor)
3860
		// Use global variables + $dateSelector + $seller and $buyer
3861
		$dirtpls=array_merge($conf->modules_parts['tpl'], array('/core/tpl'));
3862
		foreach($dirtpls as $reldir)
3863
		{
3864
			$tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
3865
			if (empty($conf->file->strict_mode)) {
3866
				$res=@include $tpl;
3867
			} else {
3868
				$res=include $tpl; // for debug
3869
			}
3870
			if ($res) break;
3871
		}
3872
	}
3873
3874
3875
3876
	/* This is to show array of line of details */
3877
3878
3879
	/**
3880
	 *	Return HTML table for object lines
3881
	 *	TODO Move this into an output class file (htmlline.class.php)
3882
	 *	If lines are into a template, title must also be into a template
3883
	 *	But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
3884
	 *
3885
	 *	@param	string		$action				Action code
3886
	 *	@param  string		$seller            	Object of seller third party
3887
	 *	@param  string  	$buyer             	Object of buyer third party
3888
	 *	@param	int			$selected		   	Object line selected
3889
	 *	@param  int	    	$dateSelector      	1=Show also date range input fields
3890
	 *	@return	void
3891
	 */
3892
	public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0)
3893
	{
3894
		global $conf, $hookmanager, $langs, $user;
3895
		// TODO We should not use global var for this !
3896
		global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
3897
3898
		// Define usemargins
3899
		$usemargins=0;
3900
		if (! empty($conf->margin->enabled) && ! empty($this->element) && in_array($this->element, array('facture','propal','commande'))) $usemargins=1;
3901
3902
		$num = count($this->lines);
3903
3904
		// Line extrafield
3905
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3906
		$extrafieldsline = new ExtraFields($this->db);
3907
		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3908
3909
		$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...
3910
		$reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3911
		if (empty($reshook))
3912
		{
3913
			// Title line
3914
		    print "<thead>\n";
3915
3916
			print '<tr class="liste_titre nodrag nodrop">';
3917
3918
			// Adds a line numbering column
3919
			if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) print '<td class="linecolnum center">&nbsp;</td>';
3920
3921
			// Description
3922
			print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
3923
3924
			if ($this->element == 'supplier_proposal' || $this->element == 'order_supplier' || $this->element == 'invoice_supplier')
3925
			{
3926
				print '<td class="linerefsupplier"><span id="title_fourn_ref">'.$langs->trans("SupplierRef").'</span></td>';
3927
			}
3928
3929
			// VAT
3930
			print '<td class="linecolvat right" style="width: 80px">'.$langs->trans('VAT').'</td>';
3931
3932
			// Price HT
3933
			print '<td class="linecoluht right" style="width: 80px">'.$langs->trans('PriceUHT').'</td>';
3934
3935
			// Multicurrency
3936
			if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf->currency) print '<td class="linecoluht_currency right" style="width: 80px">'.$langs->trans('PriceUHTCurrency', $this->multicurrency_code).'</td>';
3937
3938
			if ($inputalsopricewithtax) print '<td class="right" style="width: 80px">'.$langs->trans('PriceUTTC').'</td>';
3939
3940
			// Qty
3941
			print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
3942
3943
			if($conf->global->PRODUCT_USE_UNITS)
3944
			{
3945
				print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
3946
			}
3947
3948
			// Reduction short
3949
			print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
3950
3951
			if ($this->situation_cycle_ref) {
3952
				print '<td class="linecolcycleref right">' . $langs->trans('Progress') . '</td>';
3953
			}
3954
3955
			if ($usemargins && ! empty($conf->margin->enabled) && empty($user->societe_id))
3956
			{
3957
				if (!empty($user->rights->margins->creer))
3958
				{
3959
					if ($conf->global->MARGIN_TYPE == "1")
3960
						print '<td class="linecolmargin1 margininfos right" style="width: 80px">'.$langs->trans('BuyingPrice').'</td>';
3961
					else
3962
						print '<td class="linecolmargin1 margininfos right" style="width: 80px">'.$langs->trans('CostPrice').'</td>';
3963
				}
3964
3965
				if (! empty($conf->global->DISPLAY_MARGIN_RATES) && $user->rights->margins->liretous)
3966
					print '<td class="linecolmargin2 margininfos right" style="width: 50px">'.$langs->trans('MarginRate').'</td>';
3967
				if (! empty($conf->global->DISPLAY_MARK_RATES) && $user->rights->margins->liretous)
3968
					print '<td class="linecolmargin2 margininfos right" style="width: 50px">'.$langs->trans('MarkRate').'</td>';
3969
			}
3970
3971
			// Total HT
3972
			print '<td class="linecolht right">'.$langs->trans('TotalHTShort').'</td>';
3973
3974
			// Multicurrency
3975
			if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf->currency) print '<td class="linecoltotalht_currency right">'.$langs->trans('TotalHTShortCurrency', $this->multicurrency_code).'</td>';
3976
3977
			if ($outputalsopricetotalwithtax) print '<td class="right" style="width: 80px">'.$langs->trans('TotalTTCShort').'</td>';
3978
3979
			print '<td class="linecoledit"></td>';  // No width to allow autodim
3980
3981
			print '<td class="linecoldelete" style="width: 10px"></td>';
3982
3983
			print '<td class="linecolmove" style="width: 10px"></td>';
3984
3985
			if($action == 'selectlines')
3986
			{
3987
			    print '<td class="linecolcheckall center">';
3988
			    print '<input type="checkbox" class="linecheckboxtoggle" />';
3989
			    print '<script>$(document).ready(function() {$(".linecheckboxtoggle").click(function() {var checkBoxes = $(".linecheckbox");checkBoxes.prop("checked", this.checked);})});</script>';
3990
			    print '</td>';
3991
			}
3992
3993
			print "</tr>\n";
3994
			print "</thead>\n";
3995
		}
3996
3997
		$var = true;
3998
		$i	 = 0;
3999
4000
		print "<tbody>\n";
4001
		foreach ($this->lines as $line)
4002
		{
4003
			//Line extrafield
4004
			$line->fetch_optionals();
4005
4006
			//if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
4007
			if (is_object($hookmanager))   // Old code is commented on preceding line.
4008
			{
4009
				if (empty($line->fk_parent_line))
4010
				{
4011
					$parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
4012
					$reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
4013
				}
4014
				else
4015
				{
4016
					$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);
4017
					$reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
4018
				}
4019
			}
4020
			if (empty($reshook))
4021
			{
4022
				$this->printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafieldsline);
4023
			}
4024
4025
			$i++;
4026
		}
4027
		print "</tbody>\n";
4028
	}
4029
4030
	/**
4031
	 *	Return HTML content of a detail line
4032
	 *	TODO Move this into an output class file (htmlline.class.php)
4033
	 *
4034
	 *	@param	string      $action				GET/POST action
4035
	 *	@param  CommonObjectLine $line		       	Selected object line to output
4036
	 *	@param  string	    $var               	Is it a an odd line (true)
4037
	 *	@param  int		    $num               	Number of line (0)
4038
	 *	@param  int		    $i					I
4039
	 *	@param  int		    $dateSelector      	1=Show also date range input fields
4040
	 *	@param  string	    $seller            	Object of seller third party
4041
	 *	@param  string	    $buyer             	Object of buyer third party
4042
	 *	@param	int			$selected		   	Object line selected
4043
	 *  @param  int			$extrafieldsline	Object of extrafield line attribute
4044
	 *	@return	void
4045
	 */
4046
	public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafieldsline = 0)
4047
	{
4048
		global $conf,$langs,$user,$object,$hookmanager;
4049
		global $form,$bc,$bcdd;
4050
		global $object_rights, $disableedit, $disablemove, $disableremove;   // TODO We should not use global var for this !
4051
4052
		$object_rights = $this->getRights();
4053
4054
		$element=$this->element;
4055
4056
		$text=''; $description=''; $type=0;
4057
4058
		// Show product and description
4059
		$type=(! empty($line->product_type)?$line->product_type:$line->fk_product_type);
4060
		// Try to enhance type detection using date_start and date_end for free lines where type was not saved.
4061
		if (! empty($line->date_start)) $type=1; // deprecated
4062
		if (! empty($line->date_end)) $type=1; // deprecated
4063
4064
		// Ligne en mode visu
4065
		if ($action != 'editline' || $selected != $line->id)
4066
		{
4067
			// Product
4068
			if ($line->fk_product > 0)
4069
			{
4070
				$product_static = new Product($this->db);
4071
				$product_static->fetch($line->fk_product);
4072
4073
				$product_static->ref = $line->ref; //can change ref in hook
4074
				$product_static->label = $line->label; //can change label in hook
4075
				$text=$product_static->getNomUrl(1);
4076
4077
				// Define output language and label
4078
				if (! empty($conf->global->MAIN_MULTILANGS))
4079
				{
4080
					if (! is_object($this->thirdparty))
4081
					{
4082
						dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
4083
						return;
4084
					}
4085
4086
					$prod = new Product($this->db);
4087
					$prod->fetch($line->fk_product);
4088
4089
					$outputlangs = $langs;
4090
					$newlang='';
4091
					if (empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang=GETPOST('lang_id', 'aZ09');
4092
					if (! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang)) $newlang=$this->thirdparty->default_lang;		// For language to language of customer
4093
					if (! empty($newlang))
4094
					{
4095
						$outputlangs = new Translate("", $conf);
4096
						$outputlangs->setDefaultLang($newlang);
4097
					}
4098
4099
					$label = (! empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
4100
				}
4101
				else
4102
				{
4103
					$label = $line->product_label;
4104
				}
4105
4106
				$text.= ' - '.(! empty($line->label)?$line->label:$label);
4107
				$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.
4108
			}
4109
4110
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4111
4112
			// Output template part (modules that overwrite templates must declare this into descriptor)
4113
			// Use global variables + $dateSelector + $seller and $buyer
4114
			$dirtpls=array_merge($conf->modules_parts['tpl'], array('/core/tpl'));
4115
			foreach($dirtpls as $reldir)
4116
			{
4117
				$tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
4118
				if (empty($conf->file->strict_mode)) {
4119
					$res=@include $tpl;
4120
				} else {
4121
					$res=include $tpl; // for debug
4122
				}
4123
				if ($res) break;
4124
			}
4125
		}
4126
4127
		// Ligne en mode update
4128
		if ($this->statut == 0 && $action == 'editline' && $selected == $line->id)
4129
		{
4130
			$label = (! empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
4131
			$placeholder=' placeholder="'.$langs->trans("Label").'"';
4132
4133
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4134
4135
			// Output template part (modules that overwrite templates must declare this into descriptor)
4136
			// Use global variables + $dateSelector + $seller and $buyer
4137
			$dirtpls=array_merge($conf->modules_parts['tpl'], array('/core/tpl'));
4138
			foreach($dirtpls as $reldir)
4139
			{
4140
				$tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
4141
				if (empty($conf->file->strict_mode)) {
4142
					$res=@include $tpl;
4143
				} else {
4144
					$res=include $tpl; // for debug
4145
				}
4146
				if ($res) break;
4147
			}
4148
		}
4149
	}
4150
4151
4152
	/* This is to show array of line of details of source object */
4153
4154
4155
	/**
4156
	 * 	Return HTML table table of source object lines
4157
	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
4158
	 *  If lines are into a template, title must also be into a template
4159
	 *  But for the moment we don't know if it's possible, so we keep the method available on overloaded objects.
4160
	 *
4161
	 *	@param	string		$restrictlist		''=All lines, 'services'=Restrict to services only
4162
	 *  @return	void
4163
	 */
4164
	public function printOriginLinesList($restrictlist = '')
4165
	{
4166
		global $langs, $hookmanager, $conf;
4167
4168
		print '<tr class="liste_titre">';
4169
		print '<td>'.$langs->trans('Ref').'</td>';
4170
		print '<td>'.$langs->trans('Description').'</td>';
4171
		print '<td class="right">'.$langs->trans('VATRate').'</td>';
4172
		print '<td class="right">'.$langs->trans('PriceUHT').'</td>';
4173
		if (!empty($conf->multicurrency->enabled)) print '<td class="right">'.$langs->trans('PriceUHTCurrency').'</td>';
4174
		print '<td class="right">'.$langs->trans('Qty').'</td>';
4175
		if($conf->global->PRODUCT_USE_UNITS)
4176
		{
4177
			print '<td class="left">'.$langs->trans('Unit').'</td>';
4178
		}
4179
		print '<td class="right">'.$langs->trans('ReductionShort').'</td></tr>';
4180
4181
		$var = true;
4182
		$i	 = 0;
4183
4184
		if (! empty($this->lines))
4185
		{
4186
			foreach ($this->lines as $line)
4187
			{
4188
				if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
4189
				{
4190
					if (empty($line->fk_parent_line))
4191
					{
4192
						$parameters=array('line'=>$line,'var'=>$var,'i'=>$i);
4193
						$action='';
4194
						$hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
4195
					}
4196
				}
4197
				else
4198
				{
4199
					$this->printOriginLine($line, $var, $restrictlist);
4200
				}
4201
4202
				$i++;
4203
			}
4204
		}
4205
	}
4206
4207
	/**
4208
	 * 	Return HTML with a line of table array of source object lines
4209
	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
4210
	 *  If lines are into a template, title must also be into a template
4211
	 *  But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
4212
	 *
4213
	 * 	@param	CommonObjectLine	$line				Line
4214
	 * 	@param	string				$var				Var
4215
	 *	@param	string				$restrictlist		''=All lines, 'services'=Restrict to services only (strike line if not)
4216
	 * 	@return	void
4217
	 */
4218
	public function printOriginLine($line, $var, $restrictlist = '')
4219
	{
4220
		global $langs, $conf;
4221
4222
		//var_dump($line);
4223
		if (!empty($line->date_start))
4224
		{
4225
			$date_start=$line->date_start;
4226
		}
4227
		else
4228
		{
4229
			$date_start=$line->date_debut_prevue;
4230
			if ($line->date_debut_reel) $date_start=$line->date_debut_reel;
4231
		}
4232
		if (!empty($line->date_end))
4233
		{
4234
			$date_end=$line->date_end;
4235
		}
4236
		else
4237
		{
4238
			$date_end=$line->date_fin_prevue;
4239
			if ($line->date_fin_reel) $date_end=$line->date_fin_reel;
4240
		}
4241
4242
		$this->tpl['label'] = '';
4243
		if (! empty($line->fk_parent_line)) $this->tpl['label'].= img_picto('', 'rightarrow');
4244
4245
		if (($line->info_bits & 2) == 2)  // TODO Not sure this is used for source object
4246
		{
4247
			$discount=new DiscountAbsolute($this->db);
4248
			$discount->fk_soc = $this->socid;
4249
			$this->tpl['label'].= $discount->getNomUrl(0, 'discount');
4250
		}
4251
		elseif (! empty($line->fk_product))
4252
		{
4253
			$productstatic = new Product($this->db);
4254
			$productstatic->id = $line->fk_product;
4255
			$productstatic->ref = $line->ref;
4256
			$productstatic->type = $line->fk_product_type;
4257
            if(empty($productstatic->ref)){
4258
				$line->fetch_product();
4259
				$productstatic = $line->product;
4260
			}
4261
4262
			$this->tpl['label'].= $productstatic->getNomUrl(1);
4263
			$this->tpl['label'].= ' - '.(! empty($line->label)?$line->label:$line->product_label);
4264
			// Dates
4265
			if ($line->product_type == 1 && ($date_start || $date_end))
4266
			{
4267
				$this->tpl['label'].= get_date_range($date_start, $date_end);
4268
			}
4269
		}
4270
		else
4271
		{
4272
			$this->tpl['label'].= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
4273
			if (!empty($line->desc)) {
4274
				$this->tpl['label'].=$line->desc;
4275
			}else {
4276
				$this->tpl['label'].= ($line->label ? '&nbsp;'.$line->label : '');
4277
			}
4278
4279
			// Dates
4280
			if ($line->product_type == 1 && ($date_start || $date_end))
4281
			{
4282
				$this->tpl['label'].= get_date_range($date_start, $date_end);
4283
			}
4284
		}
4285
4286
		if (! empty($line->desc))
4287
		{
4288
			if ($line->desc == '(CREDIT_NOTE)')  // TODO Not sure this is used for source object
4289
			{
4290
				$discount=new DiscountAbsolute($this->db);
4291
				$discount->fetch($line->fk_remise_except);
4292
				$this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
4293
			}
4294
			elseif ($line->desc == '(DEPOSIT)')  // TODO Not sure this is used for source object
4295
			{
4296
				$discount=new DiscountAbsolute($this->db);
4297
				$discount->fetch($line->fk_remise_except);
4298
				$this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
4299
			}
4300
			elseif ($line->desc == '(EXCESS RECEIVED)')
4301
			{
4302
				$discount=new DiscountAbsolute($this->db);
4303
				$discount->fetch($line->fk_remise_except);
4304
				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
4305
			}
4306
			elseif ($line->desc == '(EXCESS PAID)')
4307
			{
4308
				$discount=new DiscountAbsolute($this->db);
4309
				$discount->fetch($line->fk_remise_except);
4310
				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
4311
			}
4312
			else
4313
			{
4314
				$this->tpl['description'] = dol_trunc($line->desc, 60);
4315
			}
4316
		}
4317
		else
4318
		{
4319
			$this->tpl['description'] = '&nbsp;';
4320
		}
4321
4322
        // VAT Rate
4323
        $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
4324
        $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
4325
        if (! empty($line->vat_src_code) && ! preg_match('/\(/', $this->tpl['vat_rate'])) $this->tpl['vat_rate'].=' ('.$line->vat_src_code.')';
4326
4327
		$this->tpl['price'] = price($line->subprice);
4328
		$this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
4329
		$this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
4330
		if ($conf->global->PRODUCT_USE_UNITS) $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
4331
		$this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
4332
4333
		// Is the line strike or not
4334
		$this->tpl['strike']=0;
4335
		if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) $this->tpl['strike']=1;
4336
4337
		// Output template part (modules that overwrite templates must declare this into descriptor)
4338
		// Use global variables + $dateSelector + $seller and $buyer
4339
		$dirtpls=array_merge($conf->modules_parts['tpl'], array('/core/tpl'));
4340
		foreach($dirtpls as $reldir)
4341
		{
4342
			$tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
4343
			if (empty($conf->file->strict_mode)) {
4344
				$res=@include $tpl;
4345
			} else {
4346
				$res=include $tpl; // for debug
4347
			}
4348
			if ($res) break;
4349
		}
4350
	}
4351
4352
4353
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4354
	/**
4355
	 *	Add resources to the current object : add entry into llx_element_resources
4356
	 *	Need $this->element & $this->id
4357
	 *
4358
	 *	@param		int		$resource_id		Resource id
4359
	 *	@param		string	$resource_type		'resource'
4360
	 *	@param		int		$busy				Busy or not
4361
	 *	@param		int		$mandatory			Mandatory or not
4362
	 *	@return		int							<=0 if KO, >0 if OK
4363
	 */
4364
	public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
4365
	{
4366
        // phpcs:enable
4367
		$this->db->begin();
4368
4369
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources (";
4370
		$sql.= "resource_id";
4371
		$sql.= ", resource_type";
4372
		$sql.= ", element_id";
4373
		$sql.= ", element_type";
4374
		$sql.= ", busy";
4375
		$sql.= ", mandatory";
4376
		$sql.= ") VALUES (";
4377
		$sql.= $resource_id;
4378
		$sql.= ", '".$this->db->escape($resource_type)."'";
4379
		$sql.= ", '".$this->db->escape($this->id)."'";
4380
		$sql.= ", '".$this->db->escape($this->element)."'";
4381
		$sql.= ", '".$this->db->escape($busy)."'";
4382
		$sql.= ", '".$this->db->escape($mandatory)."'";
4383
		$sql.= ")";
4384
4385
		dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
4386
		if ($this->db->query($sql))
4387
		{
4388
			$this->db->commit();
4389
			return 1;
4390
		}
4391
		else
4392
		{
4393
			$this->error=$this->db->lasterror();
4394
			$this->db->rollback();
4395
			return  0;
4396
		}
4397
	}
4398
4399
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4400
	/**
4401
	 *    Delete a link to resource line
4402
	 *
4403
	 *    @param	int		$rowid			Id of resource line to delete
4404
	 *    @param	int		$element		element name (for trigger) TODO: use $this->element into commonobject class
4405
	 *    @param	int		$notrigger		Disable all triggers
4406
	 *    @return   int						>0 if OK, <0 if KO
4407
	 */
4408
	public function delete_resource($rowid, $element, $notrigger = 0)
4409
	{
4410
        // phpcs:enable
4411
		global $user;
4412
4413
		$this->db->begin();
4414
4415
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources";
4416
		$sql.= " WHERE rowid=".$rowid;
4417
4418
		dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
4419
4420
		$resql=$this->db->query($sql);
4421
		if (! $resql)
4422
		{
4423
			$this->error=$this->db->lasterror();
4424
			$this->db->rollback();
4425
			return -1;
4426
		}
4427
		else
4428
		{
4429
			if (! $notrigger)
4430
			{
4431
				$result=$this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
4432
				if ($result < 0) { $this->db->rollback(); return -1; }
4433
			}
4434
			$this->db->commit();
4435
			return 1;
4436
		}
4437
	}
4438
4439
4440
	/**
4441
	 * Overwrite magic function to solve problem of cloning object that are kept as references
4442
	 *
4443
	 * @return void
4444
	 */
4445
	public function __clone()
4446
	{
4447
		// Force a copy of this->lines, otherwise it will point to same object.
4448
		if (isset($this->lines) && is_array($this->lines))
4449
		{
4450
			$nboflines=count($this->lines);
4451
			for($i=0; $i < $nboflines; $i++)
4452
			{
4453
				$this->lines[$i] = clone $this->lines[$i];
4454
			}
4455
		}
4456
	}
4457
4458
	/**
4459
	 * Common function for all objects extending CommonObject for generating documents
4460
	 *
4461
	 * @param 	string 		$modelspath 	Relative folder where generators are placed
4462
	 * @param 	string 		$modele 		Generator to use. Caller must set it to obj->modelpdf or GETPOST('modelpdf') for example.
4463
	 * @param 	Translate 	$outputlangs 	Output language to use
4464
	 * @param 	int 		$hidedetails 	1 to hide details. 0 by default
4465
	 * @param 	int 		$hidedesc 		1 to hide product description. 0 by default
4466
	 * @param 	int 		$hideref 		1 to hide product reference. 0 by default
4467
	 * @param   null|array  $moreparams     Array to provide more information
4468
	 * @return 	int 						>0 if OK, <0 if KO
4469
	 * @see	addFileIntoDatabaseIndex()
4470
	 */
4471
	protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
4472
	{
4473
		global $conf, $langs, $user;
4474
4475
		$srctemplatepath='';
4476
4477
		// Increase limit for PDF build
4478
		$err=error_reporting();
4479
		error_reporting(0);
4480
		@set_time_limit(120);
4481
		error_reporting($err);
4482
4483
		// If selected model is a filename template (then $modele="modelname" or "modelname:filename")
4484
		$tmp=explode(':', $modele, 2);
4485
		if (! empty($tmp[1]))
4486
		{
4487
			$modele=$tmp[0];
4488
			$srctemplatepath=$tmp[1];
4489
		}
4490
4491
		// Search template files
4492
		$file=''; $classname=''; $filefound=0;
4493
		$dirmodels=array('/');
4494
		if (is_array($conf->modules_parts['models'])) $dirmodels=array_merge($dirmodels, $conf->modules_parts['models']);
4495
		foreach($dirmodels as $reldir)
4496
		{
4497
			foreach(array('doc','pdf') as $prefix)
4498
			{
4499
				if (in_array(get_class($this), array('Adherent'))) $file = $prefix."_".$modele.".class.php";     // Member module use prefix_module.class.php
4500
				else $file = $prefix."_".$modele.".modules.php";
4501
4502
				// On verifie l'emplacement du modele
4503
				$file=dol_buildpath($reldir.$modelspath.$file, 0);
4504
				if (file_exists($file))
4505
				{
4506
					$filefound=1;
4507
					$classname=$prefix.'_'.$modele;
4508
					break;
4509
				}
4510
			}
4511
			if ($filefound) break;
4512
		}
4513
4514
		// If generator was found
4515
		if ($filefound)
4516
		{
4517
			global $db;  // Required to solve a conception default in commonstickergenerator.class.php making an include of code using $db
4518
4519
			require_once $file;
4520
4521
			$obj = new $classname($this->db);
4522
4523
			// If generator is ODT, we must have srctemplatepath defined, if not we set it.
4524
			if ($obj->type == 'odt' && empty($srctemplatepath))
4525
			{
4526
				$varfortemplatedir=$obj->scandir;
4527
				if ($varfortemplatedir && ! empty($conf->global->$varfortemplatedir))
4528
				{
4529
					$dirtoscan=$conf->global->$varfortemplatedir;
4530
4531
					$listoffiles=array();
4532
4533
					// Now we add first model found in directories scanned
4534
					$listofdir=explode(',', $dirtoscan);
4535
					foreach($listofdir as $key => $tmpdir)
4536
					{
4537
						$tmpdir=trim($tmpdir);
4538
						$tmpdir=preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
4539
						if (! $tmpdir) { unset($listofdir[$key]); continue; }
4540
						if (is_dir($tmpdir))
4541
						{
4542
							$tmpfiles=dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
4543
							if (count($tmpfiles)) $listoffiles=array_merge($listoffiles, $tmpfiles);
4544
						}
4545
					}
4546
4547
					if (count($listoffiles))
4548
					{
4549
						foreach($listoffiles as $record)
4550
						{
4551
							$srctemplatepath=$record['fullname'];
4552
							break;
4553
						}
4554
					}
4555
				}
4556
4557
				if (empty($srctemplatepath))
4558
				{
4559
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
4560
					return -1;
4561
				}
4562
			}
4563
4564
			if ($obj->type == 'odt' && ! empty($srctemplatepath))
4565
			{
4566
				if (! dol_is_file($srctemplatepath))
4567
				{
4568
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
4569
					return -1;
4570
				}
4571
			}
4572
4573
			// We save charset_output to restore it because write_file can change it if needed for
4574
			// output format that does not support UTF8.
4575
			$sav_charset_output=$outputlangs->charset_output;
4576
4577
			if (in_array(get_class($this), array('Adherent')))
4578
			{
4579
				$arrayofrecords = array();   // The write_file of templates of adherent class need this var
4580
				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams);
4581
			}
4582
			else
4583
			{
4584
				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
4585
			}
4586
			// After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
4587
4588
			if ($resultwritefile > 0)
4589
			{
4590
				$outputlangs->charset_output=$sav_charset_output;
4591
4592
				// We delete old preview
4593
				require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4594
				dol_delete_preview($this);
4595
4596
				// Index file in database
4597
				if (! empty($obj->result['fullpath']))
4598
				{
4599
					$destfull = $obj->result['fullpath'];
4600
					$upload_dir = dirname($destfull);
4601
					$destfile = basename($destfull);
4602
					$rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
4603
4604
					if (! preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir))     // If not a tmp dir
4605
					{
4606
						$filename = basename($destfile);
4607
						$rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
4608
						$rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
4609
4610
						include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
4611
						$ecmfile=new EcmFiles($this->db);
4612
						$result = $ecmfile->fetch(0, '', ($rel_dir?$rel_dir.'/':'').$filename);
4613
4614
						// Set the public "share" key
4615
						$setsharekey = false;
4616
						if ($this->element == 'propal')
4617
						{
4618
							$useonlinesignature = $conf->global->MAIN_FEATURES_LEVEL;	// Replace this with 1 when feature to make online signature is ok
4619
							if ($useonlinesignature) $setsharekey=true;
4620
							if (! empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4621
						}
4622
						if ($this->element == 'commande' && ! empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) {
4623
                            $setsharekey=true;
4624
                        }
4625
						if ($this->element == 'facture' && ! empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) {
4626
                            $setsharekey=true;
4627
                        }
4628
						if ($this->element == 'bank_account' && ! empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) {
4629
                            $setsharekey=true;
4630
                        }
4631
4632
						if ($setsharekey)
4633
						{
4634
							if (empty($ecmfile->share))	// Because object not found or share not set yet
4635
							{
4636
								require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
4637
								$ecmfile->share = getRandomPassword(true);
4638
							}
4639
						}
4640
4641
						if ($result > 0)
4642
						{
4643
							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4644
							$ecmfile->fullpath_orig = '';
4645
							$ecmfile->gen_or_uploaded = 'generated';
4646
							$ecmfile->description = '';    // indexed content
4647
							$ecmfile->keyword = '';        // keyword content
4648
							$result = $ecmfile->update($user);
4649
							if ($result < 0)
4650
							{
4651
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4652
							}
4653
						}
4654
						else
4655
						{
4656
							$ecmfile->entity = $conf->entity;
4657
							$ecmfile->filepath = $rel_dir;
4658
							$ecmfile->filename = $filename;
4659
							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4660
							$ecmfile->fullpath_orig = '';
4661
							$ecmfile->gen_or_uploaded = 'generated';
4662
							$ecmfile->description = '';    // indexed content
4663
							$ecmfile->keyword = '';        // keyword content
4664
							$ecmfile->src_object_type = $this->table_element;
4665
							$ecmfile->src_object_id   = $this->id;
4666
4667
							$result = $ecmfile->create($user);
4668
							if ($result < 0)
4669
							{
4670
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4671
							}
4672
						}
4673
4674
						/*$this->result['fullname']=$destfull;
4675
						$this->result['filepath']=$ecmfile->filepath;
4676
						$this->result['filename']=$ecmfile->filename;*/
4677
						//var_dump($obj->update_main_doc_field);exit;
4678
4679
						// Update the last_main_doc field into main object (if documenent generator has property ->update_main_doc_field set)
4680
						$update_main_doc_field=0;
4681
						if (! empty($obj->update_main_doc_field)) $update_main_doc_field=1;
4682
						if ($update_main_doc_field && ! empty($this->table_element))
4683
						{
4684
							$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath.'/'.$ecmfile->filename)."'";
4685
							$sql.= ' WHERE rowid = '.$this->id;
4686
4687
							$resql = $this->db->query($sql);
4688
							if (! $resql) dol_print_error($this->db);
4689
							else
4690
							{
4691
							    $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
4692
							}
4693
						}
4694
					}
4695
				}
4696
				else
4697
				{
4698
					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);
4699
				}
4700
4701
				// Success in building document. We build meta file.
4702
				dol_meta_create($this);
4703
4704
				return 1;
4705
			}
4706
			else
4707
			{
4708
				$outputlangs->charset_output=$sav_charset_output;
4709
				dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors);
4710
				return -1;
4711
			}
4712
		}
4713
		else
4714
		{
4715
			$this->error=$langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists", $file);
4716
			dol_print_error('', $this->error);
4717
			return -1;
4718
		}
4719
	}
4720
4721
	/**
4722
	 *  Build thumb
4723
	 *  @TODO Move this into files.lib.php
4724
	 *
4725
	 *  @param      string	$file           Path file in UTF8 to original file to create thumbs from.
4726
	 *	@return		void
4727
	 */
4728
	public function addThumbs($file)
4729
	{
4730
		global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality;
4731
4732
		require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';		// This define also $maxwidthsmall, $quality, ...
4733
4734
		$file_osencoded=dol_osencode($file);
4735
		if (file_exists($file_osencoded))
4736
		{
4737
			// Create small thumbs for company (Ratio is near 16/9)
4738
			// Used on logon for example
4739
			vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
4740
4741
			// Create mini thumbs for company (Ratio is near 16/9)
4742
			// Used on menu or for setup page for example
4743
			vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
4744
		}
4745
	}
4746
4747
4748
	/* Functions common to commonobject and commonobjectline */
4749
4750
	/* For default values */
4751
4752
	/**
4753
	 * Return the default value to use for a field when showing the create form of object.
4754
	 * Return values in this order:
4755
	 * 1) If parameter is available into POST, we return it first.
4756
	 * 2) If not but an alternate value was provided as parameter of function, we return it.
4757
	 * 3) If not but a constant $conf->global->OBJECTELEMENT_FIELDNAME is set, we return it (It is better to use the dedicated table).
4758
	 * 4) Return value found into database (TODO No yet implemented)
4759
	 *
4760
	 * @param   string              $fieldname          Name of field
4761
	 * @param   string              $alternatevalue     Alternate value to use
4762
	 * @return  string|string[]                         Default value (can be an array if the GETPOST return an array)
4763
	 **/
4764
	public function getDefaultCreateValueFor($fieldname, $alternatevalue = null)
4765
	{
4766
		global $conf, $_POST;
4767
4768
		// If param here has been posted, we use this value first.
4769
		if (isset($_POST[$fieldname])) return GETPOST($fieldname, 2);
4770
4771
		if (isset($alternatevalue)) return $alternatevalue;
4772
4773
		$newelement=$this->element;
4774
		if ($newelement == 'facture') $newelement='invoice';
4775
		if ($newelement == 'commande') $newelement='order';
4776
		if (empty($newelement))
4777
		{
4778
			dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
4779
			return '';
4780
		}
4781
4782
		$keyforfieldname=strtoupper($newelement.'_DEFAULT_'.$fieldname);
4783
		//var_dump($keyforfieldname);
4784
		if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname;
4785
4786
		// TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
4787
	}
4788
4789
4790
	/* For triggers */
4791
4792
4793
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4794
	/**
4795
	 * Call trigger based on this instance.
4796
	 * Some context information may also be provided into array property this->context.
4797
	 * NB:  Error from trigger are stacked in interface->errors
4798
	 * NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction.
4799
	 *
4800
	 * @param   string    $trigger_name   trigger's name to execute
4801
	 * @param   User      $user           Object user
4802
	 * @return  int                       Result of run_triggers
4803
	 */
4804
    public function call_trigger($trigger_name, $user)
4805
	{
4806
        // phpcs:enable
4807
		global $langs,$conf;
4808
4809
		include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
4810
		$interface=new Interfaces($this->db);
4811
		$result=$interface->run_triggers($trigger_name, $this, $user, $langs, $conf);
4812
4813
		if ($result < 0)
4814
		{
4815
			if (!empty($this->errors))
4816
			{
4817
				$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.
4818
			}
4819
			else
4820
			{
4821
				$this->errors=$interface->errors;
4822
			}
4823
		}
4824
		return $result;
4825
	}
4826
4827
4828
	/* Functions for extrafields */
4829
4830
4831
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4832
	/**
4833
	 *  Function to get extra fields of an object into $this->array_options
4834
	 *  This method is in most cases called by method fetch of objects but you can call it separately.
4835
	 *
4836
	 *  @param	int		$rowid			Id of line. Use the id of object if not defined. Deprecated. Function must be called without parameters.
4837
	 *  @param  array	$optionsArray   Array resulting of call of extrafields->fetch_name_optionals_label(). Deprecated. Function must be called without parameters.
4838
	 *  @return	int						<0 if error, 0 if no values of extrafield to find nor found, 1 if an attribute is found and value loaded
4839
	 */
4840
    public function fetch_optionals($rowid = null, $optionsArray = null)
4841
	{
4842
        // phpcs:enable
4843
		if (empty($rowid)) $rowid=$this->id;
4844
4845
		// To avoid SQL errors. Probably not the better solution though
4846
		if (!$this->table_element) {
4847
			return 0;
4848
		}
4849
4850
		$this->array_options=array();
4851
4852
		if (! is_array($optionsArray))
4853
		{
4854
			// If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
4855
			// TODO Use of existing $extrafield is not yet ready (must mutualize code that use extrafields in form first)
4856
			// global $extrafields;
4857
			//if (! is_object($extrafields))
4858
			//{
4859
				require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4860
				$extrafields = new ExtraFields($this->db);
4861
			//}
4862
4863
			// Load array of extrafields for elementype = $this->table_element
4864
			if (empty($extrafields->attributes[$this->table_element]['loaded']))
4865
			{
4866
				$extrafields->fetch_name_optionals_label($this->table_element);
4867
			}
4868
			$optionsArray = (! empty($extrafields->attributes[$this->table_element]['label'])?$extrafields->attributes[$this->table_element]['label']:null);
4869
		}
4870
		else
4871
		{
4872
			global $extrafields;
4873
			dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
4874
		}
4875
4876
		$table_element = $this->table_element;
4877
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4878
4879
		// Request to get complementary values
4880
		if (is_array($optionsArray) && count($optionsArray) > 0)
4881
		{
4882
			$sql = "SELECT rowid";
4883
			foreach ($optionsArray as $name => $label)
4884
			{
4885
				if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate')
4886
				{
4887
					$sql.= ", ".$name;
4888
				}
4889
			}
4890
			$sql.= " FROM ".MAIN_DB_PREFIX.$table_element."_extrafields";
4891
			$sql.= " WHERE fk_object = ".$rowid;
4892
4893
			//dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG);		// Too verbose
4894
			$resql=$this->db->query($sql);
4895
			if ($resql)
4896
			{
4897
				$this->array_options = array();
4898
				$numrows=$this->db->num_rows($resql);
4899
				if ($numrows)
4900
				{
4901
					$tab = $this->db->fetch_array($resql);
4902
4903
					foreach ($tab as $key => $value)
4904
					{
4905
						// 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)
4906
						if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && ! is_int($key))
4907
						{
4908
							// we can add this attribute to object
4909
							if (! empty($extrafields) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime')))
4910
							{
4911
								//var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
4912
								$this->array_options["options_".$key]=$this->db->jdate($value);
4913
							}
4914
							else
4915
							{
4916
								$this->array_options["options_".$key]=$value;
4917
							}
4918
4919
							//var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
4920
						}
4921
					}
4922
4923
					// If field is a computed field, value must become result of compute
4924
					foreach ($tab as $key => $value) {
4925
						if (! empty($extrafields) && !empty($extrafields->attributes[$this->table_element]['computed'][$key]))
4926
						{
4927
							$this->array_options["options_".$key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0);
4928
						}
4929
					}
4930
				}
4931
4932
				$this->db->free($resql);
4933
4934
				if ($numrows) return $numrows;
4935
				else return 0;
4936
			}
4937
			else
4938
			{
4939
				dol_print_error($this->db);
4940
				return -1;
4941
			}
4942
		}
4943
		return 0;
4944
	}
4945
4946
	/**
4947
	 *	Delete all extra fields values for the current object.
4948
	 *
4949
	 *  @return	int		<0 if KO, >0 if OK
4950
	 */
4951
	public function deleteExtraFields()
4952
	{
4953
		$this->db->begin();
4954
4955
		$table_element = $this->table_element;
4956
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4957
4958
		$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
4959
		dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
4960
		$resql=$this->db->query($sql_del);
4961
		if (! $resql)
4962
		{
4963
			$this->error=$this->db->lasterror();
4964
			$this->db->rollback();
4965
			return -1;
4966
		}
4967
		else
4968
		{
4969
			$this->db->commit();
4970
			return 1;
4971
		}
4972
	}
4973
4974
	/**
4975
	 *	Add/Update all extra fields values for the current object.
4976
	 *  Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
4977
	 *  This function delete record with all extrafields and insert them again from the array $this->array_options.
4978
	 *
4979
	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
4980
	 *  @param	User		$userused		Object user
4981
	 *  @return int 						-1=error, O=did nothing, 1=OK
4982
	 *  @see updateExtraField(), setValueFrom()
4983
	 */
4984
	public function insertExtraFields($trigger = '', $userused = null)
4985
	{
4986
		global $conf,$langs,$user;
4987
4988
		if (empty($userused)) $userused=$user;
4989
4990
		$error=0;
4991
4992
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
4993
4994
		if (! empty($this->array_options))
4995
		{
4996
			// Check parameters
4997
			$langs->load('admin');
4998
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4999
			$extrafields = new ExtraFields($this->db);
5000
			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
5001
5002
			//Eliminate copied source object extra_fields that do not exist in target object
5003
			$new_array_options=array();
5004
			foreach ($this->array_options as $key => $value) {
5005
				if (in_array(substr($key, 8), array_keys($target_extrafields)))	// We remove the 'options_' from $key for test
5006
					$new_array_options[$key] = $value;
5007
				elseif (in_array($key, array_keys($target_extrafields)))		// We test on $key that does not contains the 'options_' prefix
5008
					$new_array_options['options_'.$key] = $value;
5009
			}
5010
5011
			foreach($new_array_options as $key => $value)
5012
			{
5013
			   	$attributeKey      = substr($key, 8);   // Remove 'options_' prefix
5014
			   	$attributeType     = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
5015
			   	$attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
5016
			   	$attributeParam    = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
5017
			   	$attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
5018
5019
			   	if ($attributeRequired)
5020
			   	{
5021
			   		$mandatorypb=false;
5022
			   		if ($attributeType == 'link' && $this->array_options[$key] == '-1') $mandatorypb=true;
5023
			   		if ($this->array_options[$key] === '') $mandatorypb=true;
5024
			   		if ($mandatorypb)
5025
			   		{
5026
			   		    dol_syslog("Mandatory extra field ".$key." is empty");
5027
			   			$this->errors[]=$langs->trans('ErrorFieldRequired', $attributeLabel);
5028
			   			return -1;
5029
			   		}
5030
			   	}
5031
5032
				//dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5033
				//dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5034
5035
			   	switch ($attributeType)
5036
			   	{
5037
			   		case 'int':
5038
			  			if (!is_numeric($value) && $value!='')
5039
			   			{
5040
			   				$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5041
			   				return -1;
5042
			  			}
5043
			   			elseif ($value=='')
5044
			   			{
5045
			   				$new_array_options[$key] = null;
5046
			   			}
5047
			 			break;
5048
					case 'double':
5049
						$value = price2num($value);
5050
						if (!is_numeric($value) && $value!='')
5051
						{
5052
							dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5053
							$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5054
							return -1;
5055
						}
5056
						elseif ($value=='')
5057
						{
5058
							$new_array_options[$key] = null;
5059
						}
5060
						//dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5061
						$new_array_options[$key] = $value;
5062
						break;
5063
			 		/*case 'select':	// Not required, we chosed value='0' for undefined values
5064
             			if ($value=='-1')
5065
             			{
5066
             				$this->array_options[$key] = null;
5067
             			}
5068
             			break;*/
5069
			   		case 'password':
5070
			   			$algo='';
5071
			   			if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']))
5072
			   			{
5073
			   				// If there is an encryption choice, we use it to crypt data before insert
5074
			   				$tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
5075
			   				$algo=reset($tmparrays);
5076
			   				if ($algo != '')
5077
			   				{
5078
			   					//global $action;		// $action may be 'create', 'update', 'update_extras'...
5079
			   					//var_dump($action);
5080
			   					//var_dump($this->oldcopy);exit;
5081
			   					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
5082
			   					{
5083
			   						//var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
5084
				   					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.
5085
				   					{
5086
				   						$new_array_options[$key] = $this->array_options[$key];	// Value is kept
5087
				   					}
5088
									else
5089
									{
5090
										// var_dump($algo);
5091
										$newvalue = dol_hash($this->array_options[$key], $algo);
5092
										$new_array_options[$key] = $newvalue;
5093
									}
5094
			   					}
5095
			   					else
5096
			   					{
5097
			   						$new_array_options[$key] = $this->array_options[$key];	// Value is kept
5098
			   					}
5099
			   				}
5100
			   			}
5101
			   			else	// Common usage
5102
			   			{
5103
			   				$new_array_options[$key] = $this->array_options[$key];
5104
			   			}
5105
			   			break;
5106
			   		case 'price':
5107
						$new_array_options[$key] = price2num($this->array_options[$key]);
5108
						break;
5109
					case 'date':
5110
					case 'datetime':
5111
						// If data is a string instead of a timestamp, we convert it
5112
						if (! is_int($this->array_options[$key])) {
5113
							$this->array_options[$key] = strtotime($this->array_options[$key]);
5114
						}
5115
						$new_array_options[$key] = $this->db->idate($this->array_options[$key]);
5116
						break;
5117
		   			case 'link':
5118
						$param_list=array_keys($attributeParam['options']);
5119
						// 0 : ObjectName
5120
						// 1 : classPath
5121
						$InfoFieldList = explode(":", $param_list[0]);
5122
						dol_include_once($InfoFieldList[1]);
5123
						if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
5124
						{
5125
							if ($value == '-1')	// -1 is key for no defined in combo list of objects
5126
							{
5127
								$new_array_options[$key]='';
5128
							}
5129
							elseif ($value)
5130
							{
5131
								$object = new $InfoFieldList[0]($this->db);
5132
								if (is_numeric($value)) $res=$object->fetch($value);
5133
								else $res=$object->fetch('', $value);
5134
5135
								if ($res > 0) $new_array_options[$key]=$object->id;
5136
								else
5137
								{
5138
									$this->error="Id/Ref '".$value."' for object '".$object->element."' not found";
5139
									$this->db->rollback();
5140
									return -1;
5141
								}
5142
							}
5143
						}
5144
						else
5145
						{
5146
							dol_syslog('Error bad setup of extrafield', LOG_WARNING);
5147
						}
5148
						break;
5149
			   	}
5150
			}
5151
5152
			$this->db->begin();
5153
5154
			$table_element = $this->table_element;
5155
			if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
5156
5157
			$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
5158
			dol_syslog(get_class($this)."::insertExtraFields delete", LOG_DEBUG);
5159
			$this->db->query($sql_del);
5160
5161
			$sql = "INSERT INTO ".MAIN_DB_PREFIX.$table_element."_extrafields (fk_object";
5162
			foreach($new_array_options as $key => $value)
5163
			{
5164
				$attributeKey = substr($key, 8);   // Remove 'options_' prefix
5165
				// Add field of attribut
5166
				if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator
5167
					$sql.=",".$attributeKey;
5168
			}
5169
			$sql .= ") VALUES (".$this->id;
5170
5171
			foreach($new_array_options as $key => $value)
5172
			{
5173
				$attributeKey = substr($key, 8);   // Remove 'options_' prefix
5174
				// Add field of attribute
5175
				if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator)
5176
				{
5177
					if ($new_array_options[$key] != '')
5178
					{
5179
						$sql.=",'".$this->db->escape($new_array_options[$key])."'";
5180
					}
5181
					else
5182
					{
5183
						$sql.=",null";
5184
					}
5185
				}
5186
			}
5187
			$sql.=")";
5188
5189
			dol_syslog(get_class($this)."::insertExtraFields insert", LOG_DEBUG);
5190
			$resql = $this->db->query($sql);
5191
			if (! $resql)
5192
			{
5193
				$this->error=$this->db->lasterror();
5194
				$error++;
5195
			}
5196
5197
			if (! $error && $trigger)
5198
			{
5199
				// Call trigger
5200
				$this->context=array('extrafieldaddupdate'=>1);
5201
				$result=$this->call_trigger($trigger, $userused);
5202
				if ($result < 0) $error++;
5203
				// End call trigger
5204
			}
5205
5206
			if ($error)
5207
			{
5208
				$this->db->rollback();
5209
				return -1;
5210
			}
5211
			else
5212
			{
5213
				$this->db->commit();
5214
				return 1;
5215
			}
5216
		}
5217
		else return 0;
5218
	}
5219
5220
	/**
5221
	 *	Update an extra field value for the current object.
5222
	 *  Data to describe values to update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
5223
	 *
5224
	 *  @param  string      $key    		Key of the extrafield (without starting 'options_')
5225
	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
5226
	 *  @param	User		$userused		Object user
5227
	 *  @return int                 		-1=error, O=did nothing, 1=OK
5228
	 *  @see setValueFrom(), insertExtraFields()
5229
	 */
5230
	public function updateExtraField($key, $trigger = null, $userused = null)
5231
	{
5232
		global $conf,$langs,$user;
5233
5234
		if (empty($userused)) $userused=$user;
5235
5236
		$error=0;
5237
5238
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
5239
5240
		if (! empty($this->array_options) && isset($this->array_options["options_".$key]))
5241
		{
5242
			// Check parameters
5243
			$langs->load('admin');
5244
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5245
			$extrafields = new ExtraFields($this->db);
5246
			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
5247
5248
			$value=$this->array_options["options_".$key];
5249
5250
			$attributeType     = $extrafields->attributes[$this->table_element]['type'][$key];
5251
			$attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$key];
5252
			$attributeParam    = $extrafields->attributes[$this->table_element]['param'][$key];
5253
			$attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
5254
5255
			//dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5256
			//dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5257
5258
			switch ($attributeType)
5259
			{
5260
				case 'int':
5261
					if (!is_numeric($value) && $value!='')
5262
					{
5263
						$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5264
						return -1;
5265
					}
5266
					elseif ($value=='')
5267
					{
5268
						$this->array_options["options_".$key] = null;
5269
					}
5270
					break;
5271
				case 'double':
5272
					$value = price2num($value);
5273
					if (!is_numeric($value) && $value!='')
5274
					{
5275
						dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5276
						$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5277
						return -1;
5278
					}
5279
					elseif ($value=='')
5280
					{
5281
						$this->array_options["options_".$key] = null;
5282
					}
5283
					//dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5284
					$this->array_options["options_".$key] = $value;
5285
					break;
5286
			 	/*case 'select':	// Not required, we chosed value='0' for undefined values
5287
             		if ($value=='-1')
5288
             		{
5289
             			$this->array_options[$key] = null;
5290
             		}
5291
             		break;*/
5292
				case 'price':
5293
					$this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
5294
					break;
5295
				case 'date':
5296
					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5297
					break;
5298
				case 'datetime':
5299
					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5300
					break;
5301
				case 'link':
5302
					$param_list=array_keys($attributeParam['options']);
5303
					// 0 : ObjectName
5304
					// 1 : classPath
5305
					$InfoFieldList = explode(":", $param_list[0]);
5306
					dol_include_once($InfoFieldList[1]);
5307
					if ($value)
5308
					{
5309
						$object = new $InfoFieldList[0]($this->db);
5310
						$object->fetch(0, $value);
5311
						$this->array_options["options_".$key]=$object->id;
5312
					}
5313
					break;
5314
			}
5315
5316
			$this->db->begin();
5317
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key."='".$this->db->escape($this->array_options["options_".$key])."'";
5318
			$sql .= " WHERE fk_object = ".$this->id;
5319
			$resql = $this->db->query($sql);
5320
			if (! $resql)
5321
			{
5322
				$error++;
5323
				$this->error=$this->db->lasterror();
5324
			}
5325
5326
			if (! $error && $trigger)
5327
			{
5328
				// Call trigger
5329
				$this->context=array('extrafieldupdate'=>1);
5330
				$result=$this->call_trigger($trigger, $userused);
5331
				if ($result < 0) $error++;
5332
				// End call trigger
5333
			}
5334
5335
			if ($error)
5336
			{
5337
				dol_syslog(get_class($this) . "::".__METHOD__ . $this->error, LOG_ERR);
5338
				$this->db->rollback();
5339
				return -1;
5340
			}
5341
			else
5342
			{
5343
				$this->db->commit();
5344
				return 1;
5345
			}
5346
		}
5347
		else return 0;
5348
	}
5349
5350
5351
	/**
5352
	 * Return HTML string to put an input field into a page
5353
	 * Code very similar with showInputField of extra fields
5354
	 *
5355
	 * @param  array   		$val	       Array of properties for field to show
5356
	 * @param  string  		$key           Key of attribute
5357
	 * @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)
5358
	 * @param  string  		$moreparam     To add more parameters on html input tag
5359
	 * @param  string  		$keysuffix     Prefix string to add into name and id of field (can be used to avoid duplicate names)
5360
	 * @param  string  		$keyprefix     Suffix string to add into name and id of field (can be used to avoid duplicate names)
5361
	 * @param  string|int		$morecss       Value for css to define style/length of field. May also be a numeric.
5362
	 * @return string
5363
	 */
5364
	public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0)
5365
	{
5366
		global $conf,$langs,$form;
5367
5368
		if (! is_object($form))
5369
		{
5370
			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5371
			$form=new Form($this->db);
5372
		}
5373
5374
		$val=$this->fields[$key];
5375
5376
		$out='';
5377
        $type='';
5378
        $param = array();
5379
        $param['options']=array();
5380
        $size =$this->fields[$key]['size'];
5381
        // Because we work on extrafields
5382
        if(preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)){
5383
            $param['options']=array($reg[1].':'.$reg[2]=>'N');
5384
            $type ='link';
5385
        } elseif(preg_match('/^link:(.*):(.*)/i', $val['type'], $reg)) {
5386
            $param['options']=array($reg[1].':'.$reg[2]=>'N');
5387
            $type ='link';
5388
        } elseif(preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
5389
            $param['options']=array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4]=>'N');
5390
            $type ='sellist';
5391
        } elseif(preg_match('/varchar\((\d+)\)/', $val['type'], $reg)) {
5392
            $param['options']=array();
5393
            $type ='varchar';
5394
            $size=$reg[1];
5395
        } elseif(preg_match('/varchar/', $val['type'])) {
5396
            $param['options']=array();
5397
            $type ='varchar';
5398
        } elseif(is_array($this->fields[$key]['arrayofkeyval'])) {
5399
            $param['options']=$this->fields[$key]['arrayofkeyval'];
5400
            $type ='select';
5401
        } else {
5402
            $param['options']=array();
5403
            $type =$this->fields[$key]['type'];
5404
        }
5405
5406
		$label=$this->fields[$key]['label'];
5407
		//$elementtype=$this->fields[$key]['elementtype'];	// Seems not used
5408
		$default=$this->fields[$key]['default'];
5409
		$computed=$this->fields[$key]['computed'];
5410
		$unique=$this->fields[$key]['unique'];
5411
		$required=$this->fields[$key]['required'];
5412
5413
		$langfile=$this->fields[$key]['langfile'];
5414
		$list=$this->fields[$key]['list'];
5415
		$hidden=abs($this->fields[$key]['visible'])!=1?1:0;
5416
5417
		$objectid = $this->id;
5418
5419
5420
		if ($computed)
5421
		{
5422
			if (! preg_match('/^search_/', $keyprefix)) return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
5423
			else return '';
5424
		}
5425
5426
5427
		// Use in priority showsize from parameters, then $val['css'] then autodefine
5428
		if (empty($morecss) && ! empty($val['css']))
5429
		{
5430
			$showsize = $val['css'];
5431
		}
5432
		if (empty($morecss))
5433
		{
5434
			if ($type == 'date')
5435
			{
5436
				$morecss = 'minwidth100imp';
5437
			}
5438
			elseif ($type == 'datetime')
5439
			{
5440
				$morecss = 'minwidth200imp';
5441
			}
5442
			elseif (in_array($type, array('int','integer','price')) || preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type))
5443
			{
5444
				$morecss = 'maxwidth75';
5445
            } elseif ($type == 'url') {
5446
				$morecss='minwidth400';
5447
			}
5448
			elseif ($type == 'boolean')
5449
			{
5450
				$morecss='';
5451
			}
5452
			else
5453
			{
5454
				if (round($size) < 12)
5455
				{
5456
					$morecss = 'minwidth100';
5457
				}
5458
				elseif (round($size) <= 48)
5459
				{
5460
					$morecss = 'minwidth200';
5461
				}
5462
				else
5463
				{
5464
					$morecss = 'minwidth400';
5465
				}
5466
			}
5467
		}
5468
5469
		if (in_array($type, array('date','datetime')))
5470
		{
5471
			$tmp=explode(',', $size);
5472
			$newsize=$tmp[0];
5473
5474
			$showtime = in_array($type, array('datetime')) ? 1 : 0;
5475
5476
			// Do not show current date when field not required (see selectDate() method)
5477
			if (!$required && $value == '') $value = '-1';
5478
5479
			// TODO Must also support $moreparam
5480
			$out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
5481
		}
5482
		elseif (in_array($type, array('int','integer')))
5483
		{
5484
			$tmp=explode(',', $size);
5485
			$newsize=$tmp[0];
5486
			$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:'').'>';
5487
		}
5488
		elseif (preg_match('/varchar/', $type))
5489
		{
5490
			$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:'').'>';
5491
		}
5492
		elseif (in_array($type, array('mail', 'phone', 'url')))
5493
		{
5494
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5495
		}
5496
		elseif ($type == 'text')
5497
		{
5498
			if (! preg_match('/search_/', $keyprefix))		// If keyprefix is search_ or search_options_, we must just use a simple text field
5499
			{
5500
				require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5501
				$doleditor=new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
5502
				$out=$doleditor->Create(1);
5503
			}
5504
			else
5505
			{
5506
				$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5507
			}
5508
		}
5509
		elseif ($type == 'html')
5510
		{
5511
			if (! preg_match('/search_/', $keyprefix))		// If keyprefix is search_ or search_options_, we must just use a simple text field
5512
			{
5513
				require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5514
				$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%');
5515
				$out=$doleditor->Create(1);
5516
			}
5517
			else
5518
			{
5519
				$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5520
			}
5521
		}
5522
		elseif ($type == 'boolean')
5523
		{
5524
			$checked='';
5525
			if (!empty($value)) {
5526
				$checked=' checked value="1" ';
5527
			} else {
5528
				$checked=' value="1" ';
5529
			}
5530
			$out='<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam?$moreparam:'').'>';
5531
		}
5532
		elseif ($type == 'price')
5533
		{
5534
			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5535
				$value=price($value);
5536
			}
5537
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> '.$langs->getCurrencySymbol($conf->currency);
5538
		}
5539
		elseif (preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type))
5540
		{
5541
			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5542
				$value=price($value);
5543
			}
5544
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> ';
5545
		}
5546
		elseif ($type == 'select')
5547
		{
5548
			$out = '';
5549
			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5550
			{
5551
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5552
				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5553
			}
5554
5555
			$out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5556
                if((! isset($this->fields[$key]['default'])) ||($this->fields[$key]['notnull']!=1))$out.='<option value="0">&nbsp;</option>';
5557
			foreach ($param['options'] as $key => $val)
5558
			{
5559
				if ((string) $key == '') continue;
5560
				list($val, $parent) = explode('|', $val);
5561
				$out.='<option value="'.$key.'"';
5562
				$out.= (((string) $value == (string) $key)?' selected':'');
5563
				$out.= (!empty($parent)?' parent="'.$parent.'"':'');
5564
				$out.='>'.$val.'</option>';
5565
			}
5566
			$out.='</select>';
5567
		}
5568
		elseif ($type == 'sellist')
5569
		{
5570
			$out = '';
5571
			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5572
			{
5573
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5574
				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5575
			}
5576
5577
			$out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5578
			if (is_array($param['options']))
5579
			{
5580
				$param_list=array_keys($param['options']);
5581
				$InfoFieldList = explode(":", $param_list[0]);
5582
				$parentName='';
5583
				$parentField='';
5584
				// 0 : tableName
5585
				// 1 : label field name
5586
				// 2 : key fields name (if differ of rowid)
5587
				// 3 : key field parent (for dependent lists)
5588
				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5589
				$keyList=(empty($InfoFieldList[2])?'rowid':$InfoFieldList[2].' as rowid');
5590
5591
5592
				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4]))
5593
				{
5594
					if (strpos($InfoFieldList[4], 'extra.') !== false)
5595
					{
5596
						$keyList='main.'.$InfoFieldList[2].' as rowid';
5597
					} else {
5598
						$keyList=$InfoFieldList[2].' as rowid';
5599
					}
5600
				}
5601
				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3]))
5602
				{
5603
					list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
5604
					$keyList.= ', '.$parentField;
5605
				}
5606
5607
				$fields_label = explode('|', $InfoFieldList[1]);
5608
				if (is_array($fields_label))
5609
				{
5610
					$keyList .=', ';
5611
					$keyList .= implode(', ', $fields_label);
5612
				}
5613
5614
				$sqlwhere='';
5615
				$sql = 'SELECT '.$keyList;
5616
				$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
5617
				if (!empty($InfoFieldList[4]))
5618
				{
5619
					// can use SELECT request
5620
					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5621
						$InfoFieldList[4]=str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
5622
					}
5623
5624
					// current object id can be use into filter
5625
					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5626
						$InfoFieldList[4]=str_replace('$ID$', $objectid, $InfoFieldList[4]);
5627
					} else {
5628
						$InfoFieldList[4]=str_replace('$ID$', '0', $InfoFieldList[4]);
5629
					}
5630
					//We have to join on extrafield table
5631
					if (strpos($InfoFieldList[4], 'extra')!==false)
5632
					{
5633
						$sql.= ' as main, '.MAIN_DB_PREFIX .$InfoFieldList[0].'_extrafields as extra';
5634
						$sqlwhere.= ' WHERE extra.fk_object=main.'.$InfoFieldList[2]. ' AND '.$InfoFieldList[4];
5635
					}
5636
					else
5637
					{
5638
						$sqlwhere.= ' WHERE '.$InfoFieldList[4];
5639
					}
5640
				}
5641
				else
5642
				{
5643
					$sqlwhere.= ' WHERE 1=1';
5644
				}
5645
				// Some tables may have field, some other not. For the moment we disable it.
5646
				if (in_array($InfoFieldList[0], array('tablewithentity')))
5647
				{
5648
					$sqlwhere.= ' AND entity = '.$conf->entity;
5649
				}
5650
				$sql.=$sqlwhere;
5651
				//print $sql;
5652
5653
				$sql .= ' ORDER BY ' . implode(', ', $fields_label);
5654
5655
				dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
5656
				$resql = $this->db->query($sql);
5657
				if ($resql)
5658
				{
5659
					$out.='<option value="0">&nbsp;</option>';
5660
					$num = $this->db->num_rows($resql);
5661
					$i = 0;
5662
					while ($i < $num)
5663
					{
5664
						$labeltoshow='';
5665
						$obj = $this->db->fetch_object($resql);
5666
5667
						// Several field into label (eq table:code|libelle:rowid)
5668
						$notrans = false;
5669
						$fields_label = explode('|', $InfoFieldList[1]);
5670
						if (is_array($fields_label))
5671
						{
5672
							$notrans = true;
5673
							foreach ($fields_label as $field_toshow)
5674
							{
5675
								$labeltoshow.= $obj->$field_toshow.' ';
5676
							}
5677
						}
5678
						else
5679
						{
5680
							$labeltoshow=$obj->{$InfoFieldList[1]};
5681
						}
5682
						$labeltoshow=dol_trunc($labeltoshow, 45);
5683
5684
						if ($value == $obj->rowid)
5685
						{
5686
							foreach ($fields_label as $field_toshow)
5687
							{
5688
								$translabel=$langs->trans($obj->$field_toshow);
5689
								if ($translabel!=$obj->$field_toshow) {
5690
									$labeltoshow=dol_trunc($translabel, 18).' ';
5691
								}else {
5692
									$labeltoshow=dol_trunc($obj->$field_toshow, 18).' ';
5693
								}
5694
							}
5695
							$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5696
						}
5697
						else
5698
						{
5699
							if (! $notrans)
5700
							{
5701
								$translabel=$langs->trans($obj->{$InfoFieldList[1]});
5702
								if ($translabel!=$obj->{$InfoFieldList[1]}) {
5703
									$labeltoshow=dol_trunc($translabel, 18);
5704
								}
5705
								else {
5706
									$labeltoshow=dol_trunc($obj->{$InfoFieldList[1]}, 18);
5707
								}
5708
							}
5709
							if (empty($labeltoshow)) $labeltoshow='(not defined)';
5710
							if ($value==$obj->rowid)
5711
							{
5712
								$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5713
							}
5714
5715
							if (!empty($InfoFieldList[3]) && $parentField)
5716
							{
5717
								$parent = $parentName.':'.$obj->{$parentField};
5718
							}
5719
5720
							$out.='<option value="'.$obj->rowid.'"';
5721
							$out.= ($value==$obj->rowid?' selected':'');
5722
							$out.= (!empty($parent)?' parent="'.$parent.'"':'');
5723
							$out.='>'.$labeltoshow.'</option>';
5724
						}
5725
5726
						$i++;
5727
					}
5728
					$this->db->free($resql);
5729
				}
5730
				else {
5731
					print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
5732
				}
5733
			}
5734
			$out.='</select>';
5735
		}
5736
		elseif ($type == 'checkbox')
5737
		{
5738
			$value_arr=explode(',', $value);
5739
			$out=$form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options'])?null:$param['options']), $value_arr, '', 0, '', 0, '100%');
5740
		}
5741
		elseif ($type == 'radio')
5742
		{
5743
			$out='';
5744
			foreach ($param['options'] as $keyopt => $val)
5745
			{
5746
				$out.='<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'');
5747
				$out.=' value="'.$keyopt.'"';
5748
				$out.=' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
5749
				$out.= ($value==$keyopt?'checked':'');
5750
				$out.='/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$val.'</label><br>';
5751
			}
5752
		}
5753
		elseif ($type == 'chkbxlst')
5754
		{
5755
			if (is_array($value)) {
5756
				$value_arr = $value;
5757
			}
5758
			else {
5759
				$value_arr = explode(',', $value);
5760
			}
5761
5762
			if (is_array($param['options'])) {
5763
				$param_list = array_keys($param['options']);
5764
				$InfoFieldList = explode(":", $param_list[0]);
5765
				$parentName='';
5766
				$parentField='';
5767
				// 0 : tableName
5768
				// 1 : label field name
5769
				// 2 : key fields name (if differ of rowid)
5770
				// 3 : key field parent (for dependent lists)
5771
				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5772
				$keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
5773
5774
				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3])) {
5775
					list ( $parentName, $parentField ) = explode('|', $InfoFieldList[3]);
5776
					$keyList .= ', ' . $parentField;
5777
				}
5778
				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4])) {
5779
					if (strpos($InfoFieldList[4], 'extra.') !== false) {
5780
						$keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
5781
					} else {
5782
						$keyList = $InfoFieldList[2] . ' as rowid';
5783
					}
5784
				}
5785
5786
				$fields_label = explode('|', $InfoFieldList[1]);
5787
				if (is_array($fields_label)) {
5788
					$keyList .= ', ';
5789
					$keyList .= implode(', ', $fields_label);
5790
				}
5791
5792
				$sqlwhere = '';
5793
				$sql = 'SELECT ' . $keyList;
5794
				$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
5795
				if (! empty($InfoFieldList[4])) {
5796
5797
					// can use SELECT request
5798
					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5799
						$InfoFieldList[4]=str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
5800
					}
5801
5802
					// current object id can be use into filter
5803
					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5804
						$InfoFieldList[4]=str_replace('$ID$', $objectid, $InfoFieldList[4]);
5805
					} else {
5806
						$InfoFieldList[4]=str_replace('$ID$', '0', $InfoFieldList[4]);
5807
					}
5808
5809
					// We have to join on extrafield table
5810
					if (strpos($InfoFieldList[4], 'extra') !== false) {
5811
						$sql .= ' as main, ' . MAIN_DB_PREFIX . $InfoFieldList[0] . '_extrafields as extra';
5812
						$sqlwhere .= ' WHERE extra.fk_object=main.' . $InfoFieldList[2] . ' AND ' . $InfoFieldList[4];
5813
					} else {
5814
						$sqlwhere .= ' WHERE ' . $InfoFieldList[4];
5815
					}
5816
				} else {
5817
					$sqlwhere .= ' WHERE 1=1';
5818
				}
5819
				// Some tables may have field, some other not. For the moment we disable it.
5820
				if (in_array($InfoFieldList[0], array ('tablewithentity')))
5821
				{
5822
					$sqlwhere .= ' AND entity = ' . $conf->entity;
5823
				}
5824
				// $sql.=preg_replace('/^ AND /','',$sqlwhere);
5825
				// print $sql;
5826
5827
				$sql .= $sqlwhere;
5828
				dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
5829
				$resql = $this->db->query($sql);
5830
				if ($resql) {
5831
					$num = $this->db->num_rows($resql);
5832
					$i = 0;
5833
5834
					$data=array();
5835
5836
					while ( $i < $num ) {
5837
						$labeltoshow = '';
5838
						$obj = $this->db->fetch_object($resql);
5839
5840
						$notrans = false;
5841
						// Several field into label (eq table:code|libelle:rowid)
5842
						$fields_label = explode('|', $InfoFieldList[1]);
5843
						if (is_array($fields_label)) {
5844
							$notrans = true;
5845
							foreach ($fields_label as $field_toshow) {
5846
								$labeltoshow .= $obj->$field_toshow . ' ';
5847
							}
5848
						} else {
5849
							$labeltoshow = $obj->{$InfoFieldList[1]};
5850
						}
5851
						$labeltoshow = dol_trunc($labeltoshow, 45);
5852
5853
						if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5854
							foreach ($fields_label as $field_toshow) {
5855
								$translabel = $langs->trans($obj->$field_toshow);
5856
								if ($translabel != $obj->$field_toshow) {
5857
									$labeltoshow = dol_trunc($translabel, 18) . ' ';
5858
								} else {
5859
									$labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
5860
								}
5861
							}
5862
5863
							$data[$obj->rowid]=$labeltoshow;
5864
						} else {
5865
							if (! $notrans) {
5866
								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
5867
								if ($translabel != $obj->{$InfoFieldList[1]}) {
5868
									$labeltoshow = dol_trunc($translabel, 18);
5869
								} else {
5870
									$labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
5871
								}
5872
							}
5873
							if (empty($labeltoshow))
5874
								$labeltoshow = '(not defined)';
5875
5876
								if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5877
									$data[$obj->rowid]=$labeltoshow;
5878
								}
5879
5880
								if (! empty($InfoFieldList[3]) && $parentField) {
5881
									$parent = $parentName . ':' . $obj->{$parentField};
5882
								}
5883
5884
								$data[$obj->rowid]=$labeltoshow;
5885
						}
5886
5887
						$i ++;
5888
					}
5889
					$this->db->free($resql);
5890
5891
					$out=$form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
5892
				} else {
5893
					print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
5894
				}
5895
			}
5896
		}
5897
		elseif ($type == 'link')
5898
		{
5899
			$param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
5900
			$showempty=(($required && $default != '')?0:1);
5901
			$out=$form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty);
5902
			if ($conf->global->MAIN_FEATURES_LEVEL >= 2)
5903
			{
5904
            			list($class,$classfile)=explode(':', $param_list[0]);
5905
            			if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) $url_path=dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
5906
            			else $url_path=dol_buildpath(dirname(dirname($classfile)).'/'.$class.'_card.php', 1);
5907
            			$out.='<a class="butActionNew" href="'.$url_path.'?action=create&backtopage='.$_SERVER['PHP_SELF'].'"><span class="fa fa-plus-circle valignmiddle"></span></a>';
5908
            			// TODO Add Javascript code to add input fields contents to new elements urls
5909
			}
5910
		}
5911
		elseif ($type == 'password')
5912
		{
5913
			// If prefix is 'search_', field is used as a filter, we use a common text field.
5914
			$out='<input type="'.($keyprefix=='search_'?'text':'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'>';
5915
		}
5916
		elseif ($type == 'array')
5917
		{
5918
			$newval = $val;
5919
			$newval['type'] = 'varchar(256)';
5920
5921
			$out='';
5922
5923
			$inputs = array();
5924
			if(! empty($value)) {
5925
				foreach($value as $option) {
0 ignored issues
show
Bug introduced by
The expression $value of type string is not traversable.
Loading history...
5926
					$out.= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5927
					$out.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $showsize).'<br></span>';
0 ignored issues
show
Bug introduced by
The variable $showsize 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...
5928
				}
5929
			}
5930
5931
			$out.= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
5932
5933
			$newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5934
			$newInput.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $showsize).'<br></span>';
5935
5936
			if(! empty($conf->use_javascript_ajax)) {
5937
				$out.= '
5938
					<script>
5939
					$(document).ready(function() {
5940
						$("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
5941
							$("'.dol_escape_js($newInput).'").insertBefore(this);
5942
						});
5943
5944
						$(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
5945
							$(this).parent().remove();
5946
						});
5947
					});
5948
					</script>';
5949
			}
5950
		}
5951
		if (!empty($hidden)) {
5952
			$out='<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
5953
		}
5954
		/* Add comments
5955
		 if ($type == 'date') $out.=' (YYYY-MM-DD)';
5956
		 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
5957
		 */
5958
		return $out;
5959
	}
5960
5961
	/**
5962
	 * Return HTML string to show a field into a page
5963
	 * Code very similar with showOutputField of extra fields
5964
	 *
5965
	 * @param  array   $val		       Array of properties of field to show
5966
	 * @param  string  $key            Key of attribute
5967
	 * @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)
5968
	 * @param  string  $moreparam      To add more parametes on html input tag
5969
	 * @param  string  $keysuffix      Prefix string to add into name and id of field (can be used to avoid duplicate names)
5970
	 * @param  string  $keyprefix      Suffix string to add into name and id of field (can be used to avoid duplicate names)
5971
	 * @param  mixed   $showsize       Value for css to define size. May also be a numeric.
5972
	 * @return string
5973
	 */
5974
	public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $showsize = 0)
5975
	{
5976
		global $conf,$langs,$form;
5977
5978
		if (! is_object($form))
5979
		{
5980
			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5981
			$form=new Form($this->db);
5982
		}
5983
5984
		$objectid = $this->id;
5985
		$label = $val['label'];
5986
		$type  = $val['type'];
5987
		$size  = $val['css'];
5988
5989
		// Convert var to be able to share same code than showOutputField of extrafields
5990
		if (preg_match('/varchar\((\d+)\)/', $type, $reg))
5991
		{
5992
			$type = 'varchar';		// convert varchar(xx) int varchar
5993
			$size = $reg[1];
5994
		}
5995
		elseif (preg_match('/varchar/', $type)) $type = 'varchar';		// convert varchar(xx) int varchar
5996
		if (is_array($val['arrayofkeyval'])) $type='select';
5997
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) $type='link';
5998
5999
		$default=$val['default'];
6000
		$computed=$val['computed'];
6001
		$unique=$val['unique'];
6002
		$required=$val['required'];
6003
		$param=$val['param'];
6004
		if (is_array($val['arrayofkeyval'])) $param['options'] = $val['arrayofkeyval'];
6005
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg))
6006
		{
6007
			$type='link';
6008
			$param['options']=array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
6009
		}
6010
		$langfile=$val['langfile'];
6011
		$list=$val['list'];
6012
		$help=$val['help'];
6013
		$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)
6014
6015
		if ($hidden) return '';
6016
6017
		// If field is a computed field, value must become result of compute
6018
		if ($computed)
6019
		{
6020
			// Make the eval of compute string
6021
			//var_dump($computed);
6022
			$value = dol_eval($computed, 1, 0);
6023
		}
6024
6025
		if (empty($showsize))
6026
		{
6027
			if ($type == 'date')
6028
			{
6029
				//$showsize=10;
6030
				$showsize = 'minwidth100imp';
6031
			}
6032
			elseif ($type == 'datetime')
6033
			{
6034
				//$showsize=19;
6035
				$showsize = 'minwidth200imp';
6036
			}
6037
			elseif (in_array($type, array('int','double','price')))
6038
			{
6039
				//$showsize=10;
6040
				$showsize = 'maxwidth75';
6041
			}
6042
			elseif ($type == 'url')
6043
			{
6044
				$showsize='minwidth400';
6045
			}
6046
			elseif ($type == 'boolean')
6047
			{
6048
				$showsize='';
6049
			}
6050
			else
6051
			{
6052
				if (round($size) < 12)
6053
				{
6054
					$showsize = 'minwidth100';
6055
				}
6056
				elseif (round($size) <= 48)
6057
				{
6058
					$showsize = 'minwidth200';
6059
				}
6060
				else
6061
				{
6062
					//$showsize=48;
6063
					$showsize = 'minwidth400';
6064
				}
6065
			}
6066
		}
6067
6068
		// Format output value differently according to properties of field
6069
		if ($key == 'ref' && method_exists($this, 'getNomUrl')) $value=$this->getNomUrl(1, '', 0, '', 1);
6070
		elseif ($key == 'status' && method_exists($this, 'getLibStatut')) $value=$this->getLibStatut(3);
6071
		elseif ($type == 'date')
6072
		{
6073
			if(! empty($value)) {
6074
				$value=dol_print_date($value, 'day');
6075
			} else {
6076
				$value='';
6077
			}
6078
		}
6079
		elseif ($type == 'datetime')
6080
		{
6081
			if(! empty($value)) {
6082
				$value=dol_print_date($value, 'dayhour');
6083
			} else {
6084
				$value='';
6085
			}
6086
		}
6087
		elseif ($type == 'double')
6088
		{
6089
			if (!empty($value)) {
6090
				$value=price($value);
6091
			}
6092
		}
6093
		elseif ($type == 'boolean')
6094
		{
6095
			$checked='';
6096
			if (!empty($value)) {
6097
				$checked=' checked ';
6098
			}
6099
			$value='<input type="checkbox" '.$checked.' '.($moreparam?$moreparam:'').' readonly disabled>';
6100
		}
6101
		elseif ($type == 'mail')
6102
		{
6103
			$value=dol_print_email($value, 0, 0, 0, 64, 1, 1);
6104
		}
6105
		elseif ($type == 'url')
6106
		{
6107
			$value=dol_print_url($value, '_blank', 32, 1);
6108
		}
6109
		elseif ($type == 'phone')
6110
		{
6111
			$value=dol_print_phone($value, '', 0, 0, '', '&nbsp;', 1);
6112
		}
6113
		elseif ($type == 'price')
6114
		{
6115
			$value=price($value, 0, $langs, 0, 0, -1, $conf->currency);
6116
		}
6117
		elseif ($type == 'select')
6118
		{
6119
			$value=$param['options'][$value];
6120
		}
6121
		elseif ($type == 'sellist')
6122
		{
6123
			$param_list=array_keys($param['options']);
6124
			$InfoFieldList = explode(":", $param_list[0]);
6125
6126
			$selectkey="rowid";
6127
			$keyList='rowid';
6128
6129
			if (count($InfoFieldList)>=3)
6130
			{
6131
				$selectkey = $InfoFieldList[2];
6132
				$keyList=$InfoFieldList[2].' as rowid';
6133
			}
6134
6135
			$fields_label = explode('|', $InfoFieldList[1]);
6136
			if(is_array($fields_label)) {
6137
				$keyList .=', ';
6138
				$keyList .= implode(', ', $fields_label);
6139
			}
6140
6141
			$sql = 'SELECT '.$keyList;
6142
			$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
6143
			if (strpos($InfoFieldList[4], 'extra')!==false)
6144
			{
6145
				$sql.= ' as main';
6146
			}
6147
			if ($selectkey=='rowid' && empty($value)) {
6148
				$sql.= " WHERE ".$selectkey."=0";
6149
			} elseif ($selectkey=='rowid') {
6150
				$sql.= " WHERE ".$selectkey."=".$this->db->escape($value);
6151
			}else {
6152
				$sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6153
			}
6154
6155
			//$sql.= ' AND entity = '.$conf->entity;
6156
6157
			dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
6158
			$resql = $this->db->query($sql);
6159
			if ($resql)
6160
			{
6161
				$value='';	// value was used, so now we reste it to use it to build final output
6162
6163
				$obj = $this->db->fetch_object($resql);
6164
6165
				// Several field into label (eq table:code|libelle:rowid)
6166
				$fields_label = explode('|', $InfoFieldList[1]);
6167
6168
				if(is_array($fields_label) && count($fields_label)>1)
6169
				{
6170
					foreach ($fields_label as $field_toshow)
6171
					{
6172
						$translabel='';
6173
						if (!empty($obj->$field_toshow)) {
6174
							$translabel=$langs->trans($obj->$field_toshow);
6175
						}
6176
						if ($translabel!=$field_toshow) {
6177
							$value.=dol_trunc($translabel, 18).' ';
6178
						}else {
6179
							$value.=$obj->$field_toshow.' ';
6180
						}
6181
					}
6182
				}
6183
				else
6184
				{
6185
					$translabel='';
6186
					if (!empty($obj->{$InfoFieldList[1]})) {
6187
						$translabel=$langs->trans($obj->{$InfoFieldList[1]});
6188
					}
6189
					if ($translabel!=$obj->{$InfoFieldList[1]}) {
6190
						$value=dol_trunc($translabel, 18);
6191
					}else {
6192
						$value=$obj->{$InfoFieldList[1]};
6193
					}
6194
				}
6195
			}
6196
			else dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
6197
		}
6198
		elseif ($type == 'radio')
6199
		{
6200
			$value=$param['options'][$value];
6201
		}
6202
		elseif ($type == 'checkbox')
6203
		{
6204
			$value_arr=explode(',', $value);
6205
			$value='';
6206
			if (is_array($value_arr) && count($value_arr)>0)
6207
			{
6208
				foreach ($value_arr as $keyval=>$valueval) {
6209
					$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...
6210
				}
6211
				$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...
6212
			}
6213
		}
6214
		elseif ($type == 'chkbxlst')
6215
		{
6216
			$value_arr = explode(',', $value);
6217
6218
			$param_list = array_keys($param['options']);
6219
			$InfoFieldList = explode(":", $param_list[0]);
6220
6221
			$selectkey = "rowid";
6222
			$keyList = 'rowid';
6223
6224
			if (count($InfoFieldList) >= 3) {
6225
				$selectkey = $InfoFieldList[2];
6226
				$keyList = $InfoFieldList[2] . ' as rowid';
6227
			}
6228
6229
			$fields_label = explode('|', $InfoFieldList[1]);
6230
			if (is_array($fields_label)) {
6231
				$keyList .= ', ';
6232
				$keyList .= implode(', ', $fields_label);
6233
			}
6234
6235
			$sql = 'SELECT ' . $keyList;
6236
			$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
6237
			if (strpos($InfoFieldList[4], 'extra') !== false) {
6238
				$sql .= ' as main';
6239
			}
6240
			// $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6241
			// $sql.= ' AND entity = '.$conf->entity;
6242
6243
			dol_syslog(get_class($this) . ':showOutputField:$type=chkbxlst', LOG_DEBUG);
6244
			$resql = $this->db->query($sql);
6245
			if ($resql) {
6246
				$value = ''; // value was used, so now we reste it to use it to build final output
6247
				$toprint=array();
6248
				while ( $obj = $this->db->fetch_object($resql) ) {
6249
6250
					// Several field into label (eq table:code|libelle:rowid)
6251
					$fields_label = explode('|', $InfoFieldList[1]);
6252
					if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
6253
						if (is_array($fields_label) && count($fields_label) > 1) {
6254
							foreach ($fields_label as $field_toshow) {
6255
								$translabel = '';
6256
								if (! empty($obj->$field_toshow)) {
6257
									$translabel = $langs->trans($obj->$field_toshow);
6258
								}
6259
								if ($translabel != $field_toshow) {
6260
									$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6261
								} else {
6262
									$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->$field_toshow.'</li>';
6263
								}
6264
							}
6265
						} else {
6266
							$translabel = '';
6267
							if (! empty($obj->{$InfoFieldList[1]})) {
6268
								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
6269
							}
6270
							if ($translabel != $obj->{$InfoFieldList[1]}) {
6271
								$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6272
							} else {
6273
								$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->{$InfoFieldList[1]}.'</li>';
6274
							}
6275
						}
6276
					}
6277
				}
6278
				$value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6279
			} else {
6280
				dol_syslog(get_class($this) . '::showOutputField error ' . $this->db->lasterror(), LOG_WARNING);
6281
			}
6282
		}
6283
		elseif ($type == 'link')
6284
		{
6285
			$out='';
6286
6287
			// only if something to display (perf)
6288
			if ($value)
6289
			{
6290
				$param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
6291
6292
				$InfoFieldList = explode(":", $param_list[0]);
6293
				$classname=$InfoFieldList[0];
6294
				$classpath=$InfoFieldList[1];
6295
				$getnomurlparam=(empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]);
6296
				if (! empty($classpath))
6297
				{
6298
					dol_include_once($InfoFieldList[1]);
6299
					if ($classname && class_exists($classname))
6300
					{
6301
						$object = new $classname($this->db);
6302
						$object->fetch($value);
6303
						$value=$object->getNomUrl($getnomurlparam);
6304
					}
6305
				}
6306
				else
6307
				{
6308
					dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6309
					return 'Error bad setup of extrafield';
6310
				}
6311
			}
6312
			else $value='';
6313
		}
6314
		elseif ($type == 'text' || $type == 'html')
6315
		{
6316
			$value=dol_htmlentitiesbr($value);
6317
		}
6318
		elseif ($type == 'password')
6319
		{
6320
			$value=preg_replace('/./i', '*', $value);
6321
		}
6322
		elseif ($type == 'array')
6323
		{
6324
			$value = implode('<br>', $value);
6325
		}
6326
6327
		//print $type.'-'.$size;
6328
		$out=$value;
6329
6330
		return $out;
6331
	}
6332
6333
6334
	/**
6335
	 * Function to show lines of extrafields with output datas
6336
	 *
6337
	 * @param 	Extrafields $extrafields    Extrafield Object
6338
	 * @param 	string      $mode           Show output (view) or input (edit) for extrafield
6339
	 * @param 	array       $params         Optional parameters. Example: array('style'=>'class="oddeven"', 'colspan'=>$colspan)
6340
	 * @param 	string      $keysuffix      Suffix string to add after name and id of field (can be used to avoid duplicate names)
6341
	 * @param 	string      $keyprefix      Prefix string to add before name and id of field (can be used to avoid duplicate names)
6342
	 * @param	string		$onetrtd		All fields in same tr td
6343
	 * @return 	string
6344
	 */
6345
	public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = 0)
6346
	{
6347
		global $db, $conf, $langs, $action, $form;
6348
6349
		if (! is_object($form)) $form=new Form($db);
6350
6351
		$out = '';
6352
6353
		if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0)
6354
		{
6355
			$out .= "\n";
6356
			$out .= '<!-- showOptionalsInput --> ';
6357
			$out .= "\n";
6358
6359
			$e = 0;
6360
			foreach($extrafields->attributes[$this->table_element]['label'] as $key=>$label)
6361
			{
6362
				// Show only the key field in params
6363
				if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) continue;
6364
6365
				// @TODO Add test also on 'enabled' (different than 'list' that is 'visibility')
6366
				$enabled = 1;
6367
6368
				$visibility = 1;
6369
				if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key]))
6370
				{
6371
				    $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1);
6372
				}
6373
6374
				$perms = 1;
6375
				if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key]))
6376
				{
6377
					$perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1);
6378
				}
6379
6380
				if (($mode == 'create' || $mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3) continue;	// <> -1 and <> 1 and <> 3 = not visible on forms, only on list
6381
				if (empty($perms)) continue;
6382
6383
				// Load language if required
6384
				if (! empty($extrafields->attributes[$this->table_element]['langfile'][$key])) $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
6385
6386
				$colspan='3';
6387
				if (is_array($params) && count($params)>0) {
6388
					if (array_key_exists('colspan', $params)) {
6389
						$colspan=$params['colspan'];
6390
					}
6391
				}
6392
6393
				switch($mode) {
6394
					case "view":
6395
						$value=$this->array_options["options_".$key.$keysuffix];
6396
						break;
6397
					case "edit":
6398
						$getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, 'none');				// GETPOST can get value from GET, POST or setup of default values.
6399
						// GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
6400
						if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix))
6401
						{
6402
							if (is_array($getposttemp)) {
6403
								// $getposttemp is an array but following code expects a comma separated string
6404
								$value = implode(",", $getposttemp);
6405
							} else {
6406
								$value = $getposttemp;
6407
							}
6408
						} else {
6409
							$value = $this->array_options["options_" . $key];			// No GET, no POST, no default value, so we take value of object.
6410
						}
6411
						//var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
6412
						break;
6413
				}
6414
6415
				if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate')
6416
				{
6417
					$out .= $extrafields->showSeparator($key, $this);
6418
				}
6419
				else
6420
				{
6421
					$csstyle='';
6422
					$class=(!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
6423
					if (is_array($params) && count($params)>0) {
6424
						if (array_key_exists('style', $params)) {
6425
							$csstyle=$params['style'];
6426
						}
6427
					}
6428
6429
					// add html5 elements
6430
					$domData  = ' data-element="extrafield"';
6431
					$domData .= ' data-targetelement="'.$this->element.'"';
6432
					$domData .= ' data-targetid="'.$this->id.'"';
6433
6434
					$html_id = !empty($this->id) ? 'extrarow-'.$this->element.'_'.$key.'_'.$this->id : '';
6435
6436
					$out .= '<tr id="'.$html_id.'" '.$csstyle.' class="'.$class.$this->element.'_extras_'.$key.'" '.$domData.' >';
6437
6438
					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0)
6439
					{
6440
						if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) { $colspan='0'; }
6441
					}
6442
6443
					if ($action == 'selectlines') { $colspan++; }
6444
6445
					// Convert date into timestamp format (value in memory must be a timestamp)
6446
					if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime')))
6447
					{
6448
						$datenotinstring = $this->array_options['options_' . $key];
6449
						if (! is_numeric($this->array_options['options_' . $key]))	// For backward compatibility
6450
						{
6451
							$datenotinstring = $this->db->jdate($datenotinstring);
6452
						}
6453
						$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;
6454
					}
6455
					// Convert float submited string into real php numeric (value in memory must be a php numeric)
6456
					if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price','double')))
6457
					{
6458
						$value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?price2num(GETPOST($keyprefix.'options_'.$key.$keysuffix, 'alpha', 3)):$this->array_options['options_'.$key];
6459
					}
6460
6461
					$labeltoshow = $langs->trans($label);
6462
6463
					$out .= '<td class="titlefield';
6464
					if (GETPOST('action', 'none') == 'create') $out.='create';
6465
					if ($mode != 'view' && ! empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= ' fieldrequired';
6466
					$out .= '">';
6467
					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...
6468
					else $out .= $labeltoshow;
6469
					$out .= '</td>';
6470
6471
					$html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
6472
					$out .='<td id="'.$html_id.'" class="'.$this->element.'_extras_'.$key.'" '.($colspan?' colspan="'.$colspan.'"':'').'>';
6473
6474
					switch($mode) {
6475
						case "view":
6476
							$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...
6477
							break;
6478
						case "edit":
6479
							$out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id);
6480
							break;
6481
					}
6482
6483
					$out .= '</td>';
6484
6485
					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>';
6486
					else $out .= '</tr>';
6487
					$e++;
6488
				}
6489
			}
6490
			$out .= "\n";
6491
			// Add code to manage list depending on others
6492
			if (! empty($conf->use_javascript_ajax)) {
6493
				$out .= '
6494
				<script>
6495
				    jQuery(document).ready(function() {
6496
				    	function showOptions(child_list, parent_list)
6497
				    	{
6498
				    		var val = $("select[name=\"options_"+parent_list+"\"]").val();
6499
				    		var parentVal = parent_list + ":" + val;
6500
							if(val > 0) {
6501
					    		$("select[name=\""+child_list+"\"] option[parent]").hide();
6502
					    		$("select[name=\""+child_list+"\"] option[parent=\""+parentVal+"\"]").show();
6503
							} else {
6504
								$("select[name=\""+child_list+"\"] option").show();
6505
							}
6506
				    	}
6507
						function setListDependencies() {
6508
					    	jQuery("select option[parent]").parent().each(function() {
6509
					    		var child_list = $(this).attr("name");
6510
								var parent = $(this).find("option[parent]:first").attr("parent");
6511
								var infos = parent.split(":");
6512
								var parent_list = infos[0];
6513
								$("select[name=\""+parent_list+"\"]").change(function() {
6514
									showOptions(child_list, parent_list);
6515
								});
6516
					    	});
6517
						}
6518
6519
						setListDependencies();
6520
				    });
6521
				</script>'."\n";
6522
				$out .= '<!-- /showOptionalsInput --> '."\n";
6523
			}
6524
		}
6525
		return $out;
6526
	}
6527
6528
6529
	/**
6530
	 * Returns the rights used for this class
6531
	 * @return stdClass
6532
	 */
6533
	public function getRights()
6534
	{
6535
		global $user;
6536
6537
		$element = $this->element;
6538
		if ($element == 'facturerec') $element='facture';
6539
6540
		return $user->rights->{$element};
6541
	}
6542
6543
	/**
6544
	 * Function used to replace a thirdparty id with another one.
6545
	 * This function is meant to be called from replaceThirdparty with the appropiate tables
6546
	 * Column name fk_soc MUST be used to identify thirdparties
6547
	 *
6548
	 * @param  DoliDB 	   $db 			  Database handler
6549
	 * @param  int 		   $origin_id     Old thirdparty id (the thirdparty to delete)
6550
	 * @param  int 		   $dest_id       New thirdparty id (the thirdparty that will received element of the other)
6551
	 * @param  string[]    $tables        Tables that need to be changed
6552
	 * @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)
6553
	 * @return bool						  True if success, False if error
6554
	 */
6555
	public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
6556
	{
6557
		foreach ($tables as $table)
6558
		{
6559
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id;
6560
6561
			if (! $db->query($sql))
6562
			{
6563
				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.
6564
				//$this->errors = $db->lasterror();
6565
				return false;
6566
			}
6567
		}
6568
6569
		return true;
6570
	}
6571
6572
	/**
6573
	 * Get buy price to use for margin calculation. This function is called when buy price is unknown.
6574
	 *	 Set buy price = sell price if ForceBuyingPriceIfNull configured,
6575
	 *   else if calculation MARGIN_TYPE = 'costprice' and costprice is defined, use costprice as buyprice
6576
	 *	 else if calculation MARGIN_TYPE = 'pmp' and pmp is calculated, use pmp as buyprice
6577
	 *	 else set min buy price as buy price
6578
	 *
6579
	 * @param float		$unitPrice		 Product unit price
6580
	 * @param float		$discountPercent Line discount percent
6581
	 * @param int		$fk_product		 Product id
6582
	 * @return	float                    <0 if KO, buyprice if OK
6583
	 */
6584
	public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
6585
	{
6586
		global $conf;
6587
6588
		$buyPrice = 0;
6589
6590
		if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
6591
		{
6592
			$buyPrice = $unitPrice * (1 - $discountPercent / 100);
6593
		}
6594
		else
6595
		{
6596
			// Get cost price for margin calculation
6597
			if (! empty($fk_product))
6598
			{
6599
				if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice')
6600
				{
6601
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6602
					$product = new Product($this->db);
6603
					$result = $product->fetch($fk_product);
6604
					if ($result <= 0)
6605
					{
6606
						$this->errors[] = 'ErrorProductIdDoesNotExists';
6607
						return -1;
6608
					}
6609
					if ($product->cost_price > 0)
6610
					{
6611
						$buyPrice = $product->cost_price;
6612
					}
6613
					elseif ($product->pmp > 0)
6614
					{
6615
						$buyPrice = $product->pmp;
6616
					}
6617
				}
6618
				elseif (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')
6619
				{
6620
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6621
					$product = new Product($this->db);
6622
					$result = $product->fetch($fk_product);
6623
					if ($result <= 0)
6624
					{
6625
						$this->errors[] = 'ErrorProductIdDoesNotExists';
6626
						return -1;
6627
					}
6628
					if ($product->pmp > 0)
6629
					{
6630
						$buyPrice = $product->pmp;
6631
					}
6632
				}
6633
6634
				if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1','pmp','costprice')))
6635
				{
6636
					require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6637
					$productFournisseur = new ProductFournisseur($this->db);
6638
					if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0)
6639
					{
6640
						$buyPrice = $productFournisseur->fourn_unitprice;
6641
					}
6642
					elseif ($result < 0)
6643
					{
6644
						$this->errors[] = $productFournisseur->error;
6645
						return -2;
6646
					}
6647
				}
6648
			}
6649
		}
6650
		return $buyPrice;
6651
	}
6652
6653
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6654
	/**
6655
	 *  Show photos of an object (nbmax maximum), into several columns
6656
	 *
6657
	 *  @param		string	$modulepart		'product', 'ticket', ...
6658
	 *  @param      string	$sdir        	Directory to scan (full absolute path)
6659
	 *  @param      int		$size        	0=original size, 1='small' use thumbnail if possible
6660
	 *  @param      int		$nbmax       	Nombre maximum de photos (0=pas de max)
6661
	 *  @param      int		$nbbyrow     	Number of image per line or -1 to use div. Used only if size=1.
6662
	 * 	@param		int		$showfilename	1=Show filename
6663
	 * 	@param		int		$showaction		1=Show icon with action links (resize, delete)
6664
	 * 	@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.
6665
	 * 	@param		int		$maxWidth		Max width of original image when size='small'
6666
	 *  @param      int     $nolink         Do not add a href link to view enlarged imaged into a new tab
6667
	 *  @param      int     $notitle        Do not add title tag on image
6668
	 *  @param		int		$usesharelink	Use the public shared link of image (if not available, the 'nophoto' image will be shown instead)
6669
	 *  @return     string					Html code to show photo. Number of photos shown is saved in this->nbphoto
6670
	 */
6671
	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)
6672
	{
6673
        // phpcs:enable
6674
		global $conf,$user,$langs;
6675
6676
		include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php';
6677
		include_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';
6678
6679
		$sortfield='position_name';
6680
		$sortorder='asc';
6681
6682
		$dir = $sdir . '/';
6683
		$pdir = '/';
6684
		if ($modulepart == 'ticket')
6685
		{
6686
			$dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6687
			$pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6688
		}
6689
		else
6690
		{
6691
			$dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6692
			$pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6693
		}
6694
6695
		// For backward compatibility
6696
		if ($modulepart == 'product' && ! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))
6697
		{
6698
			$dir = $sdir . '/'. get_exdir($this->id, 2, 0, 0, $this, $modulepart) . $this->id ."/photos/";
6699
			$pdir = '/' . get_exdir($this->id, 2, 0, 0, $this, $modulepart) . $this->id ."/photos/";
6700
		}
6701
6702
		// Defined relative dir to DOL_DATA_ROOT
6703
		$relativedir = '';
6704
		if ($dir)
6705
		{
6706
			$relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
6707
			$relativedir = preg_replace('/^[\\/]/', '', $relativedir);
6708
			$relativedir = preg_replace('/[\\/]$/', '', $relativedir);
6709
		}
6710
6711
		$dirthumb = $dir.'thumbs/';
6712
		$pdirthumb = $pdir.'thumbs/';
6713
6714
		$return ='<!-- Photo -->'."\n";
6715
		$nbphoto=0;
6716
6717
		$filearray=dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC), 1);
6718
6719
		/*if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))    // For backward compatiblity, we scan also old dirs
6720
		 {
6721
		 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
6722
		 $filearray=array_merge($filearray, $filearrayold);
6723
		 }*/
6724
6725
		completeFileArrayWithDatabaseInfo($filearray, $relativedir);
6726
6727
		if (count($filearray))
6728
		{
6729
			if ($sortfield && $sortorder)
6730
			{
6731
				$filearray=dol_sort_array($filearray, $sortfield, $sortorder);
6732
			}
6733
6734
			foreach($filearray as $key => $val)
6735
			{
6736
				$photo='';
6737
				$file = $val['name'];
6738
6739
				//if (! utf8_check($file)) $file=utf8_encode($file);	// To be sure file is stored in UTF8 in memory
6740
6741
				//if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
6742
				if (image_format_supported($file) >= 0)
6743
				{
6744
					$nbphoto++;
6745
					$photo = $file;
6746
					$viewfilename = $file;
6747
6748
					if ($size == 1 || $size == 'small') {   // Format vignette
6749
6750
						// Find name of thumb file
6751
						$photo_vignette=basename(getImageFileNameForSize($dir.$file, '_small'));
6752
						if (! dol_is_file($dirthumb.$photo_vignette)) $photo_vignette='';
6753
6754
						// Get filesize of original file
6755
						$imgarray=dol_getImageSize($dir.$photo);
6756
6757
						if ($nbbyrow > 0)
6758
						{
6759
							if ($nbphoto == 1) $return.= '<table class="valigntop center centpercent" style="border: 0; padding: 2; border-spacing: 2px; border-collapse: separate;">';
6760
6761
							if ($nbphoto % $nbbyrow == 1) $return.= '<tr class="center valignmiddle" style="border: 1px">';
6762
							$return.= '<td style="width: '.ceil(100/$nbbyrow).'%" class="photo">';
6763
						}
6764
						elseif ($nbbyrow < 0) $return .= '<div class="inline-block">';
6765
6766
						$return.= "\n";
6767
6768
						$relativefile=preg_replace('/^\//', '', $pdir.$photo);
6769
						if (empty($nolink))
6770
						{
6771
							$urladvanced=getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
6772
							if ($urladvanced) $return.='<a href="'.$urladvanced.'">';
6773
							else $return.= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank">';
6774
						}
6775
6776
						// Show image (width height=$maxHeight)
6777
						// Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
6778
						$alt=$langs->transnoentitiesnoconv('File').': '.$relativefile;
6779
						$alt.=' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
6780
						if ($notitle) $alt='';
6781
6782
						if ($usesharelink)
6783
						{
6784
							if ($val['share'])
6785
							{
6786
								if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6787
								{
6788
									$return.= '<!-- Show original file (thumb not yet available with shared links) -->';
6789
									$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6790
								}
6791
								else {
6792
									$return.= '<!-- Show original file -->';
6793
									$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6794
								}
6795
							}
6796
							else
6797
							{
6798
								$return.= '<!-- Show nophoto file (because file is not shared) -->';
6799
								$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
6800
							}
6801
						}
6802
						else
6803
						{
6804
							if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6805
							{
6806
								$return.= '<!-- Show thumb -->';
6807
								$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).'">';
6808
							}
6809
							else {
6810
								$return.= '<!-- Show original file -->';
6811
								$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).'">';
6812
							}
6813
						}
6814
6815
						if (empty($nolink)) $return.= '</a>';
6816
						$return.="\n";
6817
6818
						if ($showfilename) $return.= '<br>'.$viewfilename;
6819
						if ($showaction)
6820
						{
6821
							$return.= '<br>';
6822
							// On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
6823
							if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight))
6824
							{
6825
								$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>';
6826
							}
6827
							// Special cas for product
6828
							if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6829
							{
6830
								// Link to resize
6831
								$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; ';
6832
6833
								// Link to delete
6834
								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6835
								$return.= img_delete().'</a>';
6836
							}
6837
						}
6838
						$return.= "\n";
6839
6840
						if ($nbbyrow > 0)
6841
						{
6842
							$return.= '</td>';
6843
							if (($nbphoto % $nbbyrow) == 0) $return.= '</tr>';
6844
						}
6845
						elseif ($nbbyrow < 0) $return.='</div>';
6846
					}
6847
6848
					if (empty($size)) {     // Format origine
6849
						$return.= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
6850
6851
						if ($showfilename) $return.= '<br>'.$viewfilename;
6852
						if ($showaction)
6853
						{
6854
							// Special case for product
6855
							if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6856
							{
6857
								// Link to resize
6858
								$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; ';
6859
6860
								// Link to delete
6861
								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6862
								$return.= img_delete().'</a>';
6863
							}
6864
						}
6865
					}
6866
6867
					// On continue ou on arrete de boucler ?
6868
					if ($nbmax && $nbphoto >= $nbmax) break;
6869
				}
6870
			}
6871
6872
			if ($size==1 || $size=='small')
6873
			{
6874
				if ($nbbyrow > 0)
6875
				{
6876
					// Ferme tableau
6877
					while ($nbphoto % $nbbyrow)
6878
					{
6879
						$return.= '<td style="width: '.ceil(100/$nbbyrow).'%">&nbsp;</td>';
6880
						$nbphoto++;
6881
					}
6882
6883
					if ($nbphoto) $return.= '</table>';
6884
				}
6885
			}
6886
		}
6887
6888
		$this->nbphoto = $nbphoto;
6889
6890
		return $return;
6891
	}
6892
6893
6894
	/**
6895
	 * Function test if type is array
6896
	 *
6897
	 * @param   array   $info   content informations of field
6898
	 * @return                  bool
6899
	 */
6900
	protected function isArray($info)
6901
	{
6902
		if(is_array($info))
6903
		{
6904
			if(isset($info['type']) && $info['type']=='array') return true;
6905
			else return false;
6906
		}
6907
		else return false;
6908
	}
6909
6910
	/**
6911
	 * Function test if type is null
6912
	 *
6913
	 * @param   array   $info   content informations of field
6914
	 * @return                  bool
6915
	 */
6916
	protected function isNull($info)
6917
	{
6918
		if(is_array($info))
6919
		{
6920
			if(isset($info['type']) && $info['type']=='null') return true;
6921
			else return false;
6922
		}
6923
		else return false;
6924
	}
6925
6926
	/**
6927
	 * Function test if type is date
6928
	 *
6929
	 * @param   array   $info   content informations of field
6930
	 * @return                  bool
6931
	 */
6932
	public function isDate($info)
6933
	{
6934
		if(isset($info['type']) && ($info['type']=='date' || $info['type']=='datetime' || $info['type']=='timestamp')) return true;
6935
		else return false;
6936
	}
6937
6938
	/**
6939
	 * Function test if type is integer
6940
	 *
6941
	 * @param   array   $info   content informations of field
6942
	 * @return                  bool
6943
	 */
6944
	public function isInt($info)
6945
	{
6946
		if(is_array($info))
6947
		{
6948
			if(isset($info['type']) && ($info['type']=='int' || preg_match('/^integer/i', $info['type']) ) ) return true;
6949
			else return false;
6950
		}
6951
		else return false;
6952
	}
6953
6954
	/**
6955
	 * Function test if type is float
6956
	 *
6957
	 * @param   array   $info   content informations of field
6958
	 * @return                  bool
6959
	 */
6960
	public function isFloat($info)
6961
	{
6962
		if(is_array($info))
6963
		{
6964
			if (isset($info['type']) && (preg_match('/^(double|real)/i', $info['type']))) return true;
6965
			else return false;
6966
		}
6967
		else return false;
6968
	}
6969
6970
	/**
6971
	 * Function test if type is text
6972
	 *
6973
	 * @param   array   $info   content informations of field
6974
	 * @return                  bool
6975
	 */
6976
	public function isText($info)
6977
	{
6978
		if(is_array($info))
6979
		{
6980
			if(isset($info['type']) && $info['type']=='text') return true;
6981
			else return false;
6982
		}
6983
		else return false;
6984
	}
6985
6986
	/**
6987
	 * Function test if is indexed
6988
	 *
6989
	 * @param   array   $info   content informations of field
6990
	 * @return                  bool
6991
	 */
6992
	protected function isIndex($info)
6993
	{
6994
		if(is_array($info))
6995
		{
6996
			if(isset($info['index']) && $info['index']==true) return true;
6997
			else return false;
6998
		}
6999
		else return false;
7000
	}
7001
7002
	/**
7003
	 * Function to prepare the values to insert.
7004
	 * Note $this->${field} are set by the page that make the createCommon or the updateCommon.
7005
	 *
7006
	 * @return array
7007
	 */
7008
	protected function setSaveQuery()
7009
	{
7010
		global $conf;
7011
7012
		$queryarray=array();
7013
		foreach ($this->fields as $field=>$info)	// Loop on definition of fields
7014
		{
7015
			// Depending on field type ('datetime', ...)
7016
			if($this->isDate($info))
7017
			{
7018
				if(empty($this->{$field}))
7019
				{
7020
					$queryarray[$field] = null;
7021
				}
7022
				else
7023
				{
7024
					$queryarray[$field] = $this->db->idate($this->{$field});
7025
				}
7026
			}
7027
			elseif($this->isArray($info))
7028
			{
7029
				if(! empty($this->{$field})) {
7030
					if(! is_array($this->{$field})) {
7031
						$this->{$field} = array($this->{$field});
7032
					}
7033
					$queryarray[$field] = serialize($this->{$field});
7034
				} else {
7035
					$queryarray[$field] = null;
7036
				}
7037
			}
7038
			elseif($this->isInt($info))
7039
			{
7040
				if ($field == 'entity' && is_null($this->{$field})) $queryarray[$field]=$conf->entity;
7041
				else
7042
				{
7043
					$queryarray[$field] = (int) price2num($this->{$field});
7044
					if (empty($queryarray[$field])) $queryarray[$field]=0;		// May be reset to null later if property 'notnull' is -1 for this field.
7045
				}
7046
			}
7047
			elseif($this->isFloat($info))
7048
			{
7049
				$queryarray[$field] = (double) price2num($this->{$field});
7050
				if (empty($queryarray[$field])) $queryarray[$field]=0;
7051
			}
7052
			else
7053
			{
7054
				$queryarray[$field] = $this->{$field};
7055
			}
7056
7057
			if ($info['type'] == 'timestamp' && empty($queryarray[$field])) unset($queryarray[$field]);
7058
			if (! empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) $queryarray[$field] = null;
7059
		}
7060
7061
		return $queryarray;
7062
	}
7063
7064
	/**
7065
	 * Function to load data from a SQL pointer into properties of current object $this
7066
	 *
7067
	 * @param   stdClass    $obj    Contain data of object from database
7068
     * @return void
7069
	 */
7070
	protected function setVarsFromFetchObj(&$obj)
7071
	{
7072
		foreach ($this->fields as $field => $info)
7073
		{
7074
			if($this->isDate($info))
7075
			{
7076
				if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
7077
				else $this->{$field} = strtotime($obj->{$field});
7078
			}
7079
			elseif($this->isArray($info))
7080
			{
7081
				if(! empty($obj->{$field})) {
7082
					$this->{$field} = @unserialize($obj->{$field});
7083
					// Hack for data not in UTF8
7084
					if($this->{$field } === false) @unserialize(utf8_decode($obj->{$field}));
7085
				} else {
7086
					$this->{$field} = array();
7087
				}
7088
			}
7089
			elseif($this->isInt($info))
7090
			{
7091
				if ($field == 'rowid') $this->id = (int) $obj->{$field};
7092
				else $this->{$field} = (int) $obj->{$field};
7093
			}
7094
			elseif($this->isFloat($info))
7095
			{
7096
				$this->{$field} = (double) $obj->{$field};
7097
			}
7098
			elseif($this->isNull($info))
7099
			{
7100
				$val = $obj->{$field};
7101
				// zero is not null
7102
				$this->{$field} = (is_null($val) || (empty($val) && $val!==0 && $val!=='0') ? null : $val);
7103
			}
7104
			else
7105
			{
7106
				$this->{$field} = $obj->{$field};
7107
			}
7108
		}
7109
7110
		// If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
7111
		if (! isset($this->fields['ref']) && isset($this->id)) $this->ref = $this->id;
7112
	}
7113
7114
	/**
7115
	 * Function to concat keys of fields
7116
	 *
7117
	 * @return string
7118
	 */
7119
	protected function getFieldList()
7120
	{
7121
		$keys = array_keys($this->fields);
7122
		return implode(',', $keys);
7123
	}
7124
7125
	/**
7126
	 * Add quote to field value if necessary
7127
	 *
7128
	 * @param 	string|int	$value			Value to protect
7129
	 * @param	array		$fieldsentry	Properties of field
7130
	 * @return 	string
7131
	 */
7132
    protected function quote($value, $fieldsentry)
7133
    {
7134
		if (is_null($value)) return 'NULL';
7135
		elseif (preg_match('/^(int|double|real)/i', $fieldsentry['type'])) return $this->db->escape("$value");
7136
		else return "'".$this->db->escape($value)."'";
7137
	}
7138
7139
7140
	/**
7141
	 * Create object into database
7142
	 *
7143
	 * @param  User $user      User that creates
7144
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
7145
	 * @return int             <0 if KO, Id of created object if OK
7146
	 */
7147
	public function createCommon(User $user, $notrigger = false)
7148
	{
7149
		global $langs;
7150
7151
		$error = 0;
7152
7153
		$now=dol_now();
7154
7155
		$fieldvalues = $this->setSaveQuery();
7156
		if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) $fieldvalues['date_creation']=$this->db->idate($now);
7157
		if (array_key_exists('fk_user_creat', $fieldvalues) && ! ($fieldvalues['fk_user_creat'] > 0)) $fieldvalues['fk_user_creat']=$user->id;
7158
		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
7159
7160
		$keys=array();
7161
		$values = array();
7162
		foreach ($fieldvalues as $k => $v) {
7163
			$keys[$k] = $k;
7164
			$value = $this->fields[$k];
7165
			$values[$k] = $this->quote($v, $value);
7166
		}
7167
7168
		// Clean and check mandatory
7169
		foreach($keys as $key)
7170
		{
7171
			// If field is an implicit foreign key field
7172
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';
7173
			if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';
7174
7175
			//var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7176
			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...
7177
			{
7178
				$error++;
7179
				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7180
			}
7181
7182
			// If field is an implicit foreign key field
7183
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) $values[$key]='null';
7184
			if (! empty($this->fields[$key]['foreignkey']) && empty($values[$key])) $values[$key]='null';
7185
		}
7186
7187
		if ($error) return -1;
7188
7189
		$this->db->begin();
7190
7191
		if (! $error)
7192
		{
7193
			$sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
7194
			$sql.= ' ('.implode(", ", $keys).')';
7195
			$sql.= ' VALUES ('.implode(", ", $values).')';
7196
7197
			$res = $this->db->query($sql);
7198
			if ($res===false) {
7199
				$error++;
7200
				$this->errors[] = $this->db->lasterror();
7201
			}
7202
		}
7203
7204
		if (! $error)
7205
		{
7206
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
7207
		}
7208
7209
		// Create extrafields
7210
		if (! $error)
7211
		{
7212
			$result=$this->insertExtraFields();
7213
			if ($result < 0) $error++;
7214
		}
7215
7216
		// Triggers
7217
		if (! $error && ! $notrigger)
7218
		{
7219
			// Call triggers
7220
			$result=$this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
7221
			if ($result < 0) { $error++; }
7222
			// End call triggers
7223
		}
7224
7225
		// Commit or rollback
7226
		if ($error) {
7227
			$this->db->rollback();
7228
			return -1;
7229
		} else {
7230
			$this->db->commit();
7231
			return $this->id;
7232
		}
7233
	}
7234
7235
7236
	/**
7237
	 * Load object in memory from the database
7238
	 *
7239
	 * @param	int    $id				Id object
7240
	 * @param	string $ref				Ref
7241
	 * @param	string	$morewhere		More SQL filters (' AND ...')
7242
	 * @return 	int         			<0 if KO, 0 if not found, >0 if OK
7243
	 */
7244
	public function fetchCommon($id, $ref = null, $morewhere = '')
7245
	{
7246
		if (empty($id) && empty($ref) && empty($morewhere)) return -1;
7247
7248
		$sql = 'SELECT '.$this->getFieldList();
7249
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
7250
7251
		if (!empty($id))  $sql.= ' WHERE rowid = '.$id;
7252
		elseif (!empty($ref)) $sql.= " WHERE ref = ".$this->quote($ref, $this->fields['ref']);
7253
		else $sql.=' WHERE 1 = 1';	// usage with empty id and empty ref is very rare
7254
		if ($morewhere)   $sql.= $morewhere;
7255
		$sql.=' LIMIT 1';	// This is a fetch, to be sure to get only one record
7256
7257
		$res = $this->db->query($sql);
7258
		if ($res)
7259
		{
7260
			$obj = $this->db->fetch_object($res);
7261
			if ($obj)
7262
			{
7263
				$this->setVarsFromFetchObj($obj);
7264
				return $this->id;
7265
			}
7266
			else
7267
			{
7268
				return 0;
7269
			}
7270
		}
7271
		else
7272
		{
7273
			$this->error = $this->db->lasterror();
7274
			$this->errors[] = $this->error;
7275
			return -1;
7276
		}
7277
	}
7278
7279
	/**
7280
	 * Update object into database
7281
	 *
7282
	 * @param  User $user      	User that modifies
7283
	 * @param  bool $notrigger 	false=launch triggers after, true=disable triggers
7284
	 * @return int             	<0 if KO, >0 if OK
7285
	 */
7286
	public function updateCommon(User $user, $notrigger = false)
7287
	{
7288
		global $conf, $langs;
7289
7290
		$error = 0;
7291
7292
		$now=dol_now();
7293
7294
		$fieldvalues = $this->setSaveQuery();
7295
		if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) $fieldvalues['date_modification']=$this->db->idate($now);
7296
		if (array_key_exists('fk_user_modif', $fieldvalues) && ! ($fieldvalues['fk_user_modif'] > 0)) $fieldvalues['fk_user_modif']=$user->id;
7297
		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
7298
7299
		$keys=array();
7300
		$values = array();
7301
		foreach ($fieldvalues as $k => $v) {
7302
			$keys[$k] = $k;
7303
			$value = $this->fields[$k];
7304
			$values[$k] = $this->quote($v, $value);
7305
			$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...
7306
		}
7307
7308
		// Clean and check mandatory
7309
		foreach($keys as $key)
7310
		{
7311
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';		// This is an implicit foreign key field
7312
			if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';					// This is an explicit foreign key field
7313
7314
			//var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7315
			/*
7316
			if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
7317
			{
7318
				$error++;
7319
				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7320
			}*/
7321
		}
7322
7323
		$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...
7324
7325
		$this->db->begin();
7326
		if (! $error)
7327
		{
7328
			$res = $this->db->query($sql);
7329
			if ($res===false)
7330
			{
7331
				$error++;
7332
				$this->errors[] = $this->db->lasterror();
7333
			}
7334
		}
7335
7336
		// Update extrafield
7337
		if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
7338
		{
7339
			$result=$this->insertExtraFields();
7340
			if ($result < 0)
7341
			{
7342
				$error++;
7343
			}
7344
		}
7345
7346
		// Triggers
7347
		if (! $error && ! $notrigger)
7348
		{
7349
			// Call triggers
7350
			$result=$this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
7351
			if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail
7352
			// End call triggers
7353
		}
7354
7355
		// Commit or rollback
7356
		if ($error) {
7357
			$this->db->rollback();
7358
			return -1;
7359
		} else {
7360
			$this->db->commit();
7361
			return $this->id;
7362
		}
7363
	}
7364
7365
	/**
7366
	 * Delete object in database
7367
	 *
7368
	 * @param 	User 	$user       			User that deletes
7369
	 * @param 	bool 	$notrigger  			false=launch triggers after, true=disable triggers
7370
	 * @param	int		$forcechilddeletion		0=no, 1=Force deletion of children
7371
	 * @return 	int             				<=0 if KO, >0 if OK
7372
	 */
7373
	public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0)
7374
	{
7375
		$error=0;
7376
7377
		$this->db->begin();
7378
7379
		if ($forcechilddeletion)
7380
		{
7381
			foreach($this->childtables as $table)
7382
			{
7383
				$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$table.' WHERE '.$this->fk_element.' = '.$this->id;
7384
				$resql = $this->db->query($sql);
7385
				if (! $resql)
7386
				{
7387
					$this->error=$this->db->lasterror();
7388
					$this->errors[]=$this->error;
7389
					$this->db->rollback();
7390
					return -1;
7391
				}
7392
			}
7393
		}
7394
		elseif (! empty($this->fk_element) && ! empty($this->childtables))	// If object has childs linked with a foreign key field, we check all child tables.
7395
		{
7396
			$objectisused = $this->isObjectUsed($this->id);
7397
			if (! empty($objectisused))
7398
			{
7399
				dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
7400
				$this->error='ErrorRecordHasChildren';
7401
				$this->errors[]=$this->error;
7402
				$this->db->rollback();
7403
				return 0;
7404
			}
7405
		}
7406
7407
		if (! $error) {
7408
			if (! $notrigger) {
7409
				// Call triggers
7410
				$result=$this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
7411
				if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail
7412
				// End call triggers
7413
			}
7414
		}
7415
7416
		if (! $error && ! empty($this->isextrafieldmanaged))
7417
		{
7418
			$sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element."_extrafields";
7419
			$sql.= " WHERE fk_object=" . $this->id;
7420
7421
			$resql = $this->db->query($sql);
7422
			if (! $resql)
7423
			{
7424
				$this->errors[] = $this->db->lasterror();
7425
				$error++;
7426
			}
7427
		}
7428
7429
		if (! $error)
7430
		{
7431
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
7432
7433
			$res = $this->db->query($sql);
7434
			if($res===false) {
7435
				$error++;
7436
				$this->errors[] = $this->db->lasterror();
7437
			}
7438
		}
7439
7440
		// Commit or rollback
7441
		if ($error) {
7442
			$this->db->rollback();
7443
			return -1;
7444
		} else {
7445
			$this->db->commit();
7446
			return 1;
7447
		}
7448
	}
7449
7450
	/**
7451
	 * Initialise object with example values
7452
	 * Id must be 0 if object instance is a specimen
7453
	 *
7454
	 * @return void
7455
	 */
7456
	public function initAsSpecimenCommon()
7457
	{
7458
	    global $user;
7459
7460
		$this->id = 0;
7461
		if (array_key_exists('label', $this->fields)) $this->label='This is label';
7462
		if (array_key_exists('note_public', $this->fields)) $this->note_public='Public note';
7463
		if (array_key_exists('note_private', $this->fields)) $this->note_private='Private note';
7464
		if (array_key_exists('date_creation', $this->fields)) $this->date_creation=(dol_now()-3600*24);
7465
		if (array_key_exists('date_modification', $this->fields)) $this->date_modification=(dol_now()-3600*24);
7466
		if (array_key_exists('fk_user_creat', $this->fields)) $this->fk_user_creat=$user->id;
7467
		if (array_key_exists('fk_user_modif', $this->fields)) $this->fk_user_modif=$user->id;
7468
		if (array_key_exists('date', $this->fields)) $this->date=dol_now();
7469
		// ...
7470
	}
7471
7472
7473
	/* Part for comments */
7474
7475
	/**
7476
	 * Load comments linked with current task
7477
	 *	@return boolean	1 if ok
7478
	 */
7479
	public function fetchComments()
7480
	{
7481
		require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
7482
7483
		$comment = new Comment($this->db);
7484
		$result=$comment->fetchAllFor($this->element, $this->id);
7485
		if ($result<0) {
7486
			$this->errors=array_merge($this->errors, $comment->errors);
7487
			return -1;
7488
		} else {
7489
			$this->comments = $comment->comments;
7490
		}
7491
		return count($this->comments);
7492
	}
7493
7494
    /**
7495
     * Return nb comments already posted
7496
     *
7497
     * @return int
7498
     */
7499
    public function getNbComments()
7500
    {
7501
        return count($this->comments);
7502
    }
7503
7504
    /**
7505
     * Trim object parameters
7506
     * @param string[] $parameters array of parameters to trim
7507
     *
7508
     * @return void
7509
     */
7510
    public function trimParameters($parameters)
7511
    {
7512
        if (!is_array($parameters)) return;
7513
        foreach ($parameters as $parameter) {
7514
            if (isset($this->$parameter)) {
7515
                $this->$parameter = trim($this->$parameter);
7516
            }
7517
        }
7518
    }
7519
}
7520