Completed
Branch develop (d233c1)
by
unknown
31:52
created

CommonObject::getLastMainDocLink()   D

Complexity

Conditions 14
Paths 112

Size

Total Lines 83
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 14
eloc 32
nc 112
nop 3
dl 0
loc 83
rs 4.7488
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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