Completed
Branch develop (bb9960)
by
unknown
27:34
created

CommonObject::updateCommon()   C

Complexity

Conditions 11
Paths 90

Size

Total Lines 51
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 31
nc 90
nop 2
dl 0
loc 51
rs 5.7333
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
 *
17
 * This program is free software; you can redistribute it and/or modify
18
 * it under the terms of the GNU General Public License as published by
19
 * the Free Software Foundation; either version 3 of the License, or
20
 * (at your option) any later version.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
 * GNU General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU General Public License
28
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29
 */
30
31
/**
32
 *	\file       htdocs/core/class/commonobject.class.php
33
 *	\ingroup    core
34
 *	\brief      File of parent class of all other business classes (invoices, contracts, proposals, orders, ...)
35
 */
36
37
38
/**
39
 *	Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
40
 */
41
abstract class CommonObject
42
{
43
	/**
44
	 * @var DoliDb		Database handler (result of a new DoliDB)
45
	 */
46
	public $db;
47
	/**
48
	 * @var int The object identifier
49
	 */
50
	public $id;
51
	/**
52
	 * @var string 		Error string
53
	 * @deprecated		Use instead the array of error strings
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 string[]	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 CommonObject An originating object?
154
	 * @see fetch_origin()
155
	 */
156
	public $origin;
157
	/**
158
	 * @var int The originating object?
159
	 * @see fetch_origin(), 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
	 * Return if an object manage the multicompany field and how.
345
	 *
346
	 * @return	int				0=No entity field managed, 1=Test with field entity, 2=Test with link to thirdparty (and sales representative)
347
	 */
348
	function getIsmultientitymanaged()
349
	{
350
		return $this->ismultientitymanaged;
351
	}
352
353
	/**
354
	 * Check an object id/ref exists
355
	 * If you don't need/want to instantiate object and just need to know if object exists, use this method instead of fetch
356
	 *
357
	 *  @param	string	$element   	String of element ('product', 'facture', ...)
358
	 *  @param	int		$id      	Id of object
359
	 *  @param  string	$ref     	Ref of object to check
360
	 *  @param	string	$ref_ext	Ref ext of object to check
361
	 *  @return int     			<0 if KO, 0 if OK but not found, >0 if OK and exists
362
	 */
363
	static function isExistingObject($element, $id, $ref='', $ref_ext='')
364
	{
365
		global $db,$conf;
366
367
		$sql = "SELECT rowid, ref, ref_ext";
368
		$sql.= " FROM ".MAIN_DB_PREFIX.$element;
369
		$sql.= " WHERE entity IN (".getEntity($element).")" ;
370
371
		if ($id > 0) $sql.= " AND rowid = ".$db->escape($id);
372
		else if ($ref) $sql.= " AND ref = '".$db->escape($ref)."'";
373
		else if ($ref_ext) $sql.= " AND ref_ext = '".$db->escape($ref_ext)."'";
374
		else {
375
			$error='ErrorWrongParameters';
376
			dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
377
			return -1;
378
		}
379
		if ($ref || $ref_ext) $sql.= " AND entity = ".$conf->entity;
380
381
		dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
382
		$resql = $db->query($sql);
383
		if ($resql)
384
		{
385
			$num=$db->num_rows($resql);
386
			if ($num > 0) return 1;
387
			else return 0;
388
		}
389
		return -1;
390
	}
391
392
	/**
393
	 * Method to output saved errors
394
	 *
395
	 * @return	string		String with errors
396
	 */
397
	function errorsToString()
398
	{
399
		return $this->error.(is_array($this->errors)?(($this->error!=''?', ':'').join(', ',$this->errors)):'');
400
	}
401
402
	/**
403
	 *	Return full name (civility+' '+name+' '+lastname)
404
	 *
405
	 *	@param	Translate	$langs			Language object for translation of civility
406
	 *	@param	int			$option			0=No option, 1=Add civility
407
	 * 	@param	int			$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname
408
	 * 	@param	int			$maxlen			Maximum length
409
	 * 	@return	string						String with full name
410
	 */
411
	function getFullName($langs,$option=0,$nameorder=-1,$maxlen=0)
412
	{
413
		//print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n";
414
		$lastname=$this->lastname;
415
		$firstname=$this->firstname;
416
		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:'')))));
417
418
		$ret='';
419
		if ($option && $this->civility_id)
420
		{
421
			if ($langs->transnoentitiesnoconv("Civility".$this->civility_id)!="Civility".$this->civility_id) $ret.=$langs->transnoentitiesnoconv("Civility".$this->civility_id).' ';
422
			else $ret.=$this->civility_id.' ';
423
		}
424
425
		$ret.=dolGetFirstLastname($firstname, $lastname, $nameorder);
426
427
		return dol_trunc($ret,$maxlen);
428
	}
429
430
	/**
431
	 * 	Return full address of contact
432
	 *
433
	 * 	@param		int			$withcountry		1=Add country into address string
434
	 *  @param		string		$sep				Separator to use to build string
435
	 *	@return		string							Full address string
436
	 */
437
	function getFullAddress($withcountry=0,$sep="\n")
438
	{
439
		if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country)))
440
		{
441
			require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
442
			$tmparray=getCountry($this->country_id,'all');
443
			$this->country_code=$tmparray['code'];
444
			$this->country     =$tmparray['label'];
445
		}
446
447
		return dol_format_address($this, $withcountry, $sep);
448
	}
449
450
451
	/**
452
	 * 	Return full address for banner
453
	 *
454
	 * 	@param		string		$htmlkey            HTML id to make banner content unique
455
	 *  @param      Object      $object				Object (thirdparty, thirdparty of contact for contact, null for a member)
456
	 *	@return		string							Full address string
457
	 */
458
	function getBannerAddress($htmlkey, $object)
459
	{
460
		global $conf, $langs;
461
462
		$countriesusingstate=array('AU','US','IN','GB','ES','UK','TR');    // See also option MAIN_FORCE_STATE_INTO_ADDRESS
463
464
		$contactid=0;
465
		$thirdpartyid=0;
466
		if ($this->element == 'societe')
467
		{
468
			$thirdpartyid=$this->id;
469
		}
470
		if ($this->element == 'contact')
471
		{
472
			$contactid=$this->id;
473
			$thirdpartyid=$object->fk_soc;
474
		}
475
		if ($this->element == 'user')
476
		{
477
			$contactid=$this->contact_id;
478
			$thirdpartyid=$object->fk_soc;
479
		}
480
481
		$out='<!-- BEGIN part to show address block -->';
482
483
		$outdone=0;
484
		$coords = $this->getFullAddress(1,', ');
485
		if ($coords)
486
		{
487
			if (! empty($conf->use_javascript_ajax))
488
			{
489
				$namecoords = $this->getFullName($langs,1).'<br>'.$coords;
490
				// hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile
491
				$out.='<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">';
492
				$out.=img_picto($langs->trans("Address"), 'object_address.png');
493
				$out.='</a> ';
494
			}
495
			$out.=dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); $outdone++;
496
			$outdone++;
497
		}
498
499
		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
500
				&& empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state)
501
		{
502
			$out.=($outdone?' - ':'').$this->state;
503
			$outdone++;
504
		}
505
506
		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>':'');
507
		if (! empty($this->phone) && empty($this->phone_pro)) {		// For objects that store pro phone into ->phone
508
			$out.=dol_print_phone($this->phone,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
509
		}
510
		if (! empty($this->phone_pro)) {
511
			$out.=dol_print_phone($this->phone_pro,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
512
		}
513
		if (! empty($this->phone_mobile)) {
514
			$out.=dol_print_phone($this->phone_mobile,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','mobile',$langs->trans("PhoneMobile")); $outdone++;
515
		}
516
		if (! empty($this->phone_perso)) {
517
			$out.=dol_print_phone($this->phone_perso,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePerso")); $outdone++;
518
		}
519
		if (! empty($this->fax)) {
520
			$out.=dol_print_phone($this->fax,$this->country_code,$contactid,$thirdpartyid,'AC_FAX','&nbsp;','fax',$langs->trans("Fax")); $outdone++;
521
		}
522
		if (! empty($this->office_phone)) {
523
			$out.=dol_print_phone($this->office_phone,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
524
		}
525
		if (! empty($this->user_mobile)) {
526
			$out.=dol_print_phone($this->user_mobile,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','mobile',$langs->trans("PhoneMobile")); $outdone++;
527
		}
528
		if (! empty($this->office_fax)) {
529
			$out.=dol_print_phone($this->fax,$this->country_code,$contactid,$thirdpartyid,'AC_FAX','&nbsp;','fax',$langs->trans("Fax")); $outdone++;
530
		}
531
532
		$out.='<div style="clear: both;"></div>';
533
		$outdone=0;
534
		if (! empty($this->email))
535
		{
536
			$out.=dol_print_email($this->email,$this->id,$object->id,'AC_EMAIL',0,0,1);
537
			$outdone++;
538
		}
539
		if (! empty($this->url))
540
		{
541
			$out.=dol_print_url($this->url,'_goout',0,1);
542
			$outdone++;
543
		}
544
		if (! empty($conf->skype->enabled))
545
		{
546
			$out.='<div style="clear: both;"></div>';
547
			if ($this->skype) $out.=dol_print_skype($this->skype,$this->id,$object->id,'AC_SKYPE');
548
			$outdone++;
549
		}
550
551
		$out.='<!-- END Part to show address block -->';
552
553
		return $out;
554
	}
555
556
	/**
557
	 *  Add a link between element $this->element and a contact
558
	 *
559
	 *  @param	int		$fk_socpeople       Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
560
	 *  @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
561
	 *  @param  string	$source             external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
562
	 *  @param  int		$notrigger			Disable all triggers
563
	 *  @return int                 		<0 if KO, >0 if OK
564
	 */
565
	function add_contact($fk_socpeople, $type_contact, $source='external',$notrigger=0)
566
	{
567
		global $user,$langs;
568
569
570
		dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
571
572
		// Check parameters
573
		if ($fk_socpeople <= 0)
574
		{
575
			$langs->load("errors");
576
			$this->error=$langs->trans("ErrorWrongValueForParameterX","1");
577
			dol_syslog(get_class($this)."::add_contact ".$this->error,LOG_ERR);
578
			return -1;
579
		}
580
		if (! $type_contact)
581
		{
582
			$langs->load("errors");
583
			$this->error=$langs->trans("ErrorWrongValueForParameterX","2");
584
			dol_syslog(get_class($this)."::add_contact ".$this->error,LOG_ERR);
585
			return -2;
586
		}
587
588
		$id_type_contact=0;
589
		if (is_numeric($type_contact))
590
		{
591
			$id_type_contact=$type_contact;
592
		}
593
		else
594
		{
595
			// On recherche id type_contact
596
			$sql = "SELECT tc.rowid";
597
			$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
598
			$sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
599
			$sql.= " AND tc.source='".$this->db->escape($source)."'";
600
			$sql.= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
601
			//print $sql;
602
			$resql=$this->db->query($sql);
603
			if ($resql)
604
			{
605
				$obj = $this->db->fetch_object($resql);
606
				if ($obj) $id_type_contact=$obj->rowid;
607
			}
608
		}
609
610
		if ($id_type_contact == 0)
611
		{
612
			$this->error='CODE_NOT_VALID_FOR_THIS_ELEMENT';
613
			dol_syslog("CODE_NOT_VALID_FOR_THIS_ELEMENT");
614
			return -3;
615
		}
616
617
		$datecreate = dol_now();
618
619
		// Socpeople must have already been added by some a trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
620
		$TListeContacts=$this->liste_contact(-1, $source);
621
		$already_added=false;
622
		if(!empty($TListeContacts)) {
623
			foreach($TListeContacts as $array_contact) {
624
				if($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
625
					$already_added=true;
626
					break;
627
				}
628
			}
629
		}
630
631
		if(!$already_added) {
632
633
			$this->db->begin();
634
635
			// Insertion dans la base
636
			$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact";
637
			$sql.= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
638
			$sql.= " VALUES (".$this->id.", ".$fk_socpeople." , " ;
639
			$sql.= "'".$this->db->idate($datecreate)."'";
640
			$sql.= ", 4, ". $id_type_contact;
641
			$sql.= ")";
642
643
			$resql=$this->db->query($sql);
644
			if ($resql)
645
			{
646
				if (! $notrigger)
647
				{
648
					$result=$this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
649
					if ($result < 0)
650
					{
651
						$this->db->rollback();
652
						return -1;
653
					}
654
				}
655
656
				$this->db->commit();
657
				return 1;
658
			}
659
			else
660
			{
661
				if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
662
				{
663
					$this->error=$this->db->errno();
664
					$this->db->rollback();
665
					echo 'err rollback';
666
					return -2;
667
				}
668
				else
669
				{
670
					$this->error=$this->db->error();
671
					$this->db->rollback();
672
					return -1;
673
				}
674
			}
675
		} else return 0;
676
	}
677
678
	/**
679
	 *    Copy contact from one element to current
680
	 *
681
	 *    @param    CommonObject    $objFrom    Source element
682
	 *    @param    string          $source     Nature of contact ('internal' or 'external')
683
	 *    @return   int                         >0 if OK, <0 if KO
684
	 */
685
	function copy_linked_contact($objFrom, $source='internal')
686
	{
687
		$contacts = $objFrom->liste_contact(-1, $source);
688
		foreach($contacts as $contact)
689
		{
690
			if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0)
691
			{
692
				$this->error=$this->db->lasterror();
693
				return -1;
694
			}
695
		}
696
		return 1;
697
	}
698
699
	/**
700
	 *      Update a link to contact line
701
	 *
702
	 *      @param	int		$rowid              Id of line contact-element
703
	 * 		@param	int		$statut	            New status of link
704
	 *      @param  int		$type_contact_id    Id of contact type (not modified if 0)
705
	 *      @param  int		$fk_socpeople	    Id of soc_people to update (not modified if 0)
706
	 *      @return int                 		<0 if KO, >= 0 if OK
707
	 */
708
	function update_contact($rowid, $statut, $type_contact_id=0, $fk_socpeople=0)
709
	{
710
		// Insertion dans la base
711
		$sql = "UPDATE ".MAIN_DB_PREFIX."element_contact set";
712
		$sql.= " statut = ".$statut;
713
		if ($type_contact_id) $sql.= ", fk_c_type_contact = '".$type_contact_id ."'";
714
		if ($fk_socpeople) $sql.= ", fk_socpeople = '".$fk_socpeople ."'";
715
		$sql.= " where rowid = ".$rowid;
716
		$resql=$this->db->query($sql);
717
		if ($resql)
718
		{
719
			return 0;
720
		}
721
		else
722
		{
723
			$this->error=$this->db->lasterror();
724
			return -1;
725
		}
726
	}
727
728
	/**
729
	 *    Delete a link to contact line
730
	 *
731
	 *    @param	int		$rowid			Id of contact link line to delete
732
	 *    @param	int		$notrigger		Disable all triggers
733
	 *    @return   int						>0 if OK, <0 if KO
734
	 */
735
	function delete_contact($rowid, $notrigger=0)
736
	{
737
		global $user;
738
739
740
		$this->db->begin();
741
742
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
743
		$sql.= " WHERE rowid =".$rowid;
744
745
		dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
746
		if ($this->db->query($sql))
747
		{
748
			if (! $notrigger)
749
			{
750
				$result=$this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
751
				if ($result < 0) { $this->db->rollback(); return -1; }
752
			}
753
754
			$this->db->commit();
755
			return 1;
756
		}
757
		else
758
		{
759
			$this->error=$this->db->lasterror();
760
			$this->db->rollback();
761
			return -1;
762
		}
763
	}
764
765
	/**
766
	 *    Delete all links between an object $this and all its contacts
767
	 *
768
	 *	  @param	string	$source		'' or 'internal' or 'external'
769
	 *	  @param	string	$code		Type of contact (code or id)
770
	 *    @return   int					>0 if OK, <0 if KO
771
	 */
772
	function delete_linked_contact($source='',$code='')
773
	{
774
		$temp = array();
775
		$typeContact = $this->liste_type_contact($source,'',0,0,$code);
776
777
		foreach($typeContact as $key => $value)
778
		{
779
			array_push($temp,$key);
780
		}
781
		$listId = implode(",", $temp);
782
783
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
784
		$sql.= " WHERE element_id = ".$this->id;
785
		if ($listId)
786
			$sql.= " AND fk_c_type_contact IN (".$listId.")";
787
788
		dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
789
		if ($this->db->query($sql))
790
		{
791
			return 1;
792
		}
793
		else
794
		{
795
			$this->error=$this->db->lasterror();
796
			return -1;
797
		}
798
	}
799
800
	/**
801
	 *    Get array of all contacts for an object
802
	 *
803
	 *    @param	int			$statut		Status of links to get (-1=all)
804
	 *    @param	string		$source		Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)
805
	 *    @param	int         $list       0:Return array contains all properties, 1:Return array contains just id
806
	 *    @param    string      $code       Filter on this code of contact type ('SHIPPING', 'BILLING', ...)
807
	 *    @return	array		            Array of contacts
808
	 */
809
	function liste_contact($statut=-1,$source='external',$list=0,$code='')
810
	{
811
		global $langs;
812
813
		$tab=array();
814
815
		$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
816
		if ($source == 'internal') $sql.=", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
817
		if ($source == 'external' || $source == 'thirdparty') $sql.=", t.fk_soc as socid, t.statut as statuscontact";
818
		$sql.= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
819
		$sql.= ", tc.source, tc.element, tc.code, tc.libelle";
820
		$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
821
		$sql.= ", ".MAIN_DB_PREFIX."element_contact ec";
822
		if ($source == 'internal') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
823
		if ($source == 'external'|| $source == 'thirdparty') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
824
		$sql.= " WHERE ec.element_id =".$this->id;
825
		$sql.= " AND ec.fk_c_type_contact=tc.rowid";
826
		$sql.= " AND tc.element='".$this->db->escape($this->element)."'";
827
		if ($code) $sql.= " AND tc.code = '".$this->db->escape($code)."'";
828
		if ($source == 'internal') $sql.= " AND tc.source = 'internal'";
829
		if ($source == 'external' || $source == 'thirdparty') $sql.= " AND tc.source = 'external'";
830
		$sql.= " AND tc.active=1";
831
		if ($statut >= 0) $sql.= " AND ec.statut = '".$statut."'";
832
		$sql.=" ORDER BY t.lastname ASC";
833
834
		dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
835
		$resql=$this->db->query($sql);
836
		if ($resql)
837
		{
838
			$num=$this->db->num_rows($resql);
839
			$i=0;
840
			while ($i < $num)
841
			{
842
				$obj = $this->db->fetch_object($resql);
843
844
				if (! $list)
845
				{
846
					$transkey="TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
847
					$libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
848
					$tab[$i]=array('source'=>$obj->source,'socid'=>$obj->socid,'id'=>$obj->id,
849
								   'nom'=>$obj->lastname,      // For backward compatibility
850
								   'civility'=>$obj->civility, 'lastname'=>$obj->lastname, 'firstname'=>$obj->firstname, 'email'=>$obj->email, 'login'=>$obj->login, 'photo'=>$obj->photo, 'statuscontact'=>$obj->statuscontact,
851
								   'rowid'=>$obj->rowid, 'code'=>$obj->code, 'libelle'=>$libelle_type, 'status'=>$obj->statuslink, 'fk_c_type_contact'=>$obj->fk_c_type_contact);
852
				}
853
				else
854
				{
855
					$tab[$i]=$obj->id;
856
				}
857
858
				$i++;
859
			}
860
861
			return $tab;
862
		}
863
		else
864
		{
865
			$this->error=$this->db->lasterror();
866
			dol_print_error($this->db);
867
			return -1;
868
		}
869
	}
870
871
872
	/**
873
	 * 		Update status of a contact linked to object
874
	 *
875
	 * 		@param	int		$rowid		Id of link between object and contact
876
	 * 		@return	int					<0 if KO, >=0 if OK
877
	 */
878
	function swapContactStatus($rowid)
879
	{
880
		$sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
881
		$sql.= " tc.code, tc.libelle";
882
		//$sql.= ", s.fk_soc";
883
		$sql.= " FROM (".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as tc)";
884
		//$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
885
		$sql.= " WHERE ec.rowid =".$rowid;
886
		$sql.= " AND ec.fk_c_type_contact=tc.rowid";
887
		$sql.= " AND tc.element = '".$this->db->escape($this->element)."'";
888
889
		dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
890
		$resql=$this->db->query($sql);
891
		if ($resql)
892
		{
893
			$obj = $this->db->fetch_object($resql);
894
			$newstatut = ($obj->statut == 4) ? 5 : 4;
895
			$result = $this->update_contact($rowid, $newstatut);
896
			$this->db->free($resql);
897
			return $result;
898
		}
899
		else
900
		{
901
			$this->error=$this->db->error();
902
			dol_print_error($this->db);
903
			return -1;
904
		}
905
906
	}
907
908
	/**
909
	 *      Return array with list of possible values for type of contacts
910
	 *
911
	 *      @param	string	$source     'internal', 'external' or 'all'
912
	 *      @param	string	$order		Sort order by : 'position', 'code', 'rowid'...
913
	 *      @param  int		$option     0=Return array id->label, 1=Return array code->label
914
	 *      @param  int		$activeonly 0=all status of contact, 1=only the active
915
	 *		@param	string	$code		Type of contact (Example: 'CUSTOMER', 'SERVICE')
916
	 *      @return array       		Array list of type of contacts (id->label if option=0, code->label if option=1)
917
	 */
918
	function liste_type_contact($source='internal', $order='position', $option=0, $activeonly=0, $code='')
919
	{
920
		global $langs;
921
922
		if (empty($order)) $order='position';
923
		if ($order == 'position') $order.=',code';
924
925
		$tab = array();
926
		$sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
927
		$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
928
		$sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
929
		if ($activeonly == 1) $sql.= " AND tc.active=1"; // only the active types
930
		if (! empty($source) && $source != 'all') $sql.= " AND tc.source='".$this->db->escape($source)."'";
931
		if (! empty($code)) $sql.= " AND tc.code='".$this->db->escape($code)."'";
932
		$sql.= $this->db->order($order,'ASC');
933
934
		//print "sql=".$sql;
935
		$resql=$this->db->query($sql);
936
		if ($resql)
937
		{
938
			$num=$this->db->num_rows($resql);
939
			$i=0;
940
			while ($i < $num)
941
			{
942
				$obj = $this->db->fetch_object($resql);
943
944
				$transkey="TypeContact_".$this->element."_".$source."_".$obj->code;
945
				$libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
946
				if (empty($option)) $tab[$obj->rowid]=$libelle_type;
947
				else $tab[$obj->code]=$libelle_type;
948
				$i++;
949
			}
950
			return $tab;
951
		}
952
		else
953
		{
954
			$this->error=$this->db->lasterror();
955
			//dol_print_error($this->db);
956
			return null;
957
		}
958
	}
959
960
	/**
961
	 *      Return id of contacts for a source and a contact code.
962
	 *      Example: contact client de facturation ('external', 'BILLING')
963
	 *      Example: contact client de livraison ('external', 'SHIPPING')
964
	 *      Example: contact interne suivi paiement ('internal', 'SALESREPFOLL')
965
	 *
966
	 *		@param	string	$source		'external' or 'internal'
967
	 *		@param	string	$code		'BILLING', 'SHIPPING', 'SALESREPFOLL', ...
968
	 *		@param	int		$status		limited to a certain status
969
	 *      @return array       		List of id for such contacts
970
	 */
971
	function getIdContact($source,$code,$status=0)
972
	{
973
		global $conf;
974
975
		$result=array();
976
		$i=0;
977
		//cas particulier pour les expeditions
978
		if($this->element=='shipping' && $this->origin_id != 0) {
979
			$id=$this->origin_id;
980
			$element='commande';
981
		} else {
982
			$id=$this->id;
983
			$element=$this->element;
984
		}
985
986
		$sql = "SELECT ec.fk_socpeople";
987
		$sql.= " FROM ".MAIN_DB_PREFIX."element_contact as ec,";
988
		if ($source == 'internal') $sql.= " ".MAIN_DB_PREFIX."user as c,";
989
		if ($source == 'external') $sql.= " ".MAIN_DB_PREFIX."socpeople as c,";
990
		$sql.= " ".MAIN_DB_PREFIX."c_type_contact as tc";
991
		$sql.= " WHERE ec.element_id = ".$id;
992
		$sql.= " AND ec.fk_socpeople = c.rowid";
993
		if ($source == 'internal') $sql.= " AND c.entity IN (0,".$conf->entity.")";
994
		if ($source == 'external') $sql.= " AND c.entity IN (".getEntity('societe').")";
995
		$sql.= " AND ec.fk_c_type_contact = tc.rowid";
996
		$sql.= " AND tc.element = '".$element."'";
997
		$sql.= " AND tc.source = '".$source."'";
998
		$sql.= " AND tc.code = '".$code."'";
999
		$sql.= " AND tc.active = 1";
1000
		if ($status) $sql.= " AND ec.statut = ".$status;
1001
1002
		dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1003
		$resql=$this->db->query($sql);
1004
		if ($resql)
1005
		{
1006
			while ($obj = $this->db->fetch_object($resql))
1007
			{
1008
				$result[$i]=$obj->fk_socpeople;
1009
				$i++;
1010
			}
1011
		}
1012
		else
1013
		{
1014
			$this->error=$this->db->error();
1015
			return null;
1016
		}
1017
1018
		return $result;
1019
	}
1020
1021
	/**
1022
	 *		Load object contact with id=$this->contactid into $this->contact
1023
	 *
1024
	 *		@param	int		$contactid      Id du contact. Use this->contactid if empty.
1025
	 *		@return	int						<0 if KO, >0 if OK
1026
	 */
1027
	function fetch_contact($contactid=null)
1028
	{
1029
		if (empty($contactid)) $contactid=$this->contactid;
1030
1031
		if (empty($contactid)) return 0;
1032
1033
		require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1034
		$contact = new Contact($this->db);
1035
		$result=$contact->fetch($contactid);
1036
		$this->contact = $contact;
1037
		return $result;
1038
	}
1039
1040
	/**
1041
	 *    	Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty
1042
	 *
1043
	 *		@param		int		$force_thirdparty_id	Force thirdparty id
1044
	 *		@return		int								<0 if KO, >0 if OK
1045
	 */
1046
	function fetch_thirdparty($force_thirdparty_id=0)
1047
	{
1048
		global $conf;
1049
1050
		if (empty($this->socid) && empty($this->fk_soc) && empty($this->fk_thirdparty) && empty($force_thirdparty_id))
1051
			return 0;
1052
1053
		require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1054
1055
		$idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : $this->fk_thirdparty);
1056
		if ($force_thirdparty_id)
1057
			$idtofetch = $force_thirdparty_id;
1058
1059
		if ($idtofetch) {
1060
			$thirdparty = new Societe($this->db);
1061
			$result = $thirdparty->fetch($idtofetch);
1062
			$this->thirdparty = $thirdparty;
1063
1064
			// Use first price level if level not defined for third party
1065
			if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1066
				$this->thirdparty->price_level = 1;
1067
			}
1068
1069
			return $result;
1070
		} else
1071
			return -1;
1072
	}
1073
1074
1075
	/**
1076
	 * Looks for an object with ref matching the wildcard provided
1077
	 * It does only work when $this->table_ref_field is set
1078
	 *
1079
	 * @param string $ref Wildcard
1080
	 * @return int >1 = OK, 0 = Not found or table_ref_field not defined, <0 = KO
1081
	 */
1082
	public function fetchOneLike($ref)
1083
	{
1084
		if (!$this->table_ref_field) {
1085
			return 0;
1086
		}
1087
1088
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE '.$this->table_ref_field.' LIKE "'.$this->db->escape($ref).'" LIMIT 1';
1089
1090
		$query = $this->db->query($sql);
1091
1092
		if (!$this->db->num_rows($query)) {
1093
			return 0;
1094
		}
1095
1096
		$result = $this->db->fetch_object($query);
1097
1098
		return $this->fetch($result->rowid);
1099
	}
1100
1101
	/**
1102
	 *	Load data for barcode into properties ->barcode_type*
1103
	 *	Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
1104
	 *  if it is not defined, ->element must be defined to know default barcode type.
1105
	 *
1106
	 *	@return		int			<0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
1107
	 */
1108
	function fetch_barcode()
1109
	{
1110
		global $conf;
1111
1112
		dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1113
1114
		$idtype=$this->barcode_type;
1115
		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
1116
		{
1117
			if ($this->element == 'product')      $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1118
			else if ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1119
			else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1120
		}
1121
1122
		if ($idtype > 0)
1123
		{
1124
			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
1125
			{
1126
				$sql = "SELECT rowid, code, libelle as label, coder";
1127
				$sql.= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1128
				$sql.= " WHERE rowid = ".$idtype;
1129
				dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1130
				$resql = $this->db->query($sql);
1131
				if ($resql)
1132
				{
1133
					$obj = $this->db->fetch_object($resql);
1134
					$this->barcode_type       = $obj->rowid;
1135
					$this->barcode_type_code  = $obj->code;
1136
					$this->barcode_type_label = $obj->label;
1137
					$this->barcode_type_coder = $obj->coder;
1138
					return 1;
1139
				}
1140
				else
1141
				{
1142
					dol_print_error($this->db);
1143
					return -1;
1144
				}
1145
			}
1146
		}
1147
		return 0;
1148
	}
1149
1150
	/**
1151
	 *		Charge le projet d'id $this->fk_project dans this->projet
1152
	 *
1153
	 *		@return		int			<0 if KO, >=0 if OK
1154
	 */
1155
	function fetch_projet()
1156
	{
1157
		include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1158
1159
		if (empty($this->fk_project) && ! empty($this->fk_projet)) $this->fk_project = $this->fk_projet;	// For backward compatibility
1160
		if (empty($this->fk_project)) return 0;
1161
1162
		$project = new Project($this->db);
1163
		$result = $project->fetch($this->fk_project);
1164
1165
		$this->projet = $project;	// deprecated
1166
		$this->project = $project;
1167
		return $result;
1168
	}
1169
1170
	/**
1171
	 *		Charge le user d'id userid dans this->user
1172
	 *
1173
	 *		@param	int		$userid 		Id du contact
1174
	 *		@return	int						<0 if KO, >0 if OK
1175
	 */
1176
	function fetch_user($userid)
1177
	{
1178
		$user = new User($this->db);
1179
		$result=$user->fetch($userid);
1180
		$this->user = $user;
1181
		return $result;
1182
	}
1183
1184
	/**
1185
	 *	Read linked origin object
1186
	 *
1187
	 *	@return		void
1188
	 */
1189
	function fetch_origin()
1190
	{
1191
		if ($this->origin == 'shipping') $this->origin = 'expedition';
0 ignored issues
show
Documentation Bug introduced by
It seems like 'expedition' of type string is incompatible with the declared type object<CommonObject> of property $origin.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1192
		if ($this->origin == 'delivery') $this->origin = 'livraison';
0 ignored issues
show
Documentation Bug introduced by
It seems like 'livraison' of type string is incompatible with the declared type object<CommonObject> of property $origin.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1193
1194
		$origin = $this->origin;
1195
1196
		$classname = ucfirst($origin);
1197
		$this->$origin = new $classname($this->db);
1198
		$this->$origin->fetch($this->origin_id);
1199
	}
1200
1201
	/**
1202
	 *    	Load object from specific field
1203
	 *
1204
	 *    	@param	string	$table		Table element or element line
1205
	 *    	@param	string	$field		Field selected
1206
	 *    	@param	string	$key		Import key
1207
	 *		@return	int					<0 if KO, >0 if OK
1208
	 */
1209
	function fetchObjectFrom($table,$field,$key)
1210
	{
1211
		global $conf;
1212
1213
		$result=false;
1214
1215
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$table;
1216
		$sql.= " WHERE ".$field." = '".$key."'";
1217
		$sql.= " AND entity = ".$conf->entity;
1218
1219
		dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1220
		$resql = $this->db->query($sql);
1221
		if ($resql)
1222
		{
1223
			$row = $this->db->fetch_row($resql);
1224
			$result = $this->fetch($row[0]);
1225
		}
1226
1227
		return $result;
1228
	}
1229
1230
	/**
1231
	 *	Getter generic. Load value from a specific field
1232
	 *
1233
	 *	@param	string	$table		Table of element or element line
1234
	 *	@param	int		$id			Element id
1235
	 *	@param	string	$field		Field selected
1236
	 *	@return	int					<0 if KO, >0 if OK
1237
	 */
1238
	function getValueFrom($table, $id, $field)
1239
	{
1240
		$result=false;
1241
		if (!empty($id) && !empty($field) && !empty($table)) {
1242
			$sql = "SELECT ".$field." FROM ".MAIN_DB_PREFIX.$table;
1243
			$sql.= " WHERE rowid = ".$id;
1244
1245
			dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1246
			$resql = $this->db->query($sql);
1247
			if ($resql)
1248
			{
1249
				$row = $this->db->fetch_row($resql);
1250
				$result = $row[0];
1251
			}
1252
		}
1253
		return $result;
1254
	}
1255
1256
	/**
1257
	 *	Setter generic. Update a specific field into database.
1258
	 *  Warning: Trigger is run only if param trigkey is provided.
1259
	 *
1260
	 *	@param	string		$field		Field to update
1261
	 *	@param	mixed		$value		New value
1262
	 *	@param	string		$table		To force other table element or element line (should not be used)
1263
	 *	@param	int			$id			To force other object id (should not be used)
1264
	 *	@param	string		$format		Data format ('text', 'date'). 'text' is used if not defined
1265
	 *	@param	string		$id_field	To force rowid field name. 'rowid' is used if not defined
1266
	 *	@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'
1267
	 *  @param  string      $trigkey    Trigger key to run (in most cases something like 'XXX_MODIFY')
1268
	 *	@return	int						<0 if KO, >0 if OK
1269
	 */
1270
	function setValueFrom($field, $value, $table='', $id=null, $format='', $id_field='', $fuser=null, $trigkey='')
1271
	{
1272
		global $user,$langs,$conf;
1273
1274
		if (empty($table)) 	  $table=$this->table_element;
1275
		if (empty($id))    	  $id=$this->id;
1276
		if (empty($format))   $format='text';
1277
		if (empty($id_field)) $id_field='rowid';
1278
1279
		$error=0;
1280
1281
		$this->db->begin();
1282
1283
		// Special case
1284
		if ($table == 'product' && $field == 'note_private') $field='note';
1285
1286
		$sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET ";
1287
		if ($format == 'text') $sql.= $field." = '".$this->db->escape($value)."'";
1288
		else if ($format == 'int') $sql.= $field." = ".$this->db->escape($value);
1289
		else if ($format == 'date') $sql.= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
1290
		if (! empty($fuser) && is_object($fuser)) $sql.=", fk_user_modif = ".$fuser->id;
1291
		elseif (empty($fuser) || $fuser != 'none') $sql.=", fk_user_modif = ".$user->id;
1292
		$sql.= " WHERE ".$id_field." = ".$id;
1293
1294
		dol_syslog(get_class($this)."::".__FUNCTION__."", LOG_DEBUG);
1295
		$resql = $this->db->query($sql);
1296
		if ($resql)
1297
		{
1298
			if ($trigkey)
1299
			{
1300
				$result=$this->call_trigger($trigkey, (! empty($fuser) && is_object($fuser)) ? $fuser : $user);   // This may set this->errors
1301
				if ($result < 0) $error++;
1302
			}
1303
1304
			if (! $error)
1305
			{
1306
				if (property_exists($this, $field)) $this->$field = $value;
1307
				$this->db->commit();
1308
				return 1;
1309
			}
1310
			else
1311
			{
1312
				$this->db->rollback();
1313
				return -2;
1314
			}
1315
		}
1316
		else
1317
		{
1318
			$this->error=$this->db->lasterror();
1319
			$this->db->rollback();
1320
			return -1;
1321
		}
1322
	}
1323
1324
	/**
1325
	 *      Load properties id_previous and id_next
1326
	 *
1327
	 *      @param	string	$filter		Optional filter. Example: " AND (t.field1 = 'aa' OR t.field2 = 'bb')"
1328
	 *	 	@param  string	$fieldid   	Name of field to use for the select MAX and MIN
1329
	 *		@param	int		$nodbprefix	Do not include DB prefix to forge table name
1330
	 *      @return int         		<0 if KO, >0 if OK
1331
	 */
1332
	function load_previous_next_ref($filter, $fieldid, $nodbprefix=0)
1333
	{
1334
		global $user;
1335
1336
		if (! $this->table_element)
1337
		{
1338
			dol_print_error('',get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
1339
			return -1;
1340
		}
1341
		if ($fieldid == 'none') return 1;
1342
1343
		// this->ismultientitymanaged contains
1344
		// 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
1345
		$alias = 's';
1346
		if ($this->element == 'societe') $alias = 'te';
1347
1348
		$sql = "SELECT MAX(te.".$fieldid.")";
1349
		$sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1350
		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
1351
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2 && !$user->rights->societe->client->voir) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1352
		$sql.= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1353
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2 && !$user->rights->societe->client->voir) $sql.= " AND sc.fk_user = " .$user->id;
1354
		if (! empty($filter))
1355
		{
1356
			if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1357
			$sql.=$filter;
1358
		}
1359
		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
1360
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql.= ' AND te.entity IN ('.getEntity($this->element, 1).')';
1361
1362
		//print $filter.' '.$sql."<br>";
1363
		$result = $this->db->query($sql);
1364
		if (! $result)
1365
		{
1366
			$this->error=$this->db->lasterror();
1367
			return -1;
1368
		}
1369
		$row = $this->db->fetch_row($result);
1370
		$this->ref_previous = $row[0];
1371
1372
1373
		$sql = "SELECT MIN(te.".$fieldid.")";
1374
		$sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1375
		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
1376
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2 && !$user->rights->societe->client->voir) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1377
		$sql.= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1378
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2 && !$user->rights->societe->client->voir) $sql.= " AND sc.fk_user = " .$user->id;
1379
		if (! empty($filter))
1380
		{
1381
			if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1382
			$sql.=$filter;
1383
		}
1384
		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
1385
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql.= ' AND te.entity IN ('.getEntity($this->element, 1).')';
1386
		// 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
1387
1388
		//print $sql."<br>";
1389
		$result = $this->db->query($sql);
1390
		if (! $result)
1391
		{
1392
			$this->error=$this->db->lasterror();
1393
			return -2;
1394
		}
1395
		$row = $this->db->fetch_row($result);
1396
		$this->ref_next = $row[0];
1397
1398
		return 1;
1399
	}
1400
1401
1402
	/**
1403
	 *      Return list of id of contacts of project
1404
	 *
1405
	 *      @param	string	$source     Source of contact: external (llx_socpeople) or internal (llx_user) or thirdparty (llx_societe)
1406
	 *      @return array				Array of id of contacts (if source=external or internal)
1407
	 * 									Array of id of third parties with at least one contact on project (if source=thirdparty)
1408
	 */
1409
	function getListContactId($source='external')
1410
	{
1411
		$contactAlreadySelected = array();
1412
		$tab = $this->liste_contact(-1,$source);
1413
		$num=count($tab);
1414
		$i = 0;
1415
		while ($i < $num)
1416
		{
1417
			if ($source == 'thirdparty') $contactAlreadySelected[$i] = $tab[$i]['socid'];
1418
			else  $contactAlreadySelected[$i] = $tab[$i]['id'];
1419
			$i++;
1420
		}
1421
		return $contactAlreadySelected;
1422
	}
1423
1424
1425
	/**
1426
	 *	Link element with a project
1427
	 *
1428
	 *	@param     	int		$projectid		Project id to link element to
1429
	 *	@return		int						<0 if KO, >0 if OK
1430
	 */
1431
	function setProject($projectid)
1432
	{
1433
		if (! $this->table_element)
1434
		{
1435
			dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined",LOG_ERR);
1436
			return -1;
1437
		}
1438
1439
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1440
		if ($this->table_element == 'actioncomm')
1441
		{
1442
			if ($projectid) $sql.= ' SET fk_project = '.$projectid;
1443
			else $sql.= ' SET fk_project = NULL';
1444
			$sql.= ' WHERE id = '.$this->id;
1445
		}
1446
		else
1447
		{
1448
			if ($projectid) $sql.= ' SET fk_projet = '.$projectid;
1449
			else $sql.= ' SET fk_projet = NULL';
1450
			$sql.= ' WHERE rowid = '.$this->id;
1451
		}
1452
1453
		dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
1454
		if ($this->db->query($sql))
1455
		{
1456
			$this->fk_project = $projectid;
1457
			return 1;
1458
		}
1459
		else
1460
		{
1461
			dol_print_error($this->db);
1462
			return -1;
1463
		}
1464
	}
1465
1466
	/**
1467
	 *  Change the payments methods
1468
	 *
1469
	 *  @param		int		$id		Id of new payment method
1470
	 *  @return		int				>0 if OK, <0 if KO
1471
	 */
1472
	function setPaymentMethods($id)
1473
	{
1474
		dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
1475
		if ($this->statut >= 0 || $this->element == 'societe')
1476
		{
1477
			// TODO uniformize field name
1478
			$fieldname = 'fk_mode_reglement';
1479
			if ($this->element == 'societe') $fieldname = 'mode_reglement';
1480
			if (get_class($this) == 'Fournisseur') $fieldname = 'mode_reglement_supplier';
1481
1482
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1483
			$sql .= ' SET '.$fieldname.' = '.$id;
1484
			$sql .= ' WHERE rowid='.$this->id;
1485
1486
			if ($this->db->query($sql))
1487
			{
1488
				$this->mode_reglement_id = $id;
1489
				// for supplier
1490
				if (get_class($this) == 'Fournisseur') $this->mode_reglement_supplier_id = $id;
1491
				return 1;
1492
			}
1493
			else
1494
			{
1495
				dol_syslog(get_class($this).'::setPaymentMethods Erreur '.$sql.' - '.$this->db->error());
1496
				$this->error=$this->db->error();
1497
				return -1;
1498
			}
1499
		}
1500
		else
1501
		{
1502
			dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
1503
			$this->error='Status of the object is incompatible '.$this->statut;
1504
			return -2;
1505
		}
1506
	}
1507
1508
	/**
1509
	 *  Change the multicurrency code
1510
	 *
1511
	 *  @param		string	$code	multicurrency code
1512
	 *  @return		int				>0 if OK, <0 if KO
1513
	 */
1514
	function setMulticurrencyCode($code)
1515
	{
1516
		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...
1517
		if ($this->statut >= 0 || $this->element == 'societe')
1518
		{
1519
			$fieldname = 'multicurrency_code';
1520
1521
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1522
			$sql .= ' SET '.$fieldname." = '".$this->db->escape($code)."'";
1523
			$sql .= ' WHERE rowid='.$this->id;
1524
1525
			if ($this->db->query($sql))
1526
			{
1527
				$this->multicurrency_code = $code;
1528
1529
				list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
1530
				if ($rate) $this->setMulticurrencyRate($rate);
1531
1532
				return 1;
1533
			}
1534
			else
1535
			{
1536
				dol_syslog(get_class($this).'::setMulticurrencyCode Erreur '.$sql.' - '.$this->db->error());
1537
				$this->error=$this->db->error();
1538
				return -1;
1539
			}
1540
		}
1541
		else
1542
		{
1543
			dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
1544
			$this->error='Status of the object is incompatible '.$this->statut;
1545
			return -2;
1546
		}
1547
	}
1548
1549
	/**
1550
	 *  Change the multicurrency rate
1551
	 *
1552
	 *  @param		double	$rate	multicurrency rate
1553
	 *  @param		int		$mode	mode 1 : amounts in company currency will be recalculated, mode 2 : amounts in foreign currency
1554
	 *  @return		int				>0 if OK, <0 if KO
1555
	 */
1556
	function setMulticurrencyRate($rate, $mode=1)
1557
	{
1558
		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...
1559
		if ($this->statut >= 0 || $this->element == 'societe')
1560
		{
1561
			$fieldname = 'multicurrency_tx';
1562
1563
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1564
			$sql .= ' SET '.$fieldname.' = '.$rate;
1565
			$sql .= ' WHERE rowid='.$this->id;
1566
1567
			if ($this->db->query($sql))
1568
			{
1569
				$this->multicurrency_tx = $rate;
1570
1571
				// Update line price
1572
				if (!empty($this->lines))
1573
				{
1574
					foreach ($this->lines as &$line)
1575
					{
1576
						if($mode == 1) {
1577
							$line->subprice = 0;
1578
						}
1579
1580
						switch ($this->element) {
1581
							case 'propal':
1582
								$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);
1583
								break;
1584
							case 'commande':
1585
								$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);
1586
								break;
1587
							case 'facture':
1588
								$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);
1589
								break;
1590
							case 'supplier_proposal':
1591
								$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);
1592
								break;
1593
							case 'order_supplier':
1594
								$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);
1595
								break;
1596
							case 'invoice_supplier':
1597
								$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);
1598
								break;
1599
							default:
1600
								dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
1601
								break;
1602
						}
1603
1604
					}
1605
				}
1606
1607
				return 1;
1608
			}
1609
			else
1610
			{
1611
				dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
1612
				$this->error=$this->db->error();
1613
				return -1;
1614
			}
1615
		}
1616
		else
1617
		{
1618
			dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
1619
			$this->error='Status of the object is incompatible '.$this->statut;
1620
			return -2;
1621
		}
1622
	}
1623
1624
	/**
1625
	 *  Change the payments terms
1626
	 *
1627
	 *  @param		int		$id		Id of new payment terms
1628
	 *  @return		int				>0 if OK, <0 if KO
1629
	 */
1630
	function setPaymentTerms($id)
1631
	{
1632
		dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
1633
		if ($this->statut >= 0 || $this->element == 'societe')
1634
		{
1635
			// TODO uniformize field name
1636
			$fieldname = 'fk_cond_reglement';
1637
			if ($this->element == 'societe') $fieldname = 'cond_reglement';
1638
			if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
1639
1640
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1641
			$sql .= ' SET '.$fieldname.' = '.$id;
1642
			$sql .= ' WHERE rowid='.$this->id;
1643
1644
			if ($this->db->query($sql))
1645
			{
1646
				$this->cond_reglement_id = $id;
1647
				// for supplier
1648
				if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
1649
				$this->cond_reglement = $id;	// for compatibility
1650
				return 1;
1651
			}
1652
			else
1653
			{
1654
				dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
1655
				$this->error=$this->db->error();
1656
				return -1;
1657
			}
1658
		}
1659
		else
1660
		{
1661
			dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
1662
			$this->error='Status of the object is incompatible '.$this->statut;
1663
			return -2;
1664
		}
1665
	}
1666
1667
	/**
1668
	 *	Define delivery address
1669
	 *  @deprecated
1670
	 *
1671
	 *	@param      int		$id		Address id
1672
	 *	@return     int				<0 si ko, >0 si ok
1673
	 */
1674
	function setDeliveryAddress($id)
1675
	{
1676
		$fieldname = 'fk_delivery_address';
1677
		if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
1678
1679
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
1680
		$sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
1681
1682
		if ($this->db->query($sql))
1683
		{
1684
			$this->fk_delivery_address = $id;
1685
			return 1;
1686
		}
1687
		else
1688
		{
1689
			$this->error=$this->db->error();
1690
			dol_syslog(get_class($this).'::setDeliveryAddress Erreur '.$sql.' - '.$this->error);
1691
			return -1;
1692
		}
1693
	}
1694
1695
1696
	/**
1697
	 *  Change the shipping method
1698
	 *
1699
	 *  @param      int     $shipping_method_id     Id of shipping method
1700
	 *  @return     int              1 if OK, 0 if KO
1701
	 */
1702
	function setShippingMethod($shipping_method_id)
1703
	{
1704
		if (! $this->table_element) {
1705
			dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined",LOG_ERR);
1706
			return -1;
1707
		}
1708
		if ($shipping_method_id<0) $shipping_method_id='NULL';
1709
		dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
1710
1711
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1712
		$sql.= " SET fk_shipping_method = ".$shipping_method_id;
1713
		$sql.= " WHERE rowid=".$this->id;
1714
1715
		if ($this->db->query($sql)) {
1716
			$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...
1717
			return 1;
1718
		} else {
1719
			dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
1720
			$this->error=$this->db->error();
1721
			return 0;
1722
		}
1723
	}
1724
1725
1726
	/**
1727
	 *  Change the warehouse
1728
	 *
1729
	 *  @param      int     $warehouse_id     Id of warehouse
1730
	 *  @return     int              1 if OK, 0 if KO
1731
	 */
1732
	function setWarehouse($warehouse_id)
1733
	{
1734
		if (! $this->table_element) {
1735
			dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined",LOG_ERR);
1736
			return -1;
1737
		}
1738
		if ($warehouse_id<0) $warehouse_id='NULL';
1739
		dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
1740
1741
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1742
		$sql.= " SET fk_warehouse = ".$warehouse_id;
1743
		$sql.= " WHERE rowid=".$this->id;
1744
1745
		if ($this->db->query($sql)) {
1746
			$this->warehouse_id = ($warehouse_id=='NULL')?null:$warehouse_id;
1747
			return 1;
1748
		} else {
1749
			dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
1750
			$this->error=$this->db->error();
1751
			return 0;
1752
		}
1753
	}
1754
1755
1756
	/**
1757
	 *		Set last model used by doc generator
1758
	 *
1759
	 *		@param		User	$user		User object that make change
1760
	 *		@param		string	$modelpdf	Modele name
1761
	 *		@return		int					<0 if KO, >0 if OK
1762
	 */
1763
	function setDocModel($user, $modelpdf)
1764
	{
1765
		if (! $this->table_element)
1766
		{
1767
			dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined",LOG_ERR);
1768
			return -1;
1769
		}
1770
1771
		$newmodelpdf=dol_trunc($modelpdf,255);
1772
1773
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1774
		$sql.= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
1775
		$sql.= " WHERE rowid = ".$this->id;
1776
		// if ($this->element == 'facture') $sql.= " AND fk_statut < 2";
1777
		// if ($this->element == 'propal')  $sql.= " AND fk_statut = 0";
1778
1779
		dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
1780
		$resql=$this->db->query($sql);
1781
		if ($resql)
1782
		{
1783
			$this->modelpdf=$modelpdf;
1784
			return 1;
1785
		}
1786
		else
1787
		{
1788
			dol_print_error($this->db);
1789
			return 0;
1790
		}
1791
	}
1792
1793
1794
	/**
1795
	 *  Change the bank account
1796
	 *
1797
	 *  @param		int		$fk_account		Id of bank account
1798
	 *  @return		int				1 if OK, 0 if KO
1799
	 */
1800
	function setBankAccount($fk_account)
1801
	{
1802
		if (! $this->table_element) {
1803
			dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined",LOG_ERR);
1804
			return -1;
1805
		}
1806
		if ($fk_account<0) $fk_account='NULL';
1807
		dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
1808
1809
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1810
		$sql.= " SET fk_account = ".$fk_account;
1811
		$sql.= " WHERE rowid=".$this->id;
1812
1813
		if ($this->db->query($sql)) {
1814
			$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...
1815
			return 1;
1816
		} else {
1817
			dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
1818
			$this->error=$this->db->error();
1819
			return 0;
1820
		}
1821
	}
1822
1823
	// TODO: Move line related operations to CommonObjectLine?
1824
1825
	/**
1826
	 *  Save a new position (field rang) for details lines.
1827
	 *  You can choose to set position for lines with already a position or lines without any position defined.
1828
	 *
1829
	 * 	@param		boolean		$renum			   True to renum all already ordered lines, false to renum only not already ordered lines.
1830
	 * 	@param		string		$rowidorder		   ASC or DESC
1831
	 * 	@param		boolean		$fk_parent_line    Table with fk_parent_line field or not
1832
	 * 	@return		int                            <0 if KO, >0 if OK
1833
	 */
1834
	function line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
1835
	{
1836
		if (! $this->table_element_line)
1837
		{
1838
			dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined",LOG_ERR);
1839
			return -1;
1840
		}
1841
		if (! $this->fk_element)
1842
		{
1843
			dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined",LOG_ERR);
1844
			return -1;
1845
		}
1846
1847
		// Count number of lines to reorder (according to choice $renum)
1848
		$nl=0;
1849
		$sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
1850
		$sql.= ' WHERE '.$this->fk_element.'='.$this->id;
1851
		if (! $renum) $sql.= ' AND rang = 0';
1852
		if ($renum) $sql.= ' AND rang <> 0';
1853
1854
		dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
1855
		$resql = $this->db->query($sql);
1856
		if ($resql)
1857
		{
1858
			$row = $this->db->fetch_row($resql);
1859
			$nl = $row[0];
1860
		}
1861
		else dol_print_error($this->db);
1862
		if ($nl > 0)
1863
		{
1864
			// The goal of this part is to reorder all lines, with all children lines sharing the same
1865
			// counter that parents.
1866
			$rows=array();
1867
1868
			// We first search all lines that are parent lines (for multilevel details lines)
1869
			$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
1870
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
1871
			if ($fk_parent_line) $sql.= ' AND fk_parent_line IS NULL';
1872
			$sql.= ' ORDER BY rang ASC, rowid '.$rowidorder;
1873
1874
			dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
1875
			$resql = $this->db->query($sql);
1876
			if ($resql)
1877
			{
1878
				$i=0;
1879
				$num = $this->db->num_rows($resql);
1880
				while ($i < $num)
1881
				{
1882
					$row = $this->db->fetch_row($resql);
1883
					$rows[] = $row[0];	// Add parent line into array rows
1884
					$childrens = $this->getChildrenOfLine($row[0]);
1885
					if (! empty($childrens))
1886
					{
1887
						foreach($childrens as $child)
1888
						{
1889
							array_push($rows, $child);
1890
						}
1891
					}
1892
					$i++;
1893
				}
1894
1895
				// Now we set a new number for each lines (parent and children with children included into parent tree)
1896
				if (! empty($rows))
1897
				{
1898
					foreach($rows as $key => $row)
1899
					{
1900
						$this->updateRangOfLine($row, ($key+1));
1901
					}
1902
				}
1903
			}
1904
			else
1905
			{
1906
				dol_print_error($this->db);
1907
			}
1908
		}
1909
		return 1;
1910
	}
1911
1912
	/**
1913
	 * 	Get children of line
1914
	 *
1915
	 * 	@param	int		$id		Id of parent line
1916
	 * 	@return	array			Array with list of children lines id
1917
	 */
1918
	function getChildrenOfLine($id)
1919
	{
1920
		$rows=array();
1921
1922
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
1923
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
1924
		$sql.= ' AND fk_parent_line = '.$id;
1925
		$sql.= ' ORDER BY rang ASC';
1926
1927
		dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG);
1928
		$resql = $this->db->query($sql);
1929
		if ($resql)
1930
		{
1931
			$i=0;
1932
			$num = $this->db->num_rows($resql);
1933
			while ($i < $num)
1934
			{
1935
				$row = $this->db->fetch_row($resql);
1936
				$rows[$i] = $row[0];
1937
				$i++;
1938
			}
1939
		}
1940
1941
		return $rows;
1942
	}
1943
1944
	/**
1945
	 * 	Update a line to have a lower rank
1946
	 *
1947
	 * 	@param 	int			$rowid				Id of line
1948
	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
1949
	 * 	@return	void
1950
	 */
1951
	function line_up($rowid, $fk_parent_line=true)
1952
	{
1953
		$this->line_order(false, 'ASC', $fk_parent_line);
1954
1955
		// Get rang of line
1956
		$rang = $this->getRangOfLine($rowid);
1957
1958
		// Update position of line
1959
		$this->updateLineUp($rowid, $rang);
1960
	}
1961
1962
	/**
1963
	 * 	Update a line to have a higher rank
1964
	 *
1965
	 * 	@param	int			$rowid				Id of line
1966
	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
1967
	 * 	@return	void
1968
	 */
1969
	function line_down($rowid, $fk_parent_line=true)
1970
	{
1971
		$this->line_order(false, 'ASC', $fk_parent_line);
1972
1973
		// Get rang of line
1974
		$rang = $this->getRangOfLine($rowid);
1975
1976
		// Get max value for rang
1977
		$max = $this->line_max();
1978
1979
		// Update position of line
1980
		$this->updateLineDown($rowid, $rang, $max);
1981
	}
1982
1983
	/**
1984
	 * 	Update position of line (rang)
1985
	 *
1986
	 * 	@param	int		$rowid		Id of line
1987
	 * 	@param	int		$rang		Position
1988
	 * 	@return	void
1989
	 */
1990
	function updateRangOfLine($rowid,$rang)
1991
	{
1992
		$fieldposition = 'rang';
1993
		if ($this->table_element_line == 'ecm_files') $fieldposition = 'position';
1994
1995
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
1996
		$sql.= ' WHERE rowid = '.$rowid;
1997
1998
		dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
1999
		if (! $this->db->query($sql))
2000
		{
2001
			dol_print_error($this->db);
2002
		}
2003
	}
2004
2005
	/**
2006
	 * 	Update position of line with ajax (rang)
2007
	 *
2008
	 * 	@param	array	$rows	Array of rows
2009
	 * 	@return	void
2010
	 */
2011
	function line_ajaxorder($rows)
2012
	{
2013
		$num = count($rows);
2014
		for ($i = 0 ; $i < $num ; $i++)
2015
		{
2016
			$this->updateRangOfLine($rows[$i], ($i+1));
2017
		}
2018
	}
2019
2020
	/**
2021
	 * 	Update position of line up (rang)
2022
	 *
2023
	 * 	@param	int		$rowid		Id of line
2024
	 * 	@param	int		$rang		Position
2025
	 * 	@return	void
2026
	 */
2027
	function updateLineUp($rowid,$rang)
2028
	{
2029
		if ($rang > 1 )
2030
		{
2031
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.$rang ;
2032
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2033
			$sql.= ' AND rang = '.($rang - 1);
2034
			if ($this->db->query($sql) )
2035
			{
2036
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang  = '.($rang - 1);
2037
				$sql.= ' WHERE rowid = '.$rowid;
2038
				if (! $this->db->query($sql) )
2039
				{
2040
					dol_print_error($this->db);
2041
				}
2042
			}
2043
			else
2044
			{
2045
				dol_print_error($this->db);
2046
			}
2047
		}
2048
	}
2049
2050
	/**
2051
	 * 	Update position of line down (rang)
2052
	 *
2053
	 * 	@param	int		$rowid		Id of line
2054
	 * 	@param	int		$rang		Position
2055
	 * 	@param	int		$max		Max
2056
	 * 	@return	void
2057
	 */
2058
	function updateLineDown($rowid,$rang,$max)
2059
	{
2060
		if ($rang < $max)
2061
		{
2062
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.$rang;
2063
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2064
			$sql.= ' AND rang = '.($rang+1);
2065
			if ($this->db->query($sql) )
2066
			{
2067
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.($rang+1);
2068
				$sql.= ' WHERE rowid = '.$rowid;
2069
				if (! $this->db->query($sql) )
2070
				{
2071
					dol_print_error($this->db);
2072
				}
2073
			}
2074
			else
2075
			{
2076
				dol_print_error($this->db);
2077
			}
2078
		}
2079
	}
2080
2081
	/**
2082
	 * 	Get position of line (rang)
2083
	 *
2084
	 * 	@param		int		$rowid		Id of line
2085
	 *  @return		int     			Value of rang in table of lines
2086
	 */
2087
	function getRangOfLine($rowid)
2088
	{
2089
		$sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2090
		$sql.= ' WHERE rowid ='.$rowid;
2091
2092
		dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
2093
		$resql = $this->db->query($sql);
2094
		if ($resql)
2095
		{
2096
			$row = $this->db->fetch_row($resql);
2097
			return $row[0];
2098
		}
2099
	}
2100
2101
	/**
2102
	 * 	Get rowid of the line relative to its position
2103
	 *
2104
	 * 	@param		int		$rang		Rang value
2105
	 *  @return     int     			Rowid of the line
2106
	 */
2107
	function getIdOfLine($rang)
2108
	{
2109
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2110
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2111
		$sql.= ' AND rang = '.$rang;
2112
		$resql = $this->db->query($sql);
2113
		if ($resql)
2114
		{
2115
			$row = $this->db->fetch_row($resql);
2116
			return $row[0];
2117
		}
2118
	}
2119
2120
	/**
2121
	 * 	Get max value used for position of line (rang)
2122
	 *
2123
	 * 	@param		int		$fk_parent_line		Parent line id
2124
	 *  @return     int  			   			Max value of rang in table of lines
2125
	 */
2126
	function line_max($fk_parent_line=0)
2127
	{
2128
		// Search the last rang with fk_parent_line
2129
		if ($fk_parent_line)
2130
		{
2131
			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2132
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2133
			$sql.= ' AND fk_parent_line = '.$fk_parent_line;
2134
2135
			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2136
			$resql = $this->db->query($sql);
2137
			if ($resql)
2138
			{
2139
				$row = $this->db->fetch_row($resql);
2140
				if (! empty($row[0]))
2141
				{
2142
					return $row[0];
2143
				}
2144
				else
2145
				{
2146
					return $this->getRangOfLine($fk_parent_line);
2147
				}
2148
			}
2149
		}
2150
		// If not, search the last rang of element
2151
		else
2152
		{
2153
			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2154
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2155
2156
			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2157
			$resql = $this->db->query($sql);
2158
			if ($resql)
2159
			{
2160
				$row = $this->db->fetch_row($resql);
2161
				return $row[0];
2162
			}
2163
		}
2164
	}
2165
2166
	/**
2167
	 *  Update external ref of element
2168
	 *
2169
	 *  @param      string		$ref_ext	Update field ref_ext
2170
	 *  @return     int      		   		<0 if KO, >0 if OK
2171
	 */
2172
	function update_ref_ext($ref_ext)
2173
	{
2174
		if (! $this->table_element)
2175
		{
2176
			dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
2177
			return -1;
2178
		}
2179
2180
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2181
		$sql.= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
2182
		$sql.= " WHERE ".(isset($this->table_rowid)?$this->table_rowid:'rowid')." = ". $this->id;
2183
2184
		dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
2185
		if ($this->db->query($sql))
2186
		{
2187
			$this->ref_ext = $ref_ext;
2188
			return 1;
2189
		}
2190
		else
2191
		{
2192
			$this->error=$this->db->error();
2193
			return -1;
2194
		}
2195
	}
2196
2197
	/**
2198
	 *  Update note of element
2199
	 *
2200
	 *  @param      string		$note		New value for note
2201
	 *  @param		string		$suffix		'', '_public' or '_private'
2202
	 *  @return     int      		   		<0 if KO, >0 if OK
2203
	 */
2204
	function update_note($note,$suffix='')
2205
	{
2206
		global $user;
2207
2208
		if (! $this->table_element)
2209
		{
2210
			dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
2211
			return -1;
2212
		}
2213
		if (! in_array($suffix,array('','_public','_private')))
2214
		{
2215
			dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
2216
			return -2;
2217
		}
2218
		// Special cas
2219
		//var_dump($this->table_element);exit;
2220
		if ($this->table_element == 'product') $suffix='';
2221
2222
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2223
		$sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL");
2224
		$sql.= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))?"fk_user_mod":"fk_user_modif")." = ".$user->id;
2225
		$sql.= " WHERE rowid =". $this->id;
2226
2227
		dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
2228
		if ($this->db->query($sql))
2229
		{
2230
			if ($suffix == '_public') $this->note_public = $note;
2231
			else if ($suffix == '_private') $this->note_private = $note;
2232
			else
2233
			{
2234
				$this->note = $note;      // deprecated
2235
				$this->note_private = $note;
2236
			}
2237
			return 1;
2238
		}
2239
		else
2240
		{
2241
			$this->error=$this->db->lasterror();
2242
			return -1;
2243
		}
2244
	}
2245
2246
	/**
2247
	 * 	Update public note (kept for backward compatibility)
2248
	 *
2249
	 * @param      string		$note		New value for note
2250
	 * @return     int      		   		<0 if KO, >0 if OK
2251
	 * @deprecated
2252
	 * @see update_note()
2253
	 */
2254
	function update_note_public($note)
2255
	{
2256
		return $this->update_note($note,'_public');
2257
	}
2258
2259
	/**
2260
	 *	Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
2261
	 *  Must be called at end of methods addline or updateline.
2262
	 *
2263
	 *	@param	int		$exclspec          	>0 = Exclude special product (product_type=9)
2264
	 *  @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
2265
	 *  @param	int		$nodatabaseupdate	1=Do not update database. Update only properties of object.
2266
	 *  @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.
2267
	 *	@return	int    			           	<0 if KO, >0 if OK
2268
	 */
2269
	function update_price($exclspec=0,$roundingadjust='none',$nodatabaseupdate=0,$seller=null)
2270
	{
2271
		global $conf;
2272
2273
		// Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
2274
		$MODULE = "";
2275
		if ($this->element == 'propal')
2276
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
2277
		elseif ($this->element == 'order')
2278
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
2279
		elseif ($this->element == 'facture')
2280
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
2281
		elseif ($this->element == 'facture_fourn')
2282
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
2283
		elseif ($this->element == 'order_supplier')
2284
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
2285
		elseif ($this->element == 'supplier_proposal')
2286
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
2287
2288
		if (! empty($MODULE)) {
2289
			if (! empty($conf->global->$MODULE)) {
2290
				$modsactivated = explode(',', $conf->global->$MODULE);
2291
				foreach ($modsactivated as $mod) {
2292
					if ($conf->$mod->enabled)
2293
						return 1; // update was disabled by specific setup
2294
				}
2295
			}
2296
		}
2297
2298
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2299
2300
		if ($roundingadjust == '-1') $roundingadjust='auto';	// For backward compatibility
2301
2302
		$forcedroundingmode=$roundingadjust;
2303
		if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode=$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
2304
		elseif ($forcedroundingmode == 'auto') $forcedroundingmode='0';
2305
2306
		$error=0;
2307
2308
		$multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
2309
2310
		// Define constants to find lines to sum
2311
		$fieldtva='total_tva';
2312
		$fieldlocaltax1='total_localtax1';
2313
		$fieldlocaltax2='total_localtax2';
2314
		$fieldup='subprice';
2315
		if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
2316
		{
2317
			$fieldtva='tva';
2318
			$fieldup='pu_ht';
2319
		}
2320
		if ($this->element == 'expensereport')
2321
		{
2322
			$fieldup='value_unit';
2323
		}
2324
2325
		$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,';
2326
		$sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
2327
			if ($this->table_element_line == 'facturedet') $sql.= ', situation_percent';
2328
			$sql.= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
2329
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2330
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2331
		if ($exclspec)
2332
		{
2333
			$product_field='product_type';
2334
			if ($this->table_element_line == 'contratdet') $product_field='';    // contratdet table has no product_type field
2335
			if ($product_field) $sql.= ' AND '.$product_field.' <> 9';
2336
		}
2337
		$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
2338
2339
		dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2340
		$resql = $this->db->query($sql);
2341
		if ($resql)
2342
		{
2343
			$this->total_ht  = 0;
2344
			$this->total_tva = 0;
2345
			$this->total_localtax1 = 0;
2346
			$this->total_localtax2 = 0;
2347
			$this->total_ttc = 0;
2348
			$total_ht_by_vats  = array();
2349
			$total_tva_by_vats = array();
2350
			$total_ttc_by_vats = array();
2351
			$this->multicurrency_total_ht	= 0;
2352
			$this->multicurrency_total_tva	= 0;
2353
			$this->multicurrency_total_ttc	= 0;
2354
2355
			$num = $this->db->num_rows($resql);
2356
			$i = 0;
2357
			while ($i < $num)
2358
			{
2359
				$obj = $this->db->fetch_object($resql);
2360
2361
				// Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
2362
				if ($forcedroundingmode == '0')	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode='auto'
2363
				{
2364
					$localtax_array=array($obj->localtax1_type,$obj->localtax1_tx,$obj->localtax2_type,$obj->localtax2_tx);
2365
					$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 2269 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...
2366
					$diff=price2num($tmpcal[1] - $obj->total_tva, 'MT', 1);
2367
					if ($diff)
2368
					{
2369
						$sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid;
2370
						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);
2371
								$resqlfix=$this->db->query($sqlfix);
2372
								if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
2373
								$obj->total_tva = $tmpcal[1];
2374
								$obj->total_ttc = $tmpcal[2];
2375
						//
2376
					}
2377
				}
2378
2379
				$this->total_ht        += $obj->total_ht;		// The field visible at end of line detail
2380
				$this->total_tva       += $obj->total_tva;
2381
				$this->total_localtax1 += $obj->total_localtax1;
2382
				$this->total_localtax2 += $obj->total_localtax2;
2383
				$this->total_ttc       += $obj->total_ttc;
2384
				$this->multicurrency_total_ht        += $obj->multicurrency_total_ht;		// The field visible at end of line detail
2385
				$this->multicurrency_total_tva       += $obj->multicurrency_total_tva;
2386
				$this->multicurrency_total_ttc       += $obj->multicurrency_total_ttc;
2387
2388
				if (! isset($total_ht_by_vats[$obj->vatrate]))  $total_ht_by_vats[$obj->vatrate]=0;
2389
				if (! isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate]=0;
2390
				if (! isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate]=0;
2391
				$total_ht_by_vats[$obj->vatrate]  += $obj->total_ht;
2392
				$total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
2393
				$total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
2394
2395
				if ($forcedroundingmode == '1')	// Check if we need adjustement onto line for vat. TODO This works on the company currency but not on multicurrency
2396
				{
2397
					$tmpvat=price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
2398
					$diff=price2num($total_tva_by_vats[$obj->vatrate]-$tmpvat, 'MT', 1);
2399
					//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";
2400
					if ($diff)
2401
					{
2402
						if (abs($diff) > 0.1) { dol_syslog('A rounding difference was detected into TOTAL but is too high to be corrected', LOG_WARNING); exit; }
2403
						$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;
2404
						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);
2405
								$resqlfix=$this->db->query($sqlfix);
2406
								if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
2407
								$this->total_tva -= $diff;
2408
								$this->total_ttc -= $diff;
2409
								$total_tva_by_vats[$obj->vatrate] -= $diff;
2410
								$total_ttc_by_vats[$obj->vatrate] -= $diff;
2411
2412
					}
2413
				}
2414
2415
				$i++;
2416
			}
2417
2418
			// Add revenue stamp to total
2419
			$this->total_ttc       			+= isset($this->revenuestamp)?$this->revenuestamp:0;
2420
			$this->multicurrency_total_ttc  += isset($this->revenuestamp)?($this->revenuestamp * $multicurrency_tx):0;
2421
2422
			// Situations totals
2423
			if ($this->situation_cycle_ref && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits'))
2424
			{
2425
				$prev_sits = $this->get_prev_sits();
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class CommonObject as the method get_prev_sits() does only exist in the following sub-classes of CommonObject: Facture. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
2426
2427
				foreach ($prev_sits as $sit) {				// $sit is an object Facture loaded with a fetch.
2428
					$this->total_ht -= $sit->total_ht;
2429
					$this->total_tva -= $sit->total_tva;
2430
					$this->total_localtax1 -= $sit->total_localtax1;
2431
					$this->total_localtax2 -= $sit->total_localtax2;
2432
					$this->total_ttc -= $sit->total_ttc;
2433
					$this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
2434
					$this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
2435
					$this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
2436
				}
2437
			}
2438
2439
			$this->db->free($resql);
2440
2441
			// Now update global field total_ht, total_ttc and tva
2442
			$fieldht='total_ht';
2443
			$fieldtva='tva';
2444
			$fieldlocaltax1='localtax1';
2445
			$fieldlocaltax2='localtax2';
2446
			$fieldttc='total_ttc';
2447
			// Specific code for backward compatibility with old field names
2448
			if ($this->element == 'facture' || $this->element == 'facturerec')             $fieldht='total';
2449
			if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='total_tva';
2450
			if ($this->element == 'propal')                                                $fieldttc='total';
2451
			if ($this->element == 'expensereport')                                         $fieldtva='total_tva';
2452
			if ($this->element == 'supplier_proposal')                                     $fieldttc='total';
2453
2454
			if (empty($nodatabaseupdate))
2455
			{
2456
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2457
				$sql .= " ".$fieldht."='".price2num($this->total_ht)."',";
2458
				$sql .= " ".$fieldtva."='".price2num($this->total_tva)."',";
2459
				$sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',";
2460
				$sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',";
2461
				$sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'";
2462
						$sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'";
2463
						$sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'";
2464
						$sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'";
2465
				$sql .= ' WHERE rowid = '.$this->id;
2466
2467
				//print "xx".$sql;
2468
				dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2469
				$resql=$this->db->query($sql);
2470
				if (! $resql)
2471
				{
2472
					$error++;
2473
					$this->error=$this->db->lasterror();
2474
					$this->errors[]=$this->db->lasterror();
2475
				}
2476
			}
2477
2478
			if (! $error)
2479
			{
2480
				return 1;
2481
			}
2482
			else
2483
			{
2484
				return -1;
2485
			}
2486
		}
2487
		else
2488
		{
2489
			dol_print_error($this->db,'Bad request in update_price');
2490
			return -1;
2491
		}
2492
	}
2493
2494
	/**
2495
	 *	Add objects linked in llx_element_element.
2496
	 *
2497
	 *	@param		string	$origin		Linked element type
2498
	 *	@param		int		$origin_id	Linked element id
2499
	 *	@return		int					<=0 if KO, >0 if OK
2500
	 *	@see		fetchObjectLinked, updateObjectLinked, deleteObjectLinked
2501
	 */
2502
	function add_object_linked($origin=null, $origin_id=null)
2503
	{
2504
		$origin = (! empty($origin) ? $origin : $this->origin);
2505
		$origin_id = (! empty($origin_id) ? $origin_id : $this->origin_id);
2506
2507
		// Special case
2508
		if ($origin == 'order') $origin='commande';
2509
		if ($origin == 'invoice') $origin='facture';
2510
2511
		$this->db->begin();
2512
2513
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
2514
		$sql.= "fk_source";
2515
		$sql.= ", sourcetype";
2516
		$sql.= ", fk_target";
2517
		$sql.= ", targettype";
2518
		$sql.= ") VALUES (";
2519
		$sql.= $origin_id;
2520
		$sql.= ", '".$this->db->escape($origin)."'";
2521
		$sql.= ", ".$this->id;
2522
		$sql.= ", '".$this->db->escape($this->element)."'";
2523
		$sql.= ")";
2524
2525
		dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG);
2526
		if ($this->db->query($sql))
2527
	  	{
2528
	  		$this->db->commit();
2529
	  		return 1;
2530
	  	}
2531
	  	else
2532
	  	{
2533
	  		$this->error=$this->db->lasterror();
2534
	  		$this->db->rollback();
2535
	  		return 0;
2536
	  	}
2537
	}
2538
2539
	/**
2540
	 *	Fetch array of objects linked to current object. Links are loaded into this->linkedObjects array and this->linkedObjectsIds
2541
	 *  Possible usage for parameters:
2542
	 *  - all parameters empty -> we look all link to current object (current object can be source or target)
2543
	 *  - source id+type -> will get target list linked to source
2544
	 *  - target id+type -> will get source list linked to target
2545
	 *  - source id+type + target type -> will get target list of the type
2546
	 *  - target id+type + target source -> will get source list of the type
2547
	 *
2548
	 *	@param	int		$sourceid		Object source id (if not defined, id of object)
2549
	 *	@param  string	$sourcetype		Object source type (if not defined, element name of object)
2550
	 *	@param  int		$targetid		Object target id (if not defined, id of object)
2551
	 *	@param  string	$targettype		Object target type (if not defined, elemennt name of object)
2552
	 *	@param  string	$clause			'OR' or 'AND' clause used when both source id and target id are provided
2553
	 *  @param	int		$alsosametype	0=Return only links to object that differs from source. 1=Include also link to objects of same type.
2554
	 *	@return	void
2555
	 *  @see	add_object_linked, updateObjectLinked, deleteObjectLinked
2556
	 */
2557
	function fetchObjectLinked($sourceid=null,$sourcetype='',$targetid=null,$targettype='',$clause='OR',$alsosametype=1)
2558
	{
2559
		global $conf;
2560
2561
		$this->linkedObjectsIds=array();
2562
		$this->linkedObjects=array();
2563
2564
		$justsource=false;
2565
		$justtarget=false;
2566
		$withtargettype=false;
2567
		$withsourcetype=false;
2568
2569
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid))
2570
		{
2571
			$justsource=true;  // the source (id and type) is a search criteria
2572
			if (! empty($targettype)) $withtargettype=true;
2573
		}
2574
		if (! empty($targetid) && ! empty($targettype) && empty($sourceid))
2575
		{
2576
			$justtarget=true;  // the target (id and type) is a search criteria
2577
			if (! empty($sourcetype)) $withsourcetype=true;
2578
		}
2579
2580
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
2581
		$targetid = (! empty($targetid) ? $targetid : $this->id);
2582
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
2583
		$targettype = (! empty($targettype) ? $targettype : $this->element);
2584
2585
		/*if (empty($sourceid) && empty($targetid))
2586
        {
2587
        	dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
2588
        	return -1;
2589
        }*/
2590
2591
		// Links between objects are stored in table element_element
2592
		$sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype';
2593
		$sql.= ' FROM '.MAIN_DB_PREFIX.'element_element';
2594
		$sql.= " WHERE ";
2595
		if ($justsource || $justtarget)
2596
		{
2597
			if ($justsource)
2598
			{
2599
				$sql.= "fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."'";
2600
				if ($withtargettype) $sql.= " AND targettype = '".$targettype."'";
2601
			}
2602
			else if ($justtarget)
2603
			{
2604
				$sql.= "fk_target = ".$targetid." AND targettype = '".$targettype."'";
2605
				if ($withsourcetype) $sql.= " AND sourcetype = '".$sourcetype."'";
2606
			}
2607
		}
2608
		else
2609
		{
2610
			$sql.= "(fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."')";
2611
			$sql.= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$targettype."')";
2612
		}
2613
		$sql .= ' ORDER BY sourcetype';
2614
		//print $sql;
2615
2616
		dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
2617
		$resql = $this->db->query($sql);
2618
		if ($resql)
2619
		{
2620
			$num = $this->db->num_rows($resql);
2621
			$i = 0;
2622
			while ($i < $num)
2623
			{
2624
				$obj = $this->db->fetch_object($resql);
2625
				if ($justsource || $justtarget)
2626
				{
2627
					if ($justsource)
2628
					{
2629
						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
2630
					}
2631
					else if ($justtarget)
2632
					{
2633
						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
2634
					}
2635
				}
2636
				else
2637
				{
2638
					if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype)
2639
					{
2640
						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
2641
					}
2642
					if ($obj->fk_target == $targetid && $obj->targettype == $targettype)
2643
					{
2644
						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
2645
					}
2646
				}
2647
				$i++;
2648
			}
2649
2650
			if (! empty($this->linkedObjectsIds))
2651
			{
2652
				foreach($this->linkedObjectsIds as $objecttype => $objectids)       // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
2653
				{
2654
					// Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
2655
					$module = $element = $subelement = $objecttype;
2656
					if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
2657
						&& preg_match('/^([^_]+)_([^_]+)/i',$objecttype,$regs))
2658
					{
2659
						$module = $element = $regs[1];
2660
						$subelement = $regs[2];
2661
					}
2662
2663
					$classpath = $element.'/class';
2664
					// To work with non standard classpath or module name
2665
					if ($objecttype == 'facture')			{
2666
						$classpath = 'compta/facture/class';
2667
					}
2668
					else if ($objecttype == 'facturerec')			{
2669
						$classpath = 'compta/facture/class'; $module = 'facture';
2670
					}
2671
					else if ($objecttype == 'propal')			{
2672
						$classpath = 'comm/propal/class';
2673
					}
2674
					else if ($objecttype == 'supplier_proposal')			{
2675
						$classpath = 'supplier_proposal/class';
2676
					}
2677
					else if ($objecttype == 'shipping')			{
2678
						$classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon';
2679
					}
2680
					else if ($objecttype == 'delivery')			{
2681
						$classpath = 'livraison/class'; $subelement = 'livraison'; $module = 'livraison_bon';
2682
					}
2683
					else if ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier')	{
2684
						$classpath = 'fourn/class'; $module = 'fournisseur';
2685
					}
2686
					else if ($objecttype == 'fichinter')			{
2687
						$classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter';
2688
					}
2689
					else if ($objecttype == 'subscription')			{
2690
						$classpath = 'adherents/class'; $module = 'adherent';
2691
					}
2692
2693
					// Set classfile
2694
					$classfile = strtolower($subelement); $classname = ucfirst($subelement);
2695
2696
					if ($objecttype == 'order') {
2697
						$classfile = 'commande'; $classname = 'Commande';
2698
					}
2699
					else if ($objecttype == 'invoice_supplier') {
2700
						$classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur';
2701
					}
2702
					else if ($objecttype == 'order_supplier')   {
2703
						$classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur';
2704
					}
2705
					else if ($objecttype == 'supplier_proposal')   {
2706
						$classfile = 'supplier_proposal'; $classname = 'SupplierProposal';
2707
					}
2708
					else if ($objecttype == 'facturerec')   {
2709
						$classfile = 'facture-rec'; $classname = 'FactureRec';
2710
					}
2711
					else if ($objecttype == 'subscription')   {
2712
						$classfile = 'subscription'; $classname = 'Subscription';
2713
					}
2714
2715
					// Here $module, $classfile and $classname are set
2716
					if ($conf->$module->enabled && (($element != $this->element) || $alsosametype))
2717
					{
2718
						dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
2719
						//print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
2720
						if (class_exists($classname))
2721
						{
2722
							foreach($objectids as $i => $objectid)	// $i is rowid into llx_element_element
2723
							{
2724
								$object = new $classname($this->db);
2725
								$ret = $object->fetch($objectid);
2726
								if ($ret >= 0)
2727
								{
2728
									$this->linkedObjects[$objecttype][$i] = $object;
2729
								}
2730
							}
2731
						}
2732
					}
2733
				}
2734
			}
2735
		}
2736
		else
2737
		{
2738
			dol_print_error($this->db);
2739
		}
2740
	}
2741
2742
	/**
2743
	 *	Update object linked of a current object
2744
	 *
2745
	 *	@param	int		$sourceid		Object source id
2746
	 *	@param  string	$sourcetype		Object source type
2747
	 *	@param  int		$targetid		Object target id
2748
	 *	@param  string	$targettype		Object target type
2749
	 *	@return							int	>0 if OK, <0 if KO
2750
	 *	@see	add_object_linked, fetObjectLinked, deleteObjectLinked
2751
	 */
2752
	function updateObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='')
2753
	{
2754
		$updatesource=false;
2755
		$updatetarget=false;
2756
2757
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource=true;
2758
		else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $updatetarget=true;
2759
2760
		$sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET ";
2761
		if ($updatesource)
2762
		{
2763
			$sql.= "fk_source = ".$sourceid;
2764
			$sql.= ", sourcetype = '".$this->db->escape($sourcetype)."'";
2765
			$sql.= " WHERE fk_target = ".$this->id;
2766
			$sql.= " AND targettype = '".$this->db->escape($this->element)."'";
2767
		}
2768
		else if ($updatetarget)
2769
		{
2770
			$sql.= "fk_target = ".$targetid;
2771
			$sql.= ", targettype = '".$this->db->escape($targettype)."'";
2772
			$sql.= " WHERE fk_source = ".$this->id;
2773
			$sql.= " AND sourcetype = '".$this->db->escape($this->element)."'";
2774
		}
2775
2776
		dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG);
2777
		if ($this->db->query($sql))
2778
		{
2779
			return 1;
2780
		}
2781
		else
2782
		{
2783
			$this->error=$this->db->lasterror();
2784
			return -1;
2785
		}
2786
	}
2787
2788
	/**
2789
	 *	Delete all links between an object $this
2790
	 *
2791
	 *	@param	int		$sourceid		Object source id
2792
	 *	@param  string	$sourcetype		Object source type
2793
	 *	@param  int		$targetid		Object target id
2794
	 *	@param  string	$targettype		Object target type
2795
	 *  @param	int		$rowid			Row id of line to delete. If defined, other parameters are not used.
2796
	 *	@return     					int	>0 if OK, <0 if KO
2797
	 *	@see	add_object_linked, updateObjectLinked, fetchObjectLinked
2798
	 */
2799
	function deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='')
2800
	{
2801
		$deletesource=false;
2802
		$deletetarget=false;
2803
2804
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource=true;
2805
		else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $deletetarget=true;
2806
2807
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
2808
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
2809
		$targetid = (! empty($targetid) ? $targetid : $this->id);
2810
		$targettype = (! empty($targettype) ? $targettype : $this->element);
2811
2812
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element";
2813
		$sql.= " WHERE";
2814
		if ($rowid > 0)
2815
		{
2816
			$sql.=" rowid = ".$rowid;
2817
		}
2818
		else
2819
		{
2820
			if ($deletesource)
2821
			{
2822
				$sql.= " fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'";
2823
				$sql.= " AND fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."'";
2824
			}
2825
			else if ($deletetarget)
2826
			{
2827
				$sql.= " fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'";
2828
				$sql.= " AND fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."'";
2829
			}
2830
			else
2831
			{
2832
				$sql.= " (fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."')";
2833
				$sql.= " OR";
2834
				$sql.= " (fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."')";
2835
			}
2836
		}
2837
2838
		dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG);
2839
		if ($this->db->query($sql))
2840
		{
2841
			return 1;
2842
		}
2843
		else
2844
		{
2845
			$this->error=$this->db->lasterror();
2846
			$this->errors[]=$this->error;
2847
			return -1;
2848
		}
2849
	}
2850
2851
	/**
2852
	 *      Set status of an object
2853
	 *
2854
	 *      @param	int		$status			Status to set
2855
	 *      @param	int		$elementId		Id of element to force (use this->id by default)
2856
	 *      @param	string	$elementType	Type of element to force (use this->table_element by default)
2857
	 *      @return int						<0 if KO, >0 if OK
2858
	 */
2859
	function setStatut($status,$elementId=null,$elementType='')
2860
	{
2861
		global $user,$langs,$conf;
2862
2863
		$savElementId=$elementId;  // To be used later to know if we were using the method using the id of this or not.
2864
2865
		$elementId = (!empty($elementId)?$elementId:$this->id);
2866
		$elementTable = (!empty($elementType)?$elementType:$this->table_element);
2867
2868
		$this->db->begin();
2869
2870
		$fieldstatus="fk_statut";
2871
		if ($elementTable == 'mailing') $fieldstatus="statut";
2872
		if ($elementTable == 'user') $fieldstatus="statut";
2873
		if ($elementTable == 'expensereport') $fieldstatus="fk_statut";
2874
		if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status";
2875
2876
		$sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable;
2877
		$sql.= " SET ".$fieldstatus." = ".$status;
2878
		// If status = 1 = validated, update also fk_user_valid
2879
		if ($status == 1 && $elementTable == 'expensereport') $sql.=", fk_user_valid = ".$user->id;
2880
		$sql.= " WHERE rowid=".$elementId;
2881
2882
		dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
2883
		if ($this->db->query($sql))
2884
		{
2885
			$error = 0;
2886
2887
			$trigkey='';
2888
			if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_SIGN';   // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
2889
			if ($this->element == 'supplier_proposal' && $status == 3) $trigkey='SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
2890
			if ($this->element == 'supplier_proposal' && $status == 4) $trigkey='SUPPLIER_PROPOSAL_CLOSE';  // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
2891
			if ($this->element == 'fichinter' && $status == 3) $trigkey='FICHINTER_CLASSIFY_DONE';
2892
			if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED';
2893
			if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED';
2894
2895
			if ($trigkey)
2896
			{
2897
				// Appel des triggers
2898
				include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
2899
				$interface=new Interfaces($this->db);
2900
				$result=$interface->run_triggers($trigkey,$this,$user,$langs,$conf);
2901
				if ($result < 0) {
2902
					$error++; $this->errors=$interface->errors;
2903
				}
2904
				// Fin appel triggers
2905
			}
2906
2907
			if (! $error)
2908
			{
2909
				$this->db->commit();
2910
2911
				if (empty($savElementId))    // If the element we update was $this (so $elementId is null)
2912
				{
2913
					$this->statut = $status;
2914
					$this->status = $status;
2915
				}
2916
2917
				return 1;
2918
			}
2919
			else
2920
			{
2921
				$this->db->rollback();
2922
				dol_syslog(get_class($this)."::setStatus ".$this->error,LOG_ERR);
2923
				return -1;
2924
			}
2925
		}
2926
		else
2927
		{
2928
			$this->error=$this->db->lasterror();
2929
			$this->db->rollback();
2930
			return -1;
2931
		}
2932
	}
2933
2934
2935
	/**
2936
	 *  Load type of canvas of an object if it exists
2937
	 *
2938
	 *  @param      int		$id     Record id
2939
	 *  @param      string	$ref    Record ref
2940
	 *  @return		int				<0 if KO, 0 if nothing done, >0 if OK
2941
	 */
2942
	function getCanvas($id=0,$ref='')
2943
	{
2944
		global $conf;
2945
2946
		if (empty($id) && empty($ref)) return 0;
2947
		if (! empty($conf->global->MAIN_DISABLE_CANVAS)) return 0;    // To increase speed. Not enabled by default.
2948
2949
		// Clean parameters
2950
		$ref = trim($ref);
2951
2952
		$sql = "SELECT rowid, canvas";
2953
		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
2954
		$sql.= " WHERE entity IN (".getEntity($this->element, 1).")";
2955
		if (! empty($id))  $sql.= " AND rowid = ".$id;
2956
		if (! empty($ref)) $sql.= " AND ref = '".$this->db->escape($ref)."'";
2957
2958
		$resql = $this->db->query($sql);
2959
		if ($resql)
2960
		{
2961
			$obj = $this->db->fetch_object($resql);
2962
			if ($obj)
2963
			{
2964
				$this->canvas   = $obj->canvas;
2965
				return 1;
2966
			}
2967
			else return 0;
2968
		}
2969
		else
2970
		{
2971
			dol_print_error($this->db);
2972
			return -1;
2973
		}
2974
	}
2975
2976
2977
	/**
2978
	 * 	Get special code of a line
2979
	 *
2980
	 * 	@param	int		$lineid		Id of line
2981
	 * 	@return	int					Special code
2982
	 */
2983
	function getSpecialCode($lineid)
2984
	{
2985
		$sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2986
		$sql.= ' WHERE rowid = '.$lineid;
2987
		$resql = $this->db->query($sql);
2988
		if ($resql)
2989
		{
2990
			$row = $this->db->fetch_row($resql);
2991
			return $row[0];
2992
		}
2993
	}
2994
2995
	/**
2996
	 *  Function to check if an object is used by others.
2997
	 *  Check is done into this->childtables. There is no check into llx_element_element.
2998
	 *
2999
	 *  @param	int		$id			Force id of object
3000
	 *  @return	int					<0 if KO, 0 if not used, >0 if already used
3001
	 */
3002
	function isObjectUsed($id=0)
3003
	{
3004
		global $langs;
3005
3006
		if (empty($id)) $id=$this->id;
3007
3008
		// Check parameters
3009
		if (! isset($this->childtables) || ! is_array($this->childtables) || count($this->childtables) == 0)
3010
		{
3011
			dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
3012
			return -1;
3013
		}
3014
3015
		$arraytoscan = $this->childtables;
3016
		// For backward compatibility, we check if array is old format array('table1', 'table2', ...)
3017
		$tmparray=array_keys($this->childtables);
3018
		if (is_numeric($tmparray[0]))
3019
		{
3020
			$arraytoscan = array_flip($this->childtables);
3021
		}
3022
3023
		// Test if child exists
3024
		$haschild=0;
3025
		foreach($arraytoscan as $table => $elementname)
3026
		{
3027
			//print $id.'-'.$table.'-'.$elementname.'<br>';
3028
			// Check if third party can be deleted
3029
			$sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table;
3030
			$sql.= " WHERE ".$this->fk_element." = ".$id;
3031
			$resql=$this->db->query($sql);
3032
			if ($resql)
3033
			{
3034
				$obj=$this->db->fetch_object($resql);
3035
				if ($obj->nb > 0)
3036
				{
3037
					$langs->load("errors");
3038
					//print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
3039
					$haschild += $obj->nb;
3040
					$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $langs->transnoentitiesnoconv($elementname));
3041
					break;    // We found at least one, we stop here
3042
				}
3043
			}
3044
			else
3045
			{
3046
				$this->errors[]=$this->db->lasterror();
3047
				return -1;
3048
			}
3049
		}
3050
		if ($haschild > 0)
3051
		{
3052
			$this->errors[]="ErrorRecordHasChildren";
3053
			return $haschild;
3054
		}
3055
		else return 0;
3056
	}
3057
3058
	/**
3059
	 *  Function to say how many lines object contains
3060
	 *
3061
	 *	@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
3062
	 *  @return	int						<0 if KO, 0 if no predefined products, nb of lines with predefined products if found
3063
	 */
3064
	function hasProductsOrServices($predefined=-1)
3065
	{
3066
		$nb=0;
3067
3068
		foreach($this->lines as $key => $val)
3069
		{
3070
			$qualified=0;
3071
			if ($predefined == -1) $qualified=1;
3072
			if ($predefined == 1 && $val->fk_product > 0) $qualified=1;
3073
			if ($predefined == 0 && $val->fk_product <= 0) $qualified=1;
3074
			if ($predefined == 2 && $val->fk_product > 0 && $val->product_type==0) $qualified=1;
3075
			if ($predefined == 3 && $val->fk_product > 0 && $val->product_type==1) $qualified=1;
3076
			if ($qualified) $nb++;
3077
		}
3078
		dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
3079
		return $nb;
3080
	}
3081
3082
	/**
3083
	 * Function that returns the total amount HT of discounts applied for all lines.
3084
	 *
3085
	 * @return 	float
3086
	 */
3087
	function getTotalDiscount()
3088
	{
3089
		$total_discount=0.00;
3090
3091
		$sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
3092
		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element."det";
3093
		$sql.= " WHERE ".$this->fk_element." = ".$this->id;
3094
3095
		dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
3096
		$resql = $this->db->query($sql);
3097
		if ($resql)
3098
		{
3099
			$num=$this->db->num_rows($resql);
3100
			$i=0;
3101
			while ($i < $num)
3102
			{
3103
				$obj = $this->db->fetch_object($resql);
3104
3105
				$pu_ht = $obj->pu_ht;
3106
				$qty= $obj->qty;
3107
				$total_ht = $obj->total_ht;
3108
3109
				$total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
3110
				$total_discount += $total_discount_line;
3111
3112
				$i++;
3113
			}
3114
		}
3115
3116
		//print $total_discount; exit;
3117
		return price2num($total_discount);
3118
	}
3119
3120
3121
	/**
3122
	 * Return into unit=0, the calculated total of weight and volume of all lines * qty
3123
	 * Calculate by adding weight and volume of each product line, so properties ->volume/volume_units/weight/weight_units must be loaded on line.
3124
	 *
3125
	 * @return  array                           array('weight'=>...,'volume'=>...)
3126
	 */
3127
	function getTotalWeightVolume()
3128
	{
3129
		$totalWeight = 0;
3130
		$totalVolume = 0;
3131
		// defined for shipment only
3132
		$totalOrdered = '';
3133
		// defined for shipment only
3134
		$totalToShip = '';
3135
3136
		foreach ($this->lines as $line)
3137
		{
3138
			if (isset($line->qty_asked))
3139
			{
3140
				if (empty($totalOrdered)) $totalOrdered=0;  // Avoid warning because $totalOrdered is ''
3141
				$totalOrdered+=$line->qty_asked;    // defined for shipment only
3142
			}
3143
			if (isset($line->qty_shipped))
3144
			{
3145
				if (empty($totalToShip)) $totalToShip=0;    // Avoid warning because $totalToShip is ''
3146
				$totalToShip+=$line->qty_shipped;   // defined for shipment only
3147
			}
3148
3149
			// Define qty, weight, volume, weight_units, volume_units
3150
			if ($this->element == 'shipping') {
3151
				// for shipments
3152
				$qty = $line->qty_shipped ? $line->qty_shipped : 0;
3153
			}
3154
			else {
3155
				$qty = $line->qty ? $line->qty : 0;
3156
			}
3157
3158
			$weight = $line->weight ? $line->weight : 0;
3159
			$volume = $line->volume ? $line->volume : 0;
3160
3161
			$weight_units=$line->weight_units;
3162
			$volume_units=$line->volume_units;
3163
3164
			$weightUnit=0;
3165
			$volumeUnit=0;
3166
			if (! empty($weight_units)) $weightUnit = $weight_units;
3167
			if (! empty($volume_units)) $volumeUnit = $volume_units;
3168
3169
			if (empty($totalWeight)) $totalWeight=0;  // Avoid warning because $totalWeight is ''
3170
			if (empty($totalVolume)) $totalVolume=0;  // Avoid warning because $totalVolume is ''
3171
3172
			//var_dump($line->volume_units);
3173
			if ($weight_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3174
			{
3175
				$trueWeightUnit=pow(10, $weightUnit);
3176
				$totalWeight += $weight * $qty * $trueWeightUnit;
3177
			}
3178
			else {
3179
		if ($weight_units == 99) {
3180
			// conversion 1 Pound = 0.45359237 KG
3181
			$trueWeightUnit = 0.45359237;
3182
			$totalWeight += $weight * $qty * $trueWeightUnit;
3183
		} elseif ($weight_units == 98) {
3184
			// conversion 1 Ounce = 0.0283495 KG
3185
			$trueWeightUnit = 0.0283495;
3186
			$totalWeight += $weight * $qty * $trueWeightUnit;
3187
		}
3188
		else
3189
					$totalWeight += $weight * $qty;   // This may be wrong if we mix different units
3190
			}
3191
			if ($volume_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3192
			{
3193
				//print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
3194
				$trueVolumeUnit=pow(10, $volumeUnit);
3195
				//print $line->volume;
3196
				$totalVolume += $volume * $qty * $trueVolumeUnit;
3197
			}
3198
			else
3199
			{
3200
				$totalVolume += $volume * $qty;   // This may be wrong if we mix different units
3201
			}
3202
		}
3203
3204
		return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
3205
	}
3206
3207
3208
	/**
3209
	 *	Set extra parameters
3210
	 *
3211
	 *	@return	int      <0 if KO, >0 if OK
3212
	 */
3213
	function setExtraParameters()
3214
	{
3215
		$this->db->begin();
3216
3217
		$extraparams = (! empty($this->extraparams) ? json_encode($this->extraparams) : null);
3218
3219
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3220
		$sql.= " SET extraparams = ".(! empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
3221
		$sql.= " WHERE rowid = ".$this->id;
3222
3223
		dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
3224
		$resql = $this->db->query($sql);
3225
		if (! $resql)
3226
		{
3227
			$this->error=$this->db->lasterror();
3228
			$this->db->rollback();
3229
			return -1;
3230
		}
3231
		else
3232
		{
3233
			$this->db->commit();
3234
			return 1;
3235
		}
3236
	}
3237
3238
3239
	/**
3240
	 *    Return incoterms informations
3241
	 *    TODO Use a cache for label get
3242
	 *
3243
	 *    @return	string	incoterms info
3244
	 */
3245
	function display_incoterms()
3246
	{
3247
		$out = '';
3248
		$this->libelle_incoterms = '';
3249
		if (!empty($this->fk_incoterms))
3250
		{
3251
			$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3252
			$result = $this->db->query($sql);
3253
			if ($result)
3254
			{
3255
				$res = $this->db->fetch_object($result);
3256
				$out .= $res->code;
3257
			}
3258
		}
3259
3260
		$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...
3261
3262
		return $out;
3263
	}
3264
3265
	/**
3266
	 *    Return incoterms informations for pdf display
3267
	 *
3268
	 *    @return	string		incoterms info
3269
	 */
3270
	function getIncotermsForPDF()
3271
	{
3272
		$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3273
		$resql = $this->db->query($sql);
3274
		if ($resql)
3275
		{
3276
			$num = $this->db->num_rows($resql);
3277
			if ($num > 0)
3278
			{
3279
				$res = $this->db->fetch_object($resql);
3280
				return 'Incoterm : '.$res->code.' - '.$this->location_incoterms;
3281
			}
3282
			else
3283
			{
3284
				return '';
3285
			}
3286
		}
3287
		else
3288
		{
3289
			$this->errors[] = $this->db->lasterror();
3290
			return false;
3291
		}
3292
	}
3293
3294
	/**
3295
	 *    Define incoterms values of current object
3296
	 *
3297
	 *    @param	int		$id_incoterm     Id of incoterm to set or '' to remove
3298
	 * 	  @param 	string  $location		 location of incoterm
3299
	 *    @return	int     		<0 if KO, >0 if OK
3300
	 */
3301
	function setIncoterms($id_incoterm, $location)
3302
	{
3303
		if ($this->id && $this->table_element)
3304
		{
3305
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3306
			$sql.= " SET fk_incoterms = ".($id_incoterm > 0 ? $id_incoterm : "null");
3307
			$sql.= ", location_incoterms = ".($id_incoterm > 0 ? "'".$this->db->escape($location)."'" : "null");
3308
			$sql.= " WHERE rowid = " . $this->id;
3309
			dol_syslog(get_class($this).'::setIncoterms', LOG_DEBUG);
3310
			$resql=$this->db->query($sql);
3311
			if ($resql)
3312
			{
3313
				$this->fk_incoterms = $id_incoterm;
3314
				$this->location_incoterms = $location;
3315
3316
				$sql = 'SELECT libelle FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3317
				$res = $this->db->query($sql);
3318
				if ($res)
3319
				{
3320
					$obj = $this->db->fetch_object($res);
3321
					$this->libelle_incoterms = $obj->libelle;
3322
				}
3323
				return 1;
3324
			}
3325
			else
3326
			{
3327
				$this->errors[] = $this->db->lasterror();
3328
				return -1;
3329
			}
3330
		}
3331
		else return -1;
3332
	}
3333
3334
3335
	/**
3336
	 *  Return if a country is inside the EEC (European Economic Community)
3337
	 *  @deprecated
3338
	 *
3339
	 *  @return     boolean		true = country inside EEC, false = country outside EEC
3340
	 */
3341
	function isInEEC()
3342
	{
3343
		require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
3344
		return isInEEC($this);
3345
	}
3346
3347
3348
	// --------------------
3349
	// TODO: All functions here must be redesigned and moved as they are not business functions but output functions
3350
	// --------------------
3351
3352
	/* This is to show add lines */
3353
3354
	/**
3355
	 *	Show add free and predefined products/services form
3356
	 *
3357
	 *  @param	int		        $dateSelector       1=Show also date range input fields
3358
	 *  @param	Societe			$seller				Object thirdparty who sell
3359
	 *  @param	Societe			$buyer				Object thirdparty who buy
3360
	 *	@return	void
3361
	 */
3362
	function formAddObjectLine($dateSelector,$seller,$buyer)
3363
	{
3364
		global $conf,$user,$langs,$object,$hookmanager;
3365
		global $form,$bcnd,$var;
3366
3367
		//Line extrafield
3368
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3369
		$extrafieldsline = new ExtraFields($this->db);
3370
		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3371
3372
		// Output template part (modules that overwrite templates must declare this into descriptor)
3373
		// Use global variables + $dateSelector + $seller and $buyer
3374
		$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3375
		foreach($dirtpls as $reldir)
3376
		{
3377
			$tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
3378
			if (empty($conf->file->strict_mode)) {
3379
				$res=@include $tpl;
3380
			} else {
3381
				$res=include $tpl; // for debug
3382
			}
3383
			if ($res) break;
3384
		}
3385
	}
3386
3387
3388
3389
	/* This is to show array of line of details */
3390
3391
3392
	/**
3393
	 *	Return HTML table for object lines
3394
	 *	TODO Move this into an output class file (htmlline.class.php)
3395
	 *	If lines are into a template, title must also be into a template
3396
	 *	But for the moment we don't know if it'st possible as we keep a method available on overloaded objects.
3397
	 *
3398
	 *	@param	string		$action				Action code
3399
	 *	@param  string		$seller            	Object of seller third party
3400
	 *	@param  string  	$buyer             	Object of buyer third party
3401
	 *	@param	int			$selected		   	Object line selected
3402
	 *	@param  int	    	$dateSelector      	1=Show also date range input fields
3403
	 *	@return	void
3404
	 */
3405
	function printObjectLines($action, $seller, $buyer, $selected=0, $dateSelector=0)
3406
	{
3407
		global $conf, $hookmanager, $langs, $user;
3408
		// TODO We should not use global var for this !
3409
		global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
3410
3411
		// Define usemargins
3412
		$usemargins=0;
3413
		if (! empty($conf->margin->enabled) && ! empty($this->element) && in_array($this->element,array('facture','propal','commande'))) $usemargins=1;
3414
3415
		$num = count($this->lines);
3416
3417
		//Line extrafield
3418
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3419
		$extrafieldsline = new ExtraFields($this->db);
3420
		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3421
3422
		$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...
3423
		$reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3424
		if (empty($reshook))
3425
		{
3426
			print '<tr class="liste_titre nodrag nodrop">';
3427
3428
			if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) print '<td class="linecolnum" align="center" width="5">&nbsp;</td>';
3429
3430
			// Description
3431
			print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
3432
3433
			if ($this->element == 'supplier_proposal')
3434
			{
3435
				print '<td class="linerefsupplier" align="right"><span id="title_fourn_ref">'.$langs->trans("SupplierProposalRefFourn").'</span></td>';
3436
			}
3437
3438
			// VAT
3439
			print '<td class="linecolvat" align="right" width="80">'.$langs->trans('VAT').'</td>';
3440
3441
			// Price HT
3442
			print '<td class="linecoluht" align="right" width="80">'.$langs->trans('PriceUHT').'</td>';
3443
3444
			// Multicurrency
3445
			if (!empty($conf->multicurrency->enabled)) print '<td class="linecoluht_currency" align="right" width="80">'.$langs->trans('PriceUHTCurrency', $this->multicurrency_code).'</td>';
3446
3447
			if ($inputalsopricewithtax) print '<td align="right" width="80">'.$langs->trans('PriceUTTC').'</td>';
3448
3449
			// Qty
3450
			print '<td class="linecolqty" align="right">'.$langs->trans('Qty').'</td>';
3451
3452
			if($conf->global->PRODUCT_USE_UNITS)
3453
			{
3454
				print '<td class="linecoluseunit" align="left">'.$langs->trans('Unit').'</td>';
3455
			}
3456
3457
			// Reduction short
3458
			print '<td class="linecoldiscount" align="right">'.$langs->trans('ReductionShort').'</td>';
3459
3460
			if ($this->situation_cycle_ref) {
3461
				print '<td class="linecolcycleref" align="right">' . $langs->trans('Progress') . '</td>';
3462
			}
3463
3464
			if ($usemargins && ! empty($conf->margin->enabled) && empty($user->societe_id))
3465
			{
3466
				if (!empty($user->rights->margins->creer))
3467
				{
3468
					if ($conf->global->MARGIN_TYPE == "1")
3469
						print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('BuyingPrice').'</td>';
3470
					else
3471
						print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('CostPrice').'</td>';
3472
				}
3473
3474
				if (! empty($conf->global->DISPLAY_MARGIN_RATES) && $user->rights->margins->liretous)
3475
					print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarginRate').'</td>';
3476
				if (! empty($conf->global->DISPLAY_MARK_RATES) && $user->rights->margins->liretous)
3477
					print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarkRate').'</td>';
3478
			}
3479
3480
			// Total HT
3481
			print '<td class="linecolht" align="right">'.$langs->trans('TotalHTShort').'</td>';
3482
3483
			// Multicurrency
3484
			if (!empty($conf->multicurrency->enabled)) print '<td class="linecoltotalht_currency" align="right">'.$langs->trans('TotalHTShortCurrency', $this->multicurrency_code).'</td>';
3485
3486
			if ($outputalsopricetotalwithtax) print '<td align="right" width="80">'.$langs->trans('TotalTTCShort').'</td>';
3487
3488
			print '<td class="linecoledit"></td>';  // No width to allow autodim
3489
3490
			print '<td class="linecoldelete" width="10"></td>';
3491
3492
			print '<td class="linecolmove" width="10"></td>';
3493
3494
			print "</tr>\n";
3495
		}
3496
3497
		$var = true;
3498
		$i	 = 0;
3499
3500
		foreach ($this->lines as $line)
3501
		{
3502
			//Line extrafield
3503
			$line->fetch_optionals($line->id,$extralabelslines);
3504
3505
3506
3507
			//if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
3508
			if (is_object($hookmanager))   // Old code is commented on preceding line.
3509
			{
3510
				if (empty($line->fk_parent_line))
3511
				{
3512
					$parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
3513
					$reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3514
				}
3515
				else
3516
				{
3517
					$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);
3518
					$reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3519
				}
3520
			}
3521
			if (empty($reshook))
3522
			{
3523
				$this->printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected,$extrafieldsline);
3524
			}
3525
3526
			$i++;
3527
		}
3528
	}
3529
3530
	/**
3531
	 *	Return HTML content of a detail line
3532
	 *	TODO Move this into an output class file (htmlline.class.php)
3533
	 *
3534
	 *	@param	string		$action				GET/POST action
3535
	 *	@param CommonObjectLine $line		       	Selected object line to output
3536
	 *	@param  string	    $var               	Is it a an odd line (true)
3537
	 *	@param  int		    $num               	Number of line (0)
3538
	 *	@param  int		    $i					I
3539
	 *	@param  int		    $dateSelector      	1=Show also date range input fields
3540
	 *	@param  string	    $seller            	Object of seller third party
3541
	 *	@param  string	    $buyer             	Object of buyer third party
3542
	 *	@param	int			$selected		   	Object line selected
3543
	 *  @param  int			$extrafieldsline	Object of extrafield line attribute
3544
	 *	@return	void
3545
	 */
3546
	function printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected=0,$extrafieldsline=0)
3547
	{
3548
		global $conf,$langs,$user,$object,$hookmanager;
3549
		global $form,$bc,$bcdd;
3550
		global $object_rights, $disableedit, $disablemove;   // TODO We should not use global var for this !
3551
3552
		$object_rights = $this->getRights();
3553
3554
		$element=$this->element;
3555
3556
		$text=''; $description=''; $type=0;
3557
3558
		// Show product and description
3559
		$type=(! empty($line->product_type)?$line->product_type:$line->fk_product_type);
3560
		// Try to enhance type detection using date_start and date_end for free lines where type was not saved.
3561
		if (! empty($line->date_start)) $type=1; // deprecated
3562
		if (! empty($line->date_end)) $type=1; // deprecated
3563
3564
		// Ligne en mode visu
3565
		if ($action != 'editline' || $selected != $line->id)
3566
		{
3567
			// Product
3568
			if ($line->fk_product > 0)
3569
			{
3570
				$product_static = new Product($this->db);
3571
				$product_static->fetch($line->fk_product);
3572
3573
				$product_static->ref = $line->ref; //can change ref in hook
3574
				$product_static->label = $line->label; //can change label in hook
3575
				$text=$product_static->getNomUrl(1);
3576
3577
				// Define output language and label
3578
				if (! empty($conf->global->MAIN_MULTILANGS))
3579
				{
3580
					if (! is_object($this->thirdparty))
3581
					{
3582
						dol_print_error('','Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
3583
						return;
3584
					}
3585
3586
					$prod = new Product($this->db);
3587
					$prod->fetch($line->fk_product);
3588
3589
					$outputlangs = $langs;
3590
					$newlang='';
3591
					if (empty($newlang) && GETPOST('lang_id','aZ09')) $newlang=GETPOST('lang_id','aZ09');
3592
					if (! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang)) $newlang=$this->thirdparty->default_lang;		// For language to language of customer
3593
					if (! empty($newlang))
3594
					{
3595
						$outputlangs = new Translate("",$conf);
3596
						$outputlangs->setDefaultLang($newlang);
3597
					}
3598
3599
					$label = (! empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
3600
				}
3601
				else
3602
				{
3603
					$label = $line->product_label;
3604
				}
3605
3606
				$text.= ' - '.(! empty($line->label)?$line->label:$label);
3607
				$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.
3608
			}
3609
3610
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
3611
3612
			// Output template part (modules that overwrite templates must declare this into descriptor)
3613
			// Use global variables + $dateSelector + $seller and $buyer
3614
			$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3615
			foreach($dirtpls as $reldir)
3616
			{
3617
				$tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
3618
				if (empty($conf->file->strict_mode)) {
3619
					$res=@include $tpl;
3620
				} else {
3621
					$res=include $tpl; // for debug
3622
				}
3623
				if ($res) break;
3624
			}
3625
		}
3626
3627
		// Ligne en mode update
3628
		if ($this->statut == 0 && $action == 'editline' && $selected == $line->id)
3629
		{
3630
			$label = (! empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
3631
			if (! empty($conf->global->MAIN_HTML5_PLACEHOLDER)) $placeholder=' placeholder="'.$langs->trans("Label").'"';
3632
			else $placeholder=' title="'.$langs->trans("Label").'"';
3633
3634
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
3635
3636
			// Output template part (modules that overwrite templates must declare this into descriptor)
3637
			// Use global variables + $dateSelector + $seller and $buyer
3638
			$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3639
			foreach($dirtpls as $reldir)
3640
			{
3641
				$tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
3642
				if (empty($conf->file->strict_mode)) {
3643
					$res=@include $tpl;
3644
				} else {
3645
					$res=include $tpl; // for debug
3646
				}
3647
				if ($res) break;
3648
			}
3649
		}
3650
	}
3651
3652
3653
	/* This is to show array of line of details of source object */
3654
3655
3656
	/**
3657
	 * 	Return HTML table table of source object lines
3658
	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
3659
	 *  If lines are into a template, title must also be into a template
3660
	 *  But for the moment we don't know if it's possible, so we keep the method available on overloaded objects.
3661
	 *
3662
	 *	@param	string		$restrictlist		''=All lines, 'services'=Restrict to services only
3663
	 *  @return	void
3664
	 */
3665
	function printOriginLinesList($restrictlist='')
3666
	{
3667
		global $langs, $hookmanager, $conf;
3668
3669
		print '<tr class="liste_titre">';
3670
		print '<td>'.$langs->trans('Ref').'</td>';
3671
		print '<td>'.$langs->trans('Description').'</td>';
3672
		print '<td align="right">'.$langs->trans('VATRate').'</td>';
3673
		print '<td align="right">'.$langs->trans('PriceUHT').'</td>';
3674
		if (!empty($conf->multicurrency->enabled)) print '<td align="right">'.$langs->trans('PriceUHTCurrency').'</td>';
3675
		print '<td align="right">'.$langs->trans('Qty').'</td>';
3676
		if($conf->global->PRODUCT_USE_UNITS)
3677
		{
3678
			print '<td align="left">'.$langs->trans('Unit').'</td>';
3679
		}
3680
		print '<td align="right">'.$langs->trans('ReductionShort').'</td></tr>';
3681
3682
		$var = true;
3683
		$i	 = 0;
3684
3685
		foreach ($this->lines as $line)
3686
		{
3687
			if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
3688
			{
3689
				if (empty($line->fk_parent_line))
3690
				{
3691
					$parameters=array('line'=>$line,'var'=>$var,'i'=>$i);
3692
					$action='';
3693
					$hookmanager->executeHooks('printOriginObjectLine',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
3694
				}
3695
			}
3696
			else
3697
			{
3698
				$this->printOriginLine($line, $var, $restrictlist);
3699
			}
3700
3701
			$i++;
3702
		}
3703
	}
3704
3705
	/**
3706
	 * 	Return HTML with a line of table array of source object lines
3707
	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
3708
	 *  If lines are into a template, title must also be into a template
3709
	 *  But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
3710
	 *
3711
	 * 	@param	CommonObjectLine	$line				Line
3712
	 * 	@param	string				$var				Var
3713
	 *	@param	string				$restrictlist		''=All lines, 'services'=Restrict to services only (strike line if not)
3714
	 * 	@return	void
3715
	 */
3716
	function printOriginLine($line, $var, $restrictlist='')
3717
	{
3718
		global $langs, $conf;
3719
3720
		//var_dump($line);
3721
		if (!empty($line->date_start))
3722
		{
3723
			$date_start=$line->date_start;
3724
		}
3725
		else
3726
		{
3727
			$date_start=$line->date_debut_prevue;
3728
			if ($line->date_debut_reel) $date_start=$line->date_debut_reel;
3729
		}
3730
		if (!empty($line->date_end))
3731
		{
3732
			$date_end=$line->date_end;
3733
		}
3734
		else
3735
		{
3736
			$date_end=$line->date_fin_prevue;
3737
			if ($line->date_fin_reel) $date_end=$line->date_fin_reel;
3738
		}
3739
3740
		$this->tpl['label'] = '';
3741
		if (! empty($line->fk_parent_line)) $this->tpl['label'].= img_picto('', 'rightarrow');
3742
3743
		if (($line->info_bits & 2) == 2)  // TODO Not sure this is used for source object
3744
		{
3745
			$discount=new DiscountAbsolute($this->db);
3746
			$discount->fk_soc = $this->socid;
3747
			$this->tpl['label'].= $discount->getNomUrl(0,'discount');
3748
		}
3749
		else if (! empty($line->fk_product))
3750
		{
3751
			$productstatic = new Product($this->db);
3752
			$productstatic->id = $line->fk_product;
3753
			$productstatic->ref = $line->ref;
3754
			$productstatic->type = $line->fk_product_type;
3755
			$this->tpl['label'].= $productstatic->getNomUrl(1);
3756
			$this->tpl['label'].= ' - '.(! empty($line->label)?$line->label:$line->product_label);
3757
			// Dates
3758
			if ($line->product_type == 1 && ($date_start || $date_end))
3759
			{
3760
				$this->tpl['label'].= get_date_range($date_start,$date_end);
3761
			}
3762
		}
3763
		else
3764
		{
3765
			$this->tpl['label'].= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''),'service') : img_object($langs->trans(''),'product')));
3766
			if (!empty($line->desc)) {
3767
				$this->tpl['label'].=$line->desc;
3768
			}else {
3769
				$this->tpl['label'].= ($line->label ? '&nbsp;'.$line->label : '');
3770
			}
3771
			// Dates
3772
			if ($line->product_type == 1 && ($date_start || $date_end))
3773
			{
3774
				$this->tpl['label'].= get_date_range($date_start,$date_end);
3775
			}
3776
		}
3777
3778
		if (! empty($line->desc))
3779
		{
3780
			if ($line->desc == '(CREDIT_NOTE)')  // TODO Not sure this is used for source object
3781
			{
3782
				$discount=new DiscountAbsolute($this->db);
3783
				$discount->fetch($line->fk_remise_except);
3784
				$this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote",$discount->getNomUrl(0));
3785
			}
3786
			elseif ($line->desc == '(DEPOSIT)')  // TODO Not sure this is used for source object
3787
			{
3788
				$discount=new DiscountAbsolute($this->db);
3789
				$discount->fetch($line->fk_remise_except);
3790
				$this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit",$discount->getNomUrl(0));
3791
			}
3792
			elseif ($line->desc == '(EXCESS RECEIVED)')
3793
			{
3794
				$discount=new DiscountAbsolute($this->db);
3795
				$discount->fetch($line->fk_remise_except);
3796
				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived",$discount->getNomUrl(0));
3797
			}
3798
			else
3799
			{
3800
				$this->tpl['description'] = dol_trunc($line->desc,60);
3801
			}
3802
		}
3803
		else
3804
		{
3805
			$this->tpl['description'] = '&nbsp;';
3806
		}
3807
3808
		// VAT Rate
3809
		$this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
3810
		if (! empty($line->vat_src_code) && ! preg_match('/\(/', $this->tpl['vat_rate'])) $this->tpl['vat_rate'].=' ('.$line->vat_src_code.')';
3811
3812
		$this->tpl['price'] = price($line->subprice);
3813
		$this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
3814
		$this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
3815
		if ($conf->global->PRODUCT_USE_UNITS) $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
3816
		$this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
3817
3818
		// Is the line strike or not
3819
		$this->tpl['strike']=0;
3820
		if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) $this->tpl['strike']=1;
3821
3822
		// Output template part (modules that overwrite templates must declare this into descriptor)
3823
		// Use global variables + $dateSelector + $seller and $buyer
3824
		$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3825
		foreach($dirtpls as $reldir)
3826
		{
3827
			$tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
3828
			if (empty($conf->file->strict_mode)) {
3829
				$res=@include $tpl;
3830
			} else {
3831
				$res=include $tpl; // for debug
3832
			}
3833
			if ($res) break;
3834
		}
3835
	}
3836
3837
3838
	/**
3839
	 *	Add resources to the current object : add entry into llx_element_resources
3840
	 *	Need $this->element & $this->id
3841
	 *
3842
	 *	@param		int		$resource_id		Resource id
3843
	 *	@param		string	$resource_type		'resource'
3844
	 *	@param		int		$busy				Busy or not
3845
	 *	@param		int		$mandatory			Mandatory or not
3846
	 *	@return		int							<=0 if KO, >0 if OK
3847
	 */
3848
	function add_element_resource($resource_id, $resource_type, $busy=0, $mandatory=0)
3849
	{
3850
		$this->db->begin();
3851
3852
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources (";
3853
		$sql.= "resource_id";
3854
		$sql.= ", resource_type";
3855
		$sql.= ", element_id";
3856
		$sql.= ", element_type";
3857
		$sql.= ", busy";
3858
		$sql.= ", mandatory";
3859
		$sql.= ") VALUES (";
3860
		$sql.= $resource_id;
3861
		$sql.= ", '".$this->db->escape($resource_type)."'";
3862
		$sql.= ", '".$this->db->escape($this->id)."'";
3863
		$sql.= ", '".$this->db->escape($this->element)."'";
3864
		$sql.= ", '".$this->db->escape($busy)."'";
3865
		$sql.= ", '".$this->db->escape($mandatory)."'";
3866
		$sql.= ")";
3867
3868
		dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
3869
		if ($this->db->query($sql))
3870
		{
3871
			$this->db->commit();
3872
			return 1;
3873
		}
3874
		else
3875
		{
3876
			$this->error=$this->db->lasterror();
3877
			$this->db->rollback();
3878
			return  0;
3879
		}
3880
	}
3881
3882
	/**
3883
	 *    Delete a link to resource line
3884
	 *
3885
	 *    @param	int		$rowid			Id of resource line to delete
3886
	 *    @param	int		$element		element name (for trigger) TODO: use $this->element into commonobject class
3887
	 *    @param	int		$notrigger		Disable all triggers
3888
	 *    @return   int						>0 if OK, <0 if KO
3889
	 */
3890
	function delete_resource($rowid, $element, $notrigger=0)
3891
	{
3892
		global $user;
3893
3894
		$this->db->begin();
3895
3896
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources";
3897
		$sql.= " WHERE rowid=".$rowid;
3898
3899
		dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
3900
3901
		$resql=$this->db->query($sql);
3902
		if (! $resql)
3903
		{
3904
			$this->error=$this->db->lasterror();
3905
			$this->db->rollback();
3906
			return -1;
3907
		}
3908
		else
3909
		{
3910
			if (! $notrigger)
3911
			{
3912
				$result=$this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
3913
				if ($result < 0) { $this->db->rollback(); return -1; }
3914
			}
3915
			$this->db->commit();
3916
			return 1;
3917
		}
3918
	}
3919
3920
3921
	/**
3922
	 * Overwrite magic function to solve problem of cloning object that are kept as references
3923
	 *
3924
	 * @return void
3925
	 */
3926
	function __clone()
3927
	{
3928
		// Force a copy of this->lines, otherwise it will point to same object.
3929
		if (isset($this->lines) && is_array($this->lines))
3930
		{
3931
			$nboflines=count($this->lines);
3932
			for($i=0; $i < $nboflines; $i++)
3933
			{
3934
				$this->lines[$i] = clone $this->lines[$i];
3935
			}
3936
		}
3937
	}
3938
3939
	/**
3940
	 * Common function for all objects extending CommonObject for generating documents
3941
	 *
3942
	 * @param 	string 		$modelspath 	Relative folder where generators are placed
3943
	 * @param 	string 		$modele 		Generator to use. Caller must set it to obj->modelpdf or GETPOST('modelpdf') for example.
3944
	 * @param 	Translate 	$outputlangs 	Output language to use
3945
	 * @param 	int 		$hidedetails 	1 to hide details. 0 by default
3946
	 * @param 	int 		$hidedesc 		1 to hide product description. 0 by default
3947
	 * @param 	int 		$hideref 		1 to hide product reference. 0 by default
3948
	 * @param   null|array  $moreparams     Array to provide more information
3949
	 * @return 	int 						>0 if OK, <0 if KO
3950
	 */
3951
	protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
3952
	{
3953
		global $conf, $langs, $user;
3954
3955
		$srctemplatepath='';
3956
3957
		// Increase limit for PDF build
3958
		$err=error_reporting();
3959
		error_reporting(0);
3960
		@set_time_limit(120);
3961
		error_reporting($err);
3962
3963
		// If selected model is a filename template (then $modele="modelname" or "modelname:filename")
3964
		$tmp=explode(':',$modele,2);
3965
		if (! empty($tmp[1]))
3966
		{
3967
			$modele=$tmp[0];
3968
			$srctemplatepath=$tmp[1];
3969
		}
3970
3971
		// Search template files
3972
		$file=''; $classname=''; $filefound=0;
3973
		$dirmodels=array('/');
3974
		if (is_array($conf->modules_parts['models'])) $dirmodels=array_merge($dirmodels,$conf->modules_parts['models']);
3975
		foreach($dirmodels as $reldir)
3976
		{
3977
			foreach(array('doc','pdf') as $prefix)
3978
			{
3979
				if (in_array(get_class($this), array('Adherent'))) $file = $prefix."_".$modele.".class.php";     // Member module use prefix_module.class.php
3980
				else $file = $prefix."_".$modele.".modules.php";
3981
3982
				// On verifie l'emplacement du modele
3983
				$file=dol_buildpath($reldir.$modelspath.$file,0);
3984
				if (file_exists($file))
3985
				{
3986
					$filefound=1;
3987
					$classname=$prefix.'_'.$modele;
3988
					break;
3989
				}
3990
			}
3991
			if ($filefound) break;
3992
		}
3993
3994
		// If generator was found
3995
		if ($filefound)
3996
		{
3997
			global $db;  // Required to solve a conception default in commonstickergenerator.class.php making an include of code using $db
3998
3999
			require_once $file;
4000
4001
			$obj = new $classname($this->db);
4002
4003
			// If generator is ODT, we must have srctemplatepath defined, if not we set it.
4004
			if ($obj->type == 'odt' && empty($srctemplatepath))
4005
			{
4006
				$varfortemplatedir=$obj->scandir;
4007
				if ($varfortemplatedir && ! empty($conf->global->$varfortemplatedir))
4008
				{
4009
					$dirtoscan=$conf->global->$varfortemplatedir;
4010
4011
					$listoffiles=array();
4012
4013
					// Now we add first model found in directories scanned
4014
					$listofdir=explode(',',$dirtoscan);
4015
					foreach($listofdir as $key => $tmpdir)
4016
					{
4017
						$tmpdir=trim($tmpdir);
4018
						$tmpdir=preg_replace('/DOL_DATA_ROOT/',DOL_DATA_ROOT,$tmpdir);
4019
						if (! $tmpdir) { unset($listofdir[$key]); continue; }
4020
						if (is_dir($tmpdir))
4021
						{
4022
							$tmpfiles=dol_dir_list($tmpdir,'files',0,'\.od(s|t)$','','name',SORT_ASC,0);
4023
							if (count($tmpfiles)) $listoffiles=array_merge($listoffiles,$tmpfiles);
4024
						}
4025
					}
4026
4027
					if (count($listoffiles))
4028
					{
4029
						foreach($listoffiles as $record)
4030
						{
4031
							$srctemplatepath=$record['fullname'];
4032
							break;
4033
						}
4034
					}
4035
				}
4036
4037
				if (empty($srctemplatepath))
4038
				{
4039
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
4040
					return -1;
4041
				}
4042
			}
4043
4044
			if ($obj->type == 'odt' && ! empty($srctemplatepath))
4045
			{
4046
				if (! dol_is_file($srctemplatepath))
4047
				{
4048
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
4049
					return -1;
4050
				}
4051
			}
4052
4053
			// We save charset_output to restore it because write_file can change it if needed for
4054
			// output format that does not support UTF8.
4055
			$sav_charset_output=$outputlangs->charset_output;
4056
4057
			if (in_array(get_class($this), array('Adherent')))
4058
			{
4059
				$arrayofrecords = array();   // The write_file of templates of adherent class need this var
4060
				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams);
4061
			}
4062
			else
4063
			{
4064
				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
4065
			}
4066
			// After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
4067
4068
			if ($resultwritefile > 0)
4069
			{
4070
				$outputlangs->charset_output=$sav_charset_output;
4071
4072
				// We delete old preview
4073
				require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4074
				dol_delete_preview($this);
4075
4076
				// Index file in database
4077
				if (! empty($obj->result['fullpath']))
4078
				{
4079
					$destfull = $obj->result['fullpath'];
4080
					$upload_dir = dirname($destfull);
4081
					$destfile = basename($destfull);
4082
					$rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $upload_dir);
4083
4084
					if (! preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir))     // If not a tmp dir
4085
					{
4086
						$filename = basename($destfile);
4087
						$rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
4088
						$rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
4089
4090
						include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
4091
						$ecmfile=new EcmFiles($this->db);
4092
						$result = $ecmfile->fetch(0, '', ($rel_dir?$rel_dir.'/':'').$filename);
4093
4094
						if (! empty($conf->global->PROPOSAL_USE_ONLINE_SIGN))
4095
						{
4096
							if (empty($ecmfile->share))	// Because object not found or share not set yet
4097
							{
4098
								require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
4099
								$ecmfile->share = getRandomPassword(true);
4100
							}
4101
						}
4102
4103
						if ($result > 0)
4104
						{
4105
							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4106
							$ecmfile->fullpath_orig = '';
4107
							$ecmfile->gen_or_uploaded = 'generated';
4108
							$ecmfile->description = '';    // indexed content
4109
							$ecmfile->keyword = '';        // keyword content
4110
							$result = $ecmfile->update($user);
4111
							if ($result < 0)
4112
							{
4113
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4114
							}
4115
						}
4116
						else
4117
						{
4118
							$ecmfile->filepath = $rel_dir;
4119
							$ecmfile->filename = $filename;
4120
							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4121
							$ecmfile->fullpath_orig = '';
4122
							$ecmfile->gen_or_uploaded = 'generated';
4123
							$ecmfile->description = '';    // indexed content
4124
							$ecmfile->keyword = '';        // keyword content
4125
							$result = $ecmfile->create($user);
4126
							if ($result < 0)
4127
							{
4128
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4129
							}
4130
						}
4131
4132
						/*$this->result['fullname']=$destfull;
4133
						$this->result['filepath']=$ecmfile->filepath;
4134
						$this->result['filename']=$ecmfile->filename;*/
4135
4136
						// Update the last_main_doc field into main object
4137
						$update_main_doc_field=0;
4138
						if (! empty($obj->update_main_doc_field)) $update_main_doc_field=1;
4139
						if ($update_main_doc_field && ! empty($this->table_element))
4140
						{
4141
							$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET last_main_doc = '".($ecmfile->filepath.'/'.$ecmfile->filename)."'";
4142
							$sql.= ' WHERE rowid = '.$this->id;
4143
							$resql = $this->db->query($sql);
4144
							if (! $resql) dol_print_error($this->db);
4145
						}
4146
					}
4147
				}
4148
				else
4149
				{
4150
					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);
4151
				}
4152
4153
				// Success in building document. We build meta file.
4154
				dol_meta_create($this);
4155
4156
				return 1;
4157
			}
4158
			else
4159
			{
4160
				$outputlangs->charset_output=$sav_charset_output;
4161
				dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors);
4162
				return -1;
4163
			}
4164
4165
		}
4166
		else
4167
		{
4168
			$this->error=$langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists",$file);
4169
			dol_print_error('',$this->error);
4170
			return -1;
4171
		}
4172
	}
4173
4174
	/**
4175
	 *  Build thumb
4176
	 *
4177
	 *  @param      string	$file           Path file in UTF8 to original file to create thumbs from.
4178
	 *	@return		void
4179
	 */
4180
	function addThumbs($file)
4181
	{
4182
		global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality;
4183
4184
		require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';		// This define also $maxwidthsmall, $quality, ...
4185
4186
		$file_osencoded=dol_osencode($file);
4187
		if (file_exists($file_osencoded))
4188
		{
4189
			// Create small thumbs for company (Ratio is near 16/9)
4190
			// Used on logon for example
4191
			vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
4192
4193
			// Create mini thumbs for company (Ratio is near 16/9)
4194
			// Used on menu or for setup page for example
4195
			vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
4196
		}
4197
	}
4198
4199
4200
	/* Functions common to commonobject and commonobjectline */
4201
4202
	/* For default values */
4203
4204
	/**
4205
	 * Return the default value to use for a field when showing the create form of object.
4206
	 * Return values in this order:
4207
	 * 1) If parameter is available into POST, we return it first.
4208
	 * 2) If not but an alternate value was provided as parameter of function, we return it.
4209
	 * 3) If not but a constant $conf->global->OBJECTELEMENT_FIELDNAME is set, we return it (It is better to use the dedicated table).
4210
	 * 4) Return value found into database (TODO No yet implemented)
4211
	 *
4212
	 * @param   string              $fieldname          Name of field
4213
	 * @param   string              $alternatevalue     Alternate value to use
4214
	 * @return  string|string[]                         Default value (can be an array if the GETPOST return an array)
4215
	 **/
4216
	function getDefaultCreateValueFor($fieldname, $alternatevalue=null)
4217
	{
4218
		global $conf, $_POST;
4219
4220
		// If param here has been posted, we use this value first.
4221
		if (isset($_POST[$fieldname])) return GETPOST($fieldname, 2);
4222
4223
		if (isset($alternatevalue)) return $alternatevalue;
4224
4225
		$newelement=$this->element;
4226
		if ($newelement == 'facture') $newelement='invoice';
4227
		if ($newelement == 'commande') $newelement='order';
4228
		if (empty($newelement))
4229
		{
4230
			dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
4231
			return '';
4232
		}
4233
4234
		$keyforfieldname=strtoupper($newelement.'_DEFAULT_'.$fieldname);
4235
		//var_dump($keyforfieldname);
4236
		if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname;
4237
4238
		// TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
4239
4240
	}
4241
4242
4243
	/* For triggers */
4244
4245
4246
	/**
4247
	 * Call trigger based on this instance.
4248
	 * Some context information may also be provided into array property this->context.
4249
	 * NB:  Error from trigger are stacked in interface->errors
4250
	 * NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction.
4251
	 *
4252
	 * @param   string    $trigger_name   trigger's name to execute
4253
	 * @param   User      $user           Object user
4254
	 * @return  int                       Result of run_triggers
4255
	 */
4256
	function call_trigger($trigger_name, $user)
4257
	{
4258
		global $langs,$conf;
4259
4260
		include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
4261
		$interface=new Interfaces($this->db);
4262
		$result=$interface->run_triggers($trigger_name,$this,$user,$langs,$conf);
4263
4264
		if ($result < 0)
4265
		{
4266
			if (!empty($this->errors))
4267
			{
4268
				$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.
4269
			}
4270
			else
4271
			{
4272
				$this->errors=$interface->errors;
4273
			}
4274
		}
4275
		return $result;
4276
	}
4277
4278
4279
	/* Functions for extrafields */
4280
4281
4282
	/**
4283
	 *  Function to get extra fields of a member into $this->array_options
4284
	 *  This method is in most cases called by method fetch of objects but you can call it separately.
4285
	 *
4286
	 *  @param	int		$rowid			Id of line. Use the id of object if not defined. Deprecated. Function must be called without parameters.
4287
	 *  @param  array	$optionsArray   Array resulting of call of extrafields->fetch_name_optionals_label(). Deprecated. Function must be called without parameters.
4288
	 *  @return	int						<0 if error, 0 if no values of extrafield to find nor found, 1 if an attribute is found and value loaded
4289
	 */
4290
	function fetch_optionals($rowid=null,$optionsArray=null)
4291
	{
4292
		if (empty($rowid)) $rowid=$this->id;
4293
4294
		// To avoid SQL errors. Probably not the better solution though
4295
		if (!$this->table_element) {
4296
			return 0;
4297
		}
4298
4299
		if (! is_array($optionsArray))
4300
		{
4301
			// If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
4302
			// TODO Use of existing extrafield is not yet ready (must mutualize code that use extrafields in form first)
4303
			// global $extrafields;
4304
			//if (! is_object($extrafields))
4305
			//{
4306
				require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4307
				$extrafields = new ExtraFields($this->db);
4308
			//}
4309
4310
			// Load array of extrafields for elementype = $this->table_element
4311
			if (empty($extrafields->attributes[$this->table_element]['loaded']))
4312
			{
4313
				$extrafields->fetch_name_optionals_label($this->table_element);
4314
			}
4315
			$optionsArray = $extrafields->attributes[$this->table_element]['label'];
4316
		}
4317
4318
		$table_element = $this->table_element;
4319
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4320
4321
		// Request to get complementary values
4322
		if (is_array($optionsArray) && count($optionsArray) > 0)
4323
		{
4324
			$sql = "SELECT rowid";
4325
			foreach ($optionsArray as $name => $label)
4326
			{
4327
				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...
4328
				{
4329
					$sql.= ", ".$name;
4330
				}
4331
			}
4332
			$sql.= " FROM ".MAIN_DB_PREFIX.$table_element."_extrafields";
4333
			$sql.= " WHERE fk_object = ".$rowid;
4334
4335
			dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG);
4336
			$resql=$this->db->query($sql);
4337
			if ($resql)
4338
			{
4339
				$numrows=$this->db->num_rows($resql);
4340
				if ($numrows)
4341
				{
4342
					$tab = $this->db->fetch_array($resql);
4343
4344
					foreach ($tab as $key => $value)
4345
					{
4346
						// 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)
4347
						if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && ! is_int($key))
4348
						{
4349
							// we can add this attribute to object
4350
							$this->array_options["options_".$key]=$value;
4351
						}
4352
					}
4353
				}
4354
4355
				$this->db->free($resql);
4356
4357
				if ($numrows) return $numrows;
4358
				else return 0;
4359
			}
4360
			else
4361
			{
4362
				dol_print_error($this->db);
4363
				return -1;
4364
			}
4365
		}
4366
		return 0;
4367
	}
4368
4369
	/**
4370
	 *	Delete all extra fields values for the current object.
4371
	 *
4372
	 *  @return	int		<0 if KO, >0 if OK
4373
	 */
4374
	function deleteExtraFields()
4375
	{
4376
		$this->db->begin();
4377
4378
		$table_element = $this->table_element;
4379
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4380
4381
		$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
4382
		dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
4383
		$resql=$this->db->query($sql_del);
4384
		if (! $resql)
4385
		{
4386
			$this->error=$this->db->lasterror();
4387
			$this->db->rollback();
4388
			return -1;
4389
		}
4390
		else
4391
		{
4392
			$this->db->commit();
4393
			return 1;
4394
		}
4395
	}
4396
4397
	/**
4398
	 *	Add/Update all extra fields values for the current object.
4399
	 *  Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
4400
	 *  This function delete record with all extrafields and insert them again from the array $this->array_options.
4401
	 *
4402
	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
4403
	 *  @param	User		$userused		Object user
4404
	 *  @return int 						-1=error, O=did nothing, 1=OK
4405
	 */
4406
	function insertExtraFields($trigger='',$userused=null)
4407
	{
4408
		global $conf,$langs,$user;
4409
4410
		if (empty($userused)) $userused=$user;
4411
4412
		$error=0;
4413
4414
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
4415
4416
		if (! empty($this->array_options))
4417
		{
4418
			// Check parameters
4419
			$langs->load('admin');
4420
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4421
			$extrafields = new ExtraFields($this->db);
4422
			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
4423
4424
			//Eliminate copied source object extra_fields that do not exist in target object
4425
			$new_array_options=array();
4426
			foreach ($this->array_options as $key => $value) {
4427
				if (in_array(substr($key,8), array_keys($target_extrafields)))
4428
					$new_array_options[$key] = $value;
4429
			}
4430
4431
			foreach($new_array_options as $key => $value)
4432
			{
4433
			   	$attributeKey = substr($key,8);   // Remove 'options_' prefix
4434
			   	$attributeType  = $extrafields->attribute_type[$attributeKey];
4435
			   	$attributeLabel = $extrafields->attribute_label[$attributeKey];
4436
			   	$attributeParam = $extrafields->attribute_param[$attributeKey];
4437
			   	switch ($attributeType)
4438
			   	{
4439
			   		case 'int':
4440
			  			if (!is_numeric($value) && $value!='')
4441
			   			{
4442
			   				$this->errors[]=$langs->trans("ExtraFieldHasWrongValue",$attributeLabel);
4443
			   				return -1;
4444
			  			}
4445
			   			elseif ($value=='')
4446
			   			{
4447
			   				$new_array_options[$key] = null;
4448
			   			}
4449
			 			break;
4450
			 		/*case 'select':	// Not required, we chosed value='0' for undefined values
4451
             			if ($value=='-1')
4452
             			{
4453
             				$this->array_options[$key] = null;
4454
             			}
4455
             			break;*/
4456
					case 'price':
4457
						$new_array_options[$key] = price2num($this->array_options[$key]);
4458
						break;
4459
					case 'date':
4460
						$new_array_options[$key] = $this->db->idate($this->array_options[$key]);
4461
						break;
4462
					case 'datetime':
4463
						$new_array_options[$key] = $this->db->idate($this->array_options[$key]);
4464
						break;
4465
		   			case 'link':
4466
						$param_list=array_keys($attributeParam ['options']);
4467
						// 0 : ObjectName
4468
						// 1 : classPath
4469
						$InfoFieldList = explode(":", $param_list[0]);
4470
						dol_include_once($InfoFieldList[1]);
4471
						if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
4472
						{
4473
							$object = new $InfoFieldList[0]($this->db);
4474
							if ($value)
4475
							{
4476
								if (is_numeric($value)) $res=$object->fetch($value);
4477
								else $res=$object->fetch('',$value);
4478
4479
								if ($res > 0) $new_array_options[$key]=$object->id;
4480
								else
4481
								{
4482
									$this->error="Ref '".$value."' for object '".$object->element."' not found";
4483
									$this->db->rollback();
4484
									return -1;
4485
								}
4486
							}
4487
						}
4488
						else
4489
						{
4490
							dol_syslog('Error bad setup of extrafield', LOG_WARNING);
4491
						}
4492
						break;
4493
			   	}
4494
			}
4495
			$this->db->begin();
4496
4497
			$table_element = $this->table_element;
4498
			if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4499
4500
			$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
4501
			dol_syslog(get_class($this)."::insertExtraFields delete", LOG_DEBUG);
4502
			$this->db->query($sql_del);
4503
4504
			$sql = "INSERT INTO ".MAIN_DB_PREFIX.$table_element."_extrafields (fk_object";
4505
			foreach($new_array_options as $key => $value)
4506
			{
4507
				$attributeKey = substr($key,8);   // Remove 'options_' prefix
4508
				// Add field of attribut
4509
				if ($extrafields->attribute_type[$attributeKey] != 'separate') // Only for other type of separate
4510
					$sql.=",".$attributeKey;
4511
			}
4512
			$sql .= ") VALUES (".$this->id;
4513
			foreach($new_array_options as $key => $value)
4514
			{
4515
				$attributeKey = substr($key,8);   // Remove 'options_' prefix
4516
				// Add field o fattribut
4517
				if($extrafields->attribute_type[$attributeKey] != 'separate') // Only for other type of separate)
4518
				{
4519
					if ($new_array_options[$key] != '')
4520
					{
4521
						$sql.=",'".$this->db->escape($new_array_options[$key])."'";
4522
					}
4523
					else
4524
					{
4525
						$sql.=",null";
4526
					}
4527
				}
4528
			}
4529
			$sql.=")";
4530
4531
			dol_syslog(get_class($this)."::insertExtraFields insert", LOG_DEBUG);
4532
			$resql = $this->db->query($sql);
4533
			if (! $resql)
4534
			{
4535
				$this->error=$this->db->lasterror();
4536
				$error++;
4537
			}
4538
			else
4539
			{
4540
				if ($trigger)
4541
				{
4542
					// Call trigger
4543
					$this->context=array('extrafieldaddupdate'=>1);
1 ignored issue
show
Documentation Bug introduced by
It seems like array('extrafieldaddupdate' => 1) of type array<string,integer,{"e...daddupdate":"integer"}> is incompatible with the declared type array<integer,string> of property $context.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
4544
					$result=$this->call_trigger($trigger, $userused);
4545
					if ($result < 0) $error++;
4546
					// End call trigger
4547
				}
4548
			}
4549
4550
			if ($error)
4551
			{
4552
				$this->db->rollback();
4553
				return -1;
4554
			}
4555
			else
4556
			{
4557
				$this->db->commit();
4558
				return 1;
4559
			}
4560
		}
4561
		else return 0;
4562
	}
4563
4564
	/**
4565
	 *	Update an exta field value for the current object.
4566
	 *  Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
4567
	 *  This function delte record with all extrafields and insert them again from the array $this->array_options.
4568
	 *
4569
	 *  @param  string      $key    Key of the extrafield
4570
	 *  @return int                 -1=error, O=did nothing, 1=OK
4571
	 */
4572
	function updateExtraField($key)
4573
	{
4574
		global $conf,$langs;
4575
4576
		$error=0;
4577
4578
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
4579
4580
		if (! empty($this->array_options) && isset($this->array_options["options_".$key]))
4581
		{
4582
			// Check parameters
4583
			$langs->load('admin');
4584
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4585
			$extrafields = new ExtraFields($this->db);
4586
			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
4587
4588
			$value=$this->array_options["options_".$key];
4589
			$attributeType  = $extrafields->attribute_type[$key];
4590
			$attributeLabel = $extrafields->attribute_label[$key];
4591
			$attributeParam = $extrafields->attribute_param[$key];
4592
			switch ($attributeType)
4593
			{
4594
				case 'int':
4595
					if (!is_numeric($value) && $value!='')
4596
					{
4597
						$this->errors[]=$langs->trans("ExtraFieldHasWrongValue",$attributeLabel);
4598
						return -1;
4599
					}
4600
					elseif ($value=='')
4601
					{
4602
						$this->array_options["options_".$key] = null;
4603
					}
4604
					break;
4605
			 	/*case 'select':	// Not required, we chosed value='0' for undefined values
4606
             		if ($value=='-1')
4607
             		{
4608
             			$this->array_options[$key] = null;
4609
             		}
4610
             		break;*/
4611
				case 'price':
4612
					$this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
4613
					break;
4614
				case 'date':
4615
					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
4616
					break;
4617
				case 'datetime':
4618
					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
4619
					break;
4620
				case 'link':
4621
					$param_list=array_keys($attributeParam ['options']);
4622
					// 0 : ObjectName
4623
					// 1 : classPath
4624
					$InfoFieldList = explode(":", $param_list[0]);
4625
					dol_include_once($InfoFieldList[1]);
4626
					$object = new $InfoFieldList[0]($this->db);
4627
					if ($value)
4628
					{
4629
						$object->fetch(0,$value);
4630
						$this->array_options["options_".$key]=$object->id;
4631
					}
4632
					break;
4633
			}
4634
4635
			$this->db->begin();
4636
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key."='".$this->db->escape($this->array_options["options_".$key])."'";
4637
			$sql .= " WHERE fk_object = ".$this->id;
4638
			$resql = $this->db->query($sql);
4639
			if (! $resql)
4640
			{
4641
				$this->error=$this->db->lasterror();
4642
				$this->db->rollback();
4643
				return -1;
4644
			}
4645
			else
4646
			{
4647
				$this->db->commit();
4648
				return 1;
4649
			}
4650
		}
4651
		else return 0;
4652
	}
4653
4654
4655
	/**
4656
	 * Return HTML string to put an input field into a page
4657
	 * Code very similar with showInputField of extra fields
4658
	 *
4659
	 * @param  array   $val		       Array of properties for field to show
4660
	 * @param  string  $key            Key of attribute
4661
	 * @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)
4662
	 * @param  string  $moreparam      To add more parametes on html input tag
4663
	 * @param  string  $keysuffix      Prefix string to add into name and id of field (can be used to avoid duplicate names)
4664
	 * @param  string  $keyprefix      Suffix string to add into name and id of field (can be used to avoid duplicate names)
4665
	 * @param  mixed   $showsize       Value for css to define size. May also be a numeric.
4666
	 * @param  int     $objectid       Current object id
4667
	 * @return string
4668
	 */
4669
	function showInputField($val, $key, $value, $moreparam='', $keysuffix='', $keyprefix='', $showsize=0, $objectid=0)
4670
	{
4671
		global $conf,$langs;
4672
4673
		$label=$val['label'];
4674
		$type =$val['type'];
4675
		$size =$val['css'];
4676
4677
		// Convert var to be able to share same code than showInputField of extrafields
4678
		if (preg_match('/varchar/', $type)) $type = 'varchar';		// convert varchar(xx) int varchar
4679
		if (is_array($val['arrayofkeyval'])) $type='select';
4680
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) $type='link';
4681
4682
		//$elementtype=$this->attribute_elementtype[$key];	// seems to not be used
4683
		$default=$val['default'];
4684
		$computed=$val['computed'];
4685
		$unique=$val['unique'];
4686
		$required=$val['required'];
4687
		$param=$val['param'];
4688
		if (is_array($val['arrayofkeyval'])) $param['options'] = $val['arrayofkeyval'];
4689
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg))
4690
		{
4691
			$type='link';
4692
			$param['options']=array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
4693
		}
4694
		$langfile=$val['langfile'];
4695
		$list=$val['list'];
4696
		$hidden=(abs($val['visible'])!=1 ? 1 : 0);
4697
		$help=$val['help'];
4698
4699
4700
		if ($computed)
4701
		{
4702
			if (! preg_match('/^search_/', $keyprefix)) return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
4703
			else return '';
4704
		}
4705
4706
		if (empty($showsize))
4707
		{
4708
			if ($type == 'date')
4709
			{
4710
				//$showsize=10;
4711
				$showsize = 'minwidth100imp';
4712
			}
4713
			elseif ($type == 'datetime')
4714
			{
4715
				//$showsize=19;
4716
				$showsize = 'minwidth200imp';
4717
			}
4718
			elseif (in_array($type,array('int','double','price')))
4719
			{
4720
				//$showsize=10;
4721
				$showsize = 'maxwidth75';
4722
			}
4723
			elseif ($type == 'url')
4724
			{
4725
				$showsize='minwidth400';
4726
			}
4727
			elseif ($type == 'boolean')
4728
			{
4729
				$showsize='';
4730
			}
4731
			else
4732
			{
4733
				if (round($size) < 12)
4734
				{
4735
					$showsize = 'minwidth100';
4736
				}
4737
				else if (round($size) <= 48)
4738
				{
4739
					$showsize = 'minwidth200';
4740
				}
4741
				else
4742
				{
4743
					//$showsize=48;
4744
					$showsize = 'minwidth400';
4745
				}
4746
			}
4747
		}
4748
4749
		if (in_array($type,array('date','datetime')))
4750
		{
4751
			$tmp=explode(',',$size);
4752
			$newsize=$tmp[0];
4753
4754
			$showtime = in_array($type,array('datetime')) ? 1 : 0;
4755
4756
			// Do not show current date when field not required (see select_date() method)
4757
			if (!$required && $value == '') $value = '-1';
4758
4759
			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
4760
			global $form;
4761
			if (! is_object($form)) $form=new Form($this->db);
4762
4763
			// TODO Must also support $moreparam
4764
			$out = $form->select_date($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, ($keyprefix != 'search_' ? 1 : 0), 1, 0, 1);
4765
		}
4766
		elseif (in_array($type,array('int','integer')))
4767
		{
4768
			$tmp=explode(',',$size);
4769
			$newsize=$tmp[0];
4770
			$out='<input type="text" class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$newsize.'" value="'.$value.'"'.($moreparam?$moreparam:'').'>';
4771
		}
4772
		elseif (preg_match('/varchar/', $type))
4773
		{
4774
			$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:'').'>';
4775
		}
4776
		elseif (in_array($type, array('mail', 'phone', 'url')))
4777
		{
4778
			$out='<input type="text" class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'>';
4779
		}
4780
		elseif ($type == 'text')
4781
		{
4782
			require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
4783
			$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%');
4784
			$out=$doleditor->Create(1);
4785
		}
4786
		elseif ($type == 'boolean')
4787
		{
4788
			$checked='';
4789
			if (!empty($value)) {
4790
				$checked=' checked value="1" ';
4791
			} else {
4792
				$checked=' value="1" ';
4793
			}
4794
			$out='<input type="checkbox" class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam?$moreparam:'').'>';
4795
		}
4796
		elseif ($type == 'price')
4797
		{
4798
			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
4799
				$value=price($value);
4800
			}
4801
			$out='<input type="text" class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> '.$langs->getCurrencySymbol($conf->currency);
4802
		}
4803
		elseif ($type == 'double')
4804
		{
4805
			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
4806
				$value=price($value);
4807
			}
4808
			$out='<input type="text" class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> ';
4809
		}
4810
		elseif ($type == 'select')
4811
		{
4812
			$out = '';
4813
			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
4814
			{
4815
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
4816
				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
4817
			}
4818
4819
			$out.='<select class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
4820
			$out.='<option value="0">&nbsp;</option>';
4821
			foreach ($param['options'] as $key => $val)
4822
			{
4823
				if ((string) $key == '') continue;
4824
				list($val, $parent) = explode('|', $val);
4825
				$out.='<option value="'.$key.'"';
4826
				$out.= (((string) $value == (string) $key)?' selected':'');
4827
				$out.= (!empty($parent)?' parent="'.$parent.'"':'');
4828
				$out.='>'.$val.'</option>';
4829
			}
4830
			$out.='</select>';
4831
		}
4832
		elseif ($type == 'sellist')
4833
		{
4834
			$out = '';
4835
			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
4836
			{
4837
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
4838
				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
4839
			}
4840
4841
			$out.='<select class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
4842
			if (is_array($param['options']))
4843
			{
4844
				$param_list=array_keys($param['options']);
4845
				$InfoFieldList = explode(":", $param_list[0]);
4846
				// 0 : tableName
4847
				// 1 : label field name
4848
				// 2 : key fields name (if differ of rowid)
4849
				// 3 : key field parent (for dependent lists)
4850
				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
4851
				$keyList=(empty($InfoFieldList[2])?'rowid':$InfoFieldList[2].' as rowid');
4852
4853
4854
				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4]))
4855
				{
4856
					if (strpos($InfoFieldList[4], 'extra.') !== false)
4857
					{
4858
						$keyList='main.'.$InfoFieldList[2].' as rowid';
4859
					} else {
4860
						$keyList=$InfoFieldList[2].' as rowid';
4861
					}
4862
				}
4863
				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3]))
4864
				{
4865
					list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
4866
					$keyList.= ', '.$parentField;
4867
				}
4868
4869
				$fields_label = explode('|',$InfoFieldList[1]);
4870
				if (is_array($fields_label))
4871
				{
4872
					$keyList .=', ';
4873
					$keyList .= implode(', ', $fields_label);
4874
				}
4875
4876
				$sqlwhere='';
4877
				$sql = 'SELECT '.$keyList;
4878
				$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
4879
				if (!empty($InfoFieldList[4]))
4880
				{
4881
					// can use SELECT request
4882
					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
4883
						$InfoFieldList[4]=str_replace('$SEL$','SELECT',$InfoFieldList[4]);
4884
					}
4885
4886
					// current object id can be use into filter
4887
					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
4888
						$InfoFieldList[4]=str_replace('$ID$',$objectid,$InfoFieldList[4]);
4889
					} else {
4890
						$InfoFieldList[4]=str_replace('$ID$','0',$InfoFieldList[4]);
4891
					}
4892
					//We have to join on extrafield table
4893
					if (strpos($InfoFieldList[4], 'extra')!==false)
4894
					{
4895
						$sql.= ' as main, '.MAIN_DB_PREFIX .$InfoFieldList[0].'_extrafields as extra';
4896
						$sqlwhere.= ' WHERE extra.fk_object=main.'.$InfoFieldList[2]. ' AND '.$InfoFieldList[4];
4897
					}
4898
					else
4899
					{
4900
						$sqlwhere.= ' WHERE '.$InfoFieldList[4];
4901
					}
4902
				}
4903
				else
4904
				{
4905
					$sqlwhere.= ' WHERE 1=1';
4906
				}
4907
				// Some tables may have field, some other not. For the moment we disable it.
4908
				if (in_array($InfoFieldList[0],array('tablewithentity')))
4909
				{
4910
					$sqlwhere.= ' AND entity = '.$conf->entity;
4911
				}
4912
				$sql.=$sqlwhere;
4913
				//print $sql;
4914
4915
				$sql .= ' ORDER BY ' . implode(', ', $fields_label);
4916
4917
				dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
4918
				$resql = $this->db->query($sql);
4919
				if ($resql)
4920
				{
4921
					$out.='<option value="0">&nbsp;</option>';
4922
					$num = $this->db->num_rows($resql);
4923
					$i = 0;
4924
					while ($i < $num)
4925
					{
4926
						$labeltoshow='';
4927
						$obj = $this->db->fetch_object($resql);
4928
4929
						// Several field into label (eq table:code|libelle:rowid)
4930
						$fields_label = explode('|',$InfoFieldList[1]);
4931
						if(is_array($fields_label))
4932
						{
4933
							$notrans = true;
4934
							foreach ($fields_label as $field_toshow)
4935
							{
4936
								$labeltoshow.= $obj->$field_toshow.' ';
4937
							}
4938
						}
4939
						else
4940
						{
4941
							$labeltoshow=$obj->{$InfoFieldList[1]};
4942
						}
4943
						$labeltoshow=dol_trunc($labeltoshow,45);
4944
4945
						if ($value==$obj->rowid)
4946
						{
4947
							foreach ($fields_label as $field_toshow)
4948
							{
4949
								$translabel=$langs->trans($obj->$field_toshow);
4950
								if ($translabel!=$obj->$field_toshow) {
4951
									$labeltoshow=dol_trunc($translabel,18).' ';
4952
								}else {
4953
									$labeltoshow=dol_trunc($obj->$field_toshow,18).' ';
4954
								}
4955
							}
4956
							$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
4957
						}
4958
						else
4959
						{
4960
							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...
4961
							{
4962
								$translabel=$langs->trans($obj->{$InfoFieldList[1]});
4963
								if ($translabel!=$obj->{$InfoFieldList[1]}) {
4964
									$labeltoshow=dol_trunc($translabel,18);
4965
								}
4966
								else {
4967
									$labeltoshow=dol_trunc($obj->{$InfoFieldList[1]},18);
4968
								}
4969
							}
4970
							if (empty($labeltoshow)) $labeltoshow='(not defined)';
4971
							if ($value==$obj->rowid)
4972
							{
4973
								$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
4974
							}
4975
4976
							if (!empty($InfoFieldList[3]))
4977
							{
4978
								$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...
4979
							}
4980
4981
							$out.='<option value="'.$obj->rowid.'"';
4982
							$out.= ($value==$obj->rowid?' selected':'');
4983
							$out.= (!empty($parent)?' parent="'.$parent.'"':'');
4984
							$out.='>'.$labeltoshow.'</option>';
4985
						}
4986
4987
						$i++;
4988
					}
4989
					$this->db->free($resql);
4990
				}
4991
				else {
4992
					print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
4993
				}
4994
			}
4995
			$out.='</select>';
4996
		}
4997
		elseif ($type == 'checkbox')
4998
		{
4999
			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5000
			$form = new Form($db);
0 ignored issues
show
Bug introduced by
The variable $db 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...
5001
5002
			$value_arr=explode(',',$value);
5003
			$out=$form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options'])?null:$param['options']), $value_arr, '', 0, '', 0, '100%');
5004
5005
		}
5006
		elseif ($type == 'radio')
5007
		{
5008
			$out='';
5009
			foreach ($param['options'] as $keyopt => $val)
5010
			{
5011
				$out.='<input class="flat '.$showsize.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'');
5012
				$out.=' value="'.$keyopt.'"';
5013
				$out.=' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
5014
				$out.= ($value==$keyopt?'checked':'');
5015
				$out.='/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$val.'</label><br>';
5016
			}
5017
		}
5018
		elseif ($type == 'chkbxlst')
5019
		{
5020
			if (is_array($value)) {
5021
				$value_arr = $value;
5022
			}
5023
			else {
5024
				$value_arr = explode(',', $value);
5025
			}
5026
5027
			if (is_array($param['options'])) {
5028
				$param_list = array_keys($param['options']);
5029
				$InfoFieldList = explode(":", $param_list[0]);
5030
				// 0 : tableName
5031
				// 1 : label field name
5032
				// 2 : key fields name (if differ of rowid)
5033
				// 3 : key field parent (for dependent lists)
5034
				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5035
				$keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
5036
5037
				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3])) {
5038
					list ( $parentName, $parentField ) = explode('|', $InfoFieldList[3]);
5039
					$keyList .= ', ' . $parentField;
5040
				}
5041
				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4])) {
5042
					if (strpos($InfoFieldList[4], 'extra.') !== false) {
5043
						$keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
5044
					} else {
5045
						$keyList = $InfoFieldList[2] . ' as rowid';
5046
					}
5047
				}
5048
5049
				$fields_label = explode('|', $InfoFieldList[1]);
5050
				if (is_array($fields_label)) {
5051
					$keyList .= ', ';
5052
					$keyList .= implode(', ', $fields_label);
5053
				}
5054
5055
				$sqlwhere = '';
5056
				$sql = 'SELECT ' . $keyList;
5057
				$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
5058
				if (! empty($InfoFieldList[4])) {
5059
5060
					// can use SELECT request
5061
					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5062
						$InfoFieldList[4]=str_replace('$SEL$','SELECT',$InfoFieldList[4]);
5063
					}
5064
5065
					// current object id can be use into filter
5066
					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5067
						$InfoFieldList[4]=str_replace('$ID$',$objectid,$InfoFieldList[4]);
5068
					} else {
5069
						$InfoFieldList[4]=str_replace('$ID$','0',$InfoFieldList[4]);
5070
					}
5071
5072
					// We have to join on extrafield table
5073
					if (strpos($InfoFieldList[4], 'extra') !== false) {
5074
						$sql .= ' as main, ' . MAIN_DB_PREFIX . $InfoFieldList[0] . '_extrafields as extra';
5075
						$sqlwhere .= ' WHERE extra.fk_object=main.' . $InfoFieldList[2] . ' AND ' . $InfoFieldList[4];
5076
					} else {
5077
						$sqlwhere .= ' WHERE ' . $InfoFieldList[4];
5078
					}
5079
				} else {
5080
					$sqlwhere .= ' WHERE 1=1';
5081
				}
5082
				// Some tables may have field, some other not. For the moment we disable it.
5083
				if (in_array($InfoFieldList[0], array ('tablewithentity')))
5084
				{
5085
					$sqlwhere .= ' AND entity = ' . $conf->entity;
5086
				}
5087
				// $sql.=preg_replace('/^ AND /','',$sqlwhere);
5088
				// print $sql;
5089
5090
				$sql .= $sqlwhere;
5091
				dol_syslog(get_class($this) . '::showInputField type=chkbxlst',LOG_DEBUG);
5092
				$resql = $this->db->query($sql);
5093
				if ($resql) {
5094
					$num = $this->db->num_rows($resql);
5095
					$i = 0;
5096
5097
					$data=array();
5098
5099
					while ( $i < $num ) {
5100
						$labeltoshow = '';
5101
						$obj = $this->db->fetch_object($resql);
5102
5103
						// Several field into label (eq table:code|libelle:rowid)
5104
						$fields_label = explode('|', $InfoFieldList[1]);
5105
						if (is_array($fields_label)) {
5106
							$notrans = true;
5107
							foreach ( $fields_label as $field_toshow ) {
5108
								$labeltoshow .= $obj->$field_toshow . ' ';
5109
							}
5110
						} else {
5111
							$labeltoshow = $obj->{$InfoFieldList[1]};
5112
						}
5113
						$labeltoshow = dol_trunc($labeltoshow, 45);
5114
5115
						if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5116
							foreach ( $fields_label as $field_toshow ) {
5117
								$translabel = $langs->trans($obj->$field_toshow);
5118
								if ($translabel != $obj->$field_toshow) {
5119
									$labeltoshow = dol_trunc($translabel, 18) . ' ';
5120
								} else {
5121
									$labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
5122
								}
5123
							}
5124
5125
							$data[$obj->rowid]=$labeltoshow;
5126
5127
						} else {
5128
							if (! $notrans) {
5129
								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
5130
								if ($translabel != $obj->{$InfoFieldList[1]}) {
5131
									$labeltoshow = dol_trunc($translabel, 18);
5132
								} else {
5133
									$labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
5134
								}
5135
							}
5136
							if (empty($labeltoshow))
5137
								$labeltoshow = '(not defined)';
5138
5139
								if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5140
									$data[$obj->rowid]=$labeltoshow;
5141
								}
5142
5143
								if (! empty($InfoFieldList[3])) {
5144
									$parent = $parentName . ':' . $obj->{$parentField};
5145
								}
5146
5147
								$data[$obj->rowid]=$labeltoshow;
5148
						}
5149
5150
						$i ++;
5151
					}
5152
					$this->db->free($resql);
5153
5154
					require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5155
					$form = new Form($db);
5156
5157
					$out=$form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
5158
5159
				} else {
5160
					print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
5161
				}
5162
			}
5163
			$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...
5164
		}
5165
		elseif ($type == 'link')
5166
		{
5167
			$out='';
5168
5169
			$param_list=array_keys($param['options']);
5170
			// 0 : ObjectName
5171
			// 1 : classPath
5172
			$InfoFieldList = explode(":", $param_list[0]);
5173
			dol_include_once($InfoFieldList[1]);
5174
			if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
5175
			{
5176
				$valuetoshow=$value;
5177
				if (!empty($value))
5178
				{
5179
					$object = new $InfoFieldList[0]($this->db);
5180
					$resfetch=$object->fetch($value);
5181
					if ($resfetch > 0)
5182
					{
5183
						$valuetoshow=$object->ref;
5184
						if ($object->element == 'societe') $valuetoshow=$object->name;  // Special case for thirdparty because ->ref is not name but id (because name is not unique)
5185
					}
5186
				}
5187
				$out.='<input type="text" class="flat '.$showsize.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$valuetoshow.'" >';
5188
			}
5189
			else
5190
			{
5191
				dol_syslog('Error bad setup of type for field '.$InfoFieldList, LOG_WARNING);
5192
				$out.='Error bad setup of type for field '.join(',', $InfoFieldList);
5193
			}
5194
		}
5195
		elseif ($type == 'password')
5196
		{
5197
			// If prefix is 'search_', field is used as a filter, we use a common text field.
5198
			$out='<input type="'.($keyprefix=='search_'?'text':'password').'" class="flat '.$showsize.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'>';
5199
		}
5200
		if (!empty($hidden)) {
5201
			$out='<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
5202
		}
5203
		/* Add comments
5204
		 if ($type == 'date') $out.=' (YYYY-MM-DD)';
5205
		 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
5206
		 */
5207
		return $out;
5208
	}
5209
5210
5211
5212
	/**
5213
	 * Function to show lines of extrafields with output datas
5214
	 *
5215
	 * @param Extrafields   $extrafields    Extrafield Object
5216
	 * @param string        $mode           Show output (view) or input (edit) for extrafield
5217
	 * @param array         $params         Optional parameters
5218
	 * @param string        $keysuffix      Suffix string to add after name and id of field (can be used to avoid duplicate names)
5219
	 * @param string        $keyprefix      Prefix string to add before name and id of field (can be used to avoid duplicate names)
5220
	 *
5221
	 * @return string
5222
	 */
5223
	function showOptionals($extrafields, $mode='view', $params=null, $keysuffix='', $keyprefix='')
5224
	{
5225
		global $_POST, $conf, $langs, $action;
5226
5227
		$out = '';
5228
5229
		if (count($extrafields->attribute_label) > 0)
5230
		{
5231
			$out .= "\n";
5232
			$out .= '<!-- showOptionalsInput --> ';
5233
			$out .= "\n";
5234
5235
			$e = 0;
5236
			foreach($extrafields->attribute_label as $key=>$label)
5237
			{
5238
				if (empty($extrafields->attribute_list[$key])) continue;												// 0 = Never visible field
5239
				if (($mode == 'create' || $mode == 'edit') && abs($extrafields->attribute_list[$key]) != 1) continue;	// <> -1 and <> 1 = not visible on forms, only on list
5240
5241
				// Load language if required
5242
				if (! empty($extrafields->attributes[$this->table_element]['langfile'][$key])) $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
5243
5244
				if (is_array($params) && count($params)>0) {
5245
					if (array_key_exists('colspan',$params)) {
5246
						$colspan=$params['colspan'];
5247
					}
5248
				}else {
5249
					$colspan='3';
5250
				}
5251
5252
				switch($mode) {
5253
					case "view":
5254
						$value=$this->array_options["options_".$key.$keysuffix];
5255
						break;
5256
					case "edit":
5257
						$getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, 'none');				// GETPOST can get value from GET, POST or setup of default values.
5258
						// GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
5259
						if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix))
5260
						{
5261
							if (is_array($getposttemp)) {
5262
								// $getposttemp is an array but following code expects a comma separated string
5263
								$value = implode(",", $getposttemp);
5264
							} else {
5265
								$value = $getposttemp;
5266
							}
5267
						} else {
5268
							$value = $this->array_options["options_" . $key];			// No GET, no POST, no default value, so we take value of object.
5269
						}
5270
						break;
5271
				}
5272
				//var_dump($value);
5273
5274
				if ($extrafields->attribute_type[$key] == 'separate')
5275
				{
5276
					$out .= $extrafields->showSeparator($key);
5277
				}
5278
				else
5279
				{
5280
					$csstyle='';
5281
					$class=(!empty($extrafields->attribute_hidden[$key]) ? 'class="hideobject" ' : '');
5282
					if (is_array($params) && count($params)>0) {
5283
						if (array_key_exists('style',$params)) {
5284
							$csstyle=$params['style'];
5285
						}
5286
					}
5287
					if ( !empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0)
5288
					{
5289
						$out .= '<tr '.$class.$csstyle.' class="'.$this->element.'_extras_'.$key.'">';
5290
						$colspan='0';
5291
					}
5292
					else
5293
					{
5294
						$out .= '<tr '.$class.$csstyle.' class="'.$this->element.'_extras_'.$key.'">';
5295
					}
5296
					// Convert date into timestamp format (value in memory must be a timestamp)
5297
					if (in_array($extrafields->attribute_type[$key],array('date','datetime')))
5298
					{
5299
						$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]);
5300
					}
5301
					// Convert float submited string into real php numeric (value in memory must be a php numeric)
5302
					if (in_array($extrafields->attribute_type[$key],array('price','double')))
5303
					{
5304
						$value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?price2num(GETPOST($keyprefix.'options_'.$key.$keysuffix,'int',3)):$this->array_options['options_'.$key];
5305
					}
5306
5307
					$labeltoshow = $langs->trans($label);
5308
5309
					if($extrafields->attribute_required[$key])
5310
					{
5311
						$labeltoshow = '<span'.($mode != 'view' ? ' class="fieldrequired"':'').'>'.$labeltoshow.'</span>';
5312
					}
5313
					$out .= '<td>'.$labeltoshow.'</td>';
5314
5315
					$html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
5316
					$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...
5317
5318
					switch($mode) {
5319
						case "view":
5320
							$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...
5321
							break;
5322
						case "edit":
5323
							$out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id);
5324
							break;
5325
					}
5326
5327
					$out .= '</td>';
5328
5329
					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>';
5330
					else $out .= '</tr>';
5331
					$e++;
5332
				}
5333
			}
5334
			$out .= "\n";
5335
			// Add code to manage list depending on others
5336
			if (! empty($conf->use_javascript_ajax)) {
5337
				$out .= '
5338
				<script type="text/javascript">
5339
				    jQuery(document).ready(function() {
5340
				    	function showOptions(child_list, parent_list)
5341
				    	{
5342
				    		var val = $("select[name=\"options_"+parent_list+"\"]").val();
5343
				    		var parentVal = parent_list + ":" + val;
5344
							if(val > 0) {
5345
					    		$("select[name=\""+child_list+"\"] option[parent]").hide();
5346
					    		$("select[name=\""+child_list+"\"] option[parent=\""+parentVal+"\"]").show();
5347
							} else {
5348
								$("select[name=\""+child_list+"\"] option").show();
5349
							}
5350
				    	}
5351
						function setListDependencies() {
5352
					    	jQuery("select option[parent]").parent().each(function() {
5353
					    		var child_list = $(this).attr("name");
5354
								var parent = $(this).find("option[parent]:first").attr("parent");
5355
								var infos = parent.split(":");
5356
								var parent_list = infos[0];
5357
								$("select[name=\""+parent_list+"\"]").change(function() {
5358
									showOptions(child_list, parent_list);
5359
								});
5360
					    	});
5361
						}
5362
5363
						setListDependencies();
5364
				    });
5365
				</script>'."\n";
5366
				$out .= '<!-- /showOptionalsInput --> '."\n";
5367
			}
5368
		}
5369
		return $out;
5370
	}
5371
5372
5373
	/**
5374
	 * Returns the rights used for this class
5375
	 * @return stdClass
5376
	 */
5377
	public function getRights()
5378
	{
5379
		global $user;
5380
5381
		$element = $this->element;
5382
		if ($element == 'facturerec') $element='facture';
5383
5384
		return $user->rights->{$element};
5385
	}
5386
5387
	/**
5388
	 * Function used to replace a thirdparty id with another one.
5389
	 * This function is meant to be called from replaceThirdparty with the appropiate tables
5390
	 * Column name fk_soc MUST be used to identify thirdparties
5391
	 *
5392
	 * @param  DoliDB 	   $db 			  Database handler
5393
	 * @param  int 		   $origin_id     Old thirdparty id (the thirdparty to delete)
5394
	 * @param  int 		   $dest_id       New thirdparty id (the thirdparty that will received element of the other)
5395
	 * @param  string[]    $tables        Tables that need to be changed
5396
	 * @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)
5397
	 * @return bool
5398
	 */
5399
	public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
5400
	{
5401
		foreach ($tables as $table)
5402
		{
5403
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id;
5404
5405
			if (! $db->query($sql))
5406
			{
5407
				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.
5408
				//$this->errors = $db->lasterror();
5409
				return false;
5410
			}
5411
		}
5412
5413
		return true;
5414
	}
5415
5416
	/**
5417
	 * Get buy price to use for margin calculation. This function is called when buy price is unknown.
5418
	 *	 Set buy price = sell price if ForceBuyingPriceIfNull configured,
5419
	 *   else if calculation MARGIN_TYPE = 'costprice' and costprice is defined, use costprice as buyprice
5420
	 *	 else if calculation MARGIN_TYPE = 'pmp' and pmp is calculated, use pmp as buyprice
5421
	 *	 else set min buy price as buy price
5422
	 *
5423
	 * @param float		$unitPrice		 Product unit price
5424
	 * @param float		$discountPercent Line discount percent
5425
	 * @param int		$fk_product		 Product id
5426
	 * @return	float                    <0 if KO, buyprice if OK
5427
	 */
5428
	public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
5429
	{
5430
		global $conf;
5431
5432
		$buyPrice = 0;
5433
5434
		if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
5435
		{
5436
			$buyPrice = $unitPrice * (1 - $discountPercent / 100);
5437
		}
5438
		else
5439
		{
5440
			// Get cost price for margin calculation
5441
			if (! empty($fk_product))
5442
			{
5443
				if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice')
5444
				{
5445
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
5446
					$product = new Product($this->db);
5447
					$result = $product->fetch($fk_product);
5448
					if ($result <= 0)
5449
					{
5450
						$this->errors[] = 'ErrorProductIdDoesNotExists';
5451
						return -1;
5452
					}
5453
					if ($product->cost_price > 0)
5454
					{
5455
						$buyPrice = $product->cost_price;
5456
					}
5457
					else if ($product->pmp > 0)
5458
					{
5459
						$buyPrice = $product->pmp;
5460
					}
5461
				}
5462
				else if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')
5463
				{
5464
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
5465
					$product = new Product($this->db);
5466
					$result = $product->fetch($fk_product);
5467
					if ($result <= 0)
5468
					{
5469
						$this->errors[] = 'ErrorProductIdDoesNotExists';
5470
						return -1;
5471
					}
5472
					if ($product->pmp > 0)
5473
					{
5474
						$buyPrice = $product->pmp;
5475
					}
5476
				}
5477
5478
				if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1','pmp','costprice')))
5479
				{
5480
					require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
5481
					$productFournisseur = new ProductFournisseur($this->db);
5482
					if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0)
5483
					{
5484
						$buyPrice = $productFournisseur->fourn_unitprice;
5485
					}
5486
					else if ($result < 0)
5487
					{
5488
						$this->errors[] = $productFournisseur->error;
5489
						return -2;
5490
					}
5491
				}
5492
			}
5493
		}
5494
		return $buyPrice;
5495
	}
5496
5497
5498
5499
	/**
5500
	 * Function test if type is array
5501
	 *
5502
	 * @param   array   $info   content informations of field
5503
	 * @return                  bool
5504
	 */
5505
	protected function isArray($info)
5506
	{
5507
		if(is_array($info))
5508
		{
5509
			if(isset($info['type']) && $info['type']=='array') return true;
5510
			else return false;
5511
		}
5512
		else return false;
5513
	}
5514
5515
	/**
5516
	 * Function test if type is null
5517
	 *
5518
	 * @param   array   $info   content informations of field
5519
	 * @return                  bool
5520
	 */
5521
	protected function isNull($info)
5522
	{
5523
		if(is_array($info))
5524
		{
5525
			if(isset($info['type']) && $info['type']=='null') return true;
5526
			else return false;
5527
		}
5528
		else return false;
5529
	}
5530
5531
5532
	/**
5533
	 * Function test if type is date
5534
	 *
5535
	 * @param   array   $info   content informations of field
5536
	 * @return                  bool
5537
	 */
5538
	public function isDate($info)
5539
	{
5540
		if(isset($info['type']) && ($info['type']=='date' || $info['type']=='datetime' || $info['type']=='timestamp')) return true;
5541
		else return false;
5542
	}
5543
5544
	/**
5545
	 * Function test if type is integer
5546
	 *
5547
	 * @param   array   $info   content informations of field
5548
	 * @return                  bool
5549
	 */
5550
	public function isInt($info)
5551
	{
5552
		if(is_array($info))
5553
		{
5554
			if(isset($info['type']) && ($info['type']=='int' || $info['type']=='integer' )) return true;
5555
			else return false;
5556
		}
5557
		else return false;
5558
	}
5559
5560
	/**
5561
	 * Function test if type is float
5562
	 *
5563
	 * @param   array   $info   content informations of field
5564
	 * @return                  bool
5565
	 */
5566
	public function isFloat($info)
5567
	{
5568
		if(is_array($info))
5569
		{
5570
			if (isset($info['type']) && (preg_match('/^(double|real)/i', $info['type']))) return true;
5571
			else return false;
5572
		}
5573
		else return false;
5574
	}
5575
5576
	/**
5577
	 * Function test if type is text
5578
	 *
5579
	 * @param   array   $info   content informations of field
5580
	 * @return                  bool
5581
	 */
5582
	public function isText($info)
5583
	{
5584
		if(is_array($info))
5585
		{
5586
			if(isset($info['type']) && $info['type']=='text') return true;
5587
			else return false;
5588
		}
5589
		else return false;
5590
	}
5591
5592
	/**
5593
	 * Function test if is indexed
5594
	 *
5595
	 * @param   array   $info   content informations of field
5596
	 * @return                  bool
5597
	 */
5598
	protected function isIndex($info)
5599
	{
5600
		if(is_array($info))
5601
		{
5602
			if(isset($info['index']) && $info['index']==true) return true;
5603
			else return false;
5604
		}
5605
		else return false;
5606
	}
5607
5608
	/**
5609
	 * Function to prepare the values to insert.
5610
	 * Note $this->${field} are set by the page that make the createCommon or the updateCommon.
5611
	 *
5612
	 * @return array
5613
	 */
5614
	private function set_save_query()
5615
	{
5616
		global $conf;
5617
5618
		$queryarray=array();
5619
		foreach ($this->fields as $field=>$info)	// Loop on definition of fields
5620
		{
5621
			// Depending on field type ('datetime', ...)
5622
			if($this->isDate($info))
5623
			{
5624
				if(empty($this->{$field}))
5625
				{
5626
					$queryarray[$field] = NULL;
5627
				}
5628
				else
5629
				{
5630
					$queryarray[$field] = $this->db->idate($this->{$field});
5631
				}
5632
			}
5633
			else if($this->isArray($info))
5634
			{
5635
				$queryarray[$field] = serialize($this->{$field});
5636
			}
5637
			else if($this->isInt($info))
5638
			{
5639
				if ($field == 'entity' && is_null($this->{$field})) $queryarray[$field]=$conf->entity;
5640
				else
5641
				{
5642
					$queryarray[$field] = (int) price2num($this->{$field});
5643
					if (empty($queryarray[$field])) $queryarray[$field]=0;		// May be reset to null later if property 'notnull' is -1 for this field.
5644
				}
5645
			}
5646
			else if($this->isFloat($info))
5647
			{
5648
				$queryarray[$field] = (double) price2num($this->{$field});
5649
				if (empty($queryarray[$field])) $queryarray[$field]=0;
5650
			}
5651
			else
5652
			{
5653
				$queryarray[$field] = $this->{$field};
5654
			}
5655
5656
			if ($info['type'] == 'timestamp' && empty($queryarray[$field])) unset($queryarray[$field]);
5657
			if (! empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) $queryarray[$field] = null;
5658
		}
5659
5660
		return $queryarray;
5661
	}
5662
5663
	/**
5664
	 * Function to load data into current object this
5665
	 *
5666
	 * @param   stdClass    $obj    Contain data of object from database
5667
	 */
5668
	private function setVarsFromFetchObj(&$obj)
5669
	{
5670
		foreach ($this->fields as $field => $info)
5671
		{
5672
			if($this->isDate($info))
5673
			{
5674
				if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
5675
				else $this->{$field} = strtotime($obj->{$field});
5676
			}
5677
			elseif($this->isArray($info))
5678
			{
5679
				$this->{$field} = @unserialize($obj->{$field});
5680
				// Hack for data not in UTF8
5681
				if($this->{$field } === FALSE) @unserialize(utf8_decode($obj->{$field}));
5682
			}
5683
			elseif($this->isInt($info))
5684
			{
5685
				if ($field == 'rowid') $this->id = (int) $obj->{$field};
5686
				else $this->{$field} = (int) $obj->{$field};
5687
			}
5688
			elseif($this->isFloat($info))
5689
			{
5690
				$this->{$field} = (double) $obj->{$field};
5691
			}
5692
			elseif($this->isNull($info))
5693
			{
5694
				$val = $obj->{$field};
5695
				// zero is not null
5696
				$this->{$field} = (is_null($val) || (empty($val) && $val!==0 && $val!=='0') ? null : $val);
5697
			}
5698
			else
5699
			{
5700
				$this->{$field} = $obj->{$field};
5701
			}
5702
5703
		}
5704
	}
5705
5706
	/**
5707
	 * Function to concat keys of fields
5708
	 *
5709
	 * @return string
5710
	 */
5711
	private function get_field_list()
5712
	{
5713
		$keys = array_keys($this->fields);
5714
		return implode(',', $keys);
5715
	}
5716
5717
	/**
5718
	 * Add quote to field value if necessary
5719
	 *
5720
	 * @param 	string|int	$value			Value to protect
5721
	 * @param	array		$fieldsentry	Properties of field
5722
	 * @return 	string
5723
	 */
5724
	protected function quote($value, $fieldsentry) {
5725
		if (is_null($value)) return 'NULL';
5726
		else if (preg_match('/^(int|double|real)/i', $fieldsentry['type'])) return $this->db->escape("$value");
5727
		else return "'".$this->db->escape($value)."'";
5728
	}
5729
5730
5731
	/**
5732
	 * Create object into database
5733
	 *
5734
	 * @param  User $user      User that creates
5735
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
5736
	 * @return int             <0 if KO, Id of created object if OK
5737
	 */
5738
	public function createCommon(User $user, $notrigger = false)
5739
	{
5740
		$error = 0;
5741
5742
		$now=dol_now();
5743
5744
		$fieldvalues = $this->set_save_query();
5745
		if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) $fieldvalues['date_creation']=$this->db->idate($now);
5746
		if (array_key_exists('fk_user_creat', $fieldvalues) && ! ($fieldvalues['fk_user_creat'] > 0)) $fieldvalues['fk_user_creat']=$user->id;
5747
		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
5748
5749
		$keys=array();
5750
		$values = array();
5751
		foreach ($fieldvalues as $k => $v) {
5752
			$keys[] = $k;
5753
			$values[] = $this->quote($v, $this->fields[$k]);
5754
		}
5755
5756
		$this->db->begin();
5757
5758
		if (! $error)
5759
		{
5760
			$sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
5761
			$sql.= ' ('.implode( ", ", $keys ).')';
5762
			$sql.= ' VALUES ('.implode( ", ", $values ).')';
5763
5764
			$res = $this->db->query($sql);
5765
			if ($res===false) {
5766
				$error++;
5767
				$this->errors[] = $this->db->lasterror();
5768
			}
5769
		}
5770
5771
		if (! $error && ! $notrigger) {
5772
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
5773
5774
			if (!$notrigger) {
5775
				// Call triggers
5776
				$result=$this->call_trigger(strtoupper(get_class($this)).'_CREATE',$user);
5777
				if ($result < 0) { $error++; }
5778
				// End call triggers
5779
			}
5780
		}
5781
5782
		// Commit or rollback
5783
		if ($error) {
5784
			$this->db->rollback();
5785
			return -1;
5786
		} else {
5787
			$this->db->commit();
5788
			return $this->id;
5789
		}
5790
	}
5791
5792
5793
	/**
5794
	 * Load object in memory from the database
5795
	 *
5796
	 * @param int    $id   Id object
5797
	 * @param string $ref  Ref
5798
	 * @return int         <0 if KO, 0 if not found, >0 if OK
5799
	 */
5800
	public function fetchCommon($id, $ref = null)
5801
	{
5802
		if (empty($id) && empty($ref)) return false;
5803
5804
		$sql = 'SELECT '.$this->get_field_list();
5805
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
5806
5807
		if(!empty($id)) $sql.= ' WHERE rowid = '.$id;
5808
		else $sql.= " WHERE ref = ".$this->quote($ref, $this->fields['ref']);
5809
5810
		$res = $this->db->query($sql);
5811
		if ($res)
5812
		{
5813
			if ($obj = $this->db->fetch_object($res))
5814
			{
5815
				if ($obj)
5816
				{
5817
					$this->setVarsFromFetchObj($obj);
5818
					return $this->id;
5819
				}
5820
				else
5821
				{
5822
					return 0;
5823
				}
5824
			}
5825
			else
5826
			{
5827
				$this->error = $this->db->lasterror();
5828
				$this->errors[] = $this->error;
5829
				return -1;
5830
			}
5831
		}
5832
		else
5833
		{
5834
			$this->error = $this->db->lasterror();
5835
			$this->errors[] = $this->error;
5836
			return -1;
5837
		}
5838
	}
5839
5840
	/**
5841
	 * Update object into database
5842
	 *
5843
	 * @param  User $user      User that modifies
5844
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
5845
	 * @return int             <0 if KO, >0 if OK
5846
	 */
5847
	public function updateCommon(User $user, $notrigger = false)
5848
	{
5849
		$error = 0;
5850
5851
		$fieldvalues = $this->set_save_query();
5852
		unset($fieldvalues['rowid']);	// We don't update this field, it is the key to define which record to update.
5853
5854
		foreach ($fieldvalues as $k => $v) {
5855
			if (is_array($key)){
5856
				$i=array_search($k, $key);
0 ignored issues
show
Bug introduced by
The variable $key 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...
5857
				if ( $i !== false) {
5858
					$where[] = $key[$i].'=' . $this->quote($v, $this->fields[$k]);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$where was never initialized. Although not strictly required by PHP, it is generally a good practice to add $where = 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...
5859
					continue;
5860
				}
5861
			} else {
5862
				if ( $k == $key) {
5863
					$where[] = $k.'=' .$this->quote($v, $this->fields[$k]);
0 ignored issues
show
Bug introduced by
The variable $where 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...
5864
					continue;
5865
				}
5866
			}
5867
			$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...
5868
		}
5869
		$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...
5870
5871
		$this->db->begin();
5872
		if (! $error)
5873
		{
5874
			$res = $this->db->query($sql);
5875
			if ($res===false)
5876
			{
5877
				$error++;
5878
				$this->errors[] = $this->db->lasterror();
5879
			}
5880
		}
5881
5882
		if (! $error && ! $notrigger) {
5883
			// Call triggers
5884
			$result=$this->call_trigger(strtoupper(get_class($this)).'_MODIFY',$user);
5885
			if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail
5886
			// End call triggers
5887
		}
5888
5889
		// Commit or rollback
5890
		if ($error) {
5891
			$this->db->rollback();
5892
			return -1;
5893
		} else {
5894
			$this->db->commit();
5895
			return $this->id;
5896
		}
5897
	}
5898
5899
	/**
5900
	 * Delete object in database
5901
	 *
5902
	 * @param User $user       User that deletes
5903
	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
5904
	 * @return int             <0 if KO, >0 if OK
5905
	 */
5906
	public function deleteCommon(User $user, $notrigger = false)
5907
	{
5908
		$error=0;
5909
5910
		$this->db->begin();
5911
5912
		if (! $error) {
5913
			if (! $notrigger) {
5914
				// Call triggers
5915
				$result=$this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
5916
				if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail
5917
				// End call triggers
5918
			}
5919
		}
5920
5921
		if (! $error)
5922
		{
5923
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
5924
5925
			$res = $this->db->query($sql);
5926
			if($res===false) {
5927
				$error++;
5928
				$this->errors[] = $this->db->lasterror();
5929
			}
5930
		}
5931
5932
		// Commit or rollback
5933
		if ($error) {
5934
			$this->db->rollback();
5935
			return -1;
5936
		} else {
5937
			$this->db->commit();
5938
			return 1;
5939
		}
5940
	}
5941
5942
	/**
5943
	 * Initialise object with example values
5944
	 * Id must be 0 if object instance is a specimen
5945
	 *
5946
	 * @return void
5947
	 */
5948
	public function initAsSpecimenCommon()
5949
	{
5950
		$this->id = 0;
5951
5952
		// TODO...
5953
	}
5954
5955
	/**
5956
	 * Load comments linked with current task
5957
	 *	@return boolean	1 if ok
5958
	 */
5959
	public function fetchComments()
5960
	{
5961
		require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
5962
5963
		$comment = new Comment($this->db);
5964
		$this->comments = Comment::fetchAllFor($this->element, $this->id);
5965
		return 1;
5966
	}
5967
5968
	/**
5969
	 * Return nb comments already posted
5970
	 *
5971
	 * @return int
5972
	 */
5973
	public function getNbComments()
5974
	{
5975
		return count($this->comments);
5976
	}
5977
5978
}
5979