Completed
Branch develop (0fc56d)
by
unknown
26:25
created

CommonObject::createCommon()   C

Complexity

Conditions 9
Paths 48

Size

Total Lines 47
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

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

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1566
								break;
1567
							case 'commande':
1568
								$this->updateline($line->id, $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);
1 ignored issue
show
Bug introduced by
The method updateline() does not exist on CommonObject. Did you maybe mean updateLineUp()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1569
								break;
1570
							case 'facture':
1571
								$this->updateline($line->id, $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);
1 ignored issue
show
Bug introduced by
The method updateline() does not exist on CommonObject. Did you maybe mean updateLineUp()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1572
								break;
1573
							case 'supplier_proposal':
1574
								$this->updateline($line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $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);
1 ignored issue
show
Bug introduced by
The method updateline() does not exist on CommonObject. Did you maybe mean updateLineUp()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1575
								break;
1576
							case 'order_supplier':
1577
								$this->updateline($line->id, $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);
1 ignored issue
show
Bug introduced by
The method updateline() does not exist on CommonObject. Did you maybe mean updateLineUp()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1578
								break;
1579
							case 'invoice_supplier':
1580
								$this->updateline($line->id, $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);
1 ignored issue
show
Bug introduced by
The method updateline() does not exist on CommonObject. Did you maybe mean updateLineUp()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
1581
								break;
1582
							default:
1583
								dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
1584
								break;
1585
						}
1586
1587
					}
1588
				}
1589
1590
    			return 1;
1591
    		}
1592
    		else
1593
    		{
1594
    			dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
1595
    			$this->error=$this->db->error();
1596
    			return -1;
1597
    		}
1598
    	}
1599
    	else
1600
    	{
1601
    		dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
1602
    		$this->error='Status of the object is incompatible '.$this->statut;
1603
    		return -2;
1604
    	}
1605
    }
1606
1607
    /**
1608
     *  Change the payments terms
1609
     *
1610
     *  @param		int		$id		Id of new payment terms
1611
     *  @return		int				>0 if OK, <0 if KO
1612
     */
1613
    function setPaymentTerms($id)
1614
    {
1615
    	dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
1616
    	if ($this->statut >= 0 || $this->element == 'societe')
1617
    	{
1618
    		// TODO uniformize field name
1619
    		$fieldname = 'fk_cond_reglement';
1620
    		if ($this->element == 'societe') $fieldname = 'cond_reglement';
1621
    		if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
1622
1623
    		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1624
    		$sql .= ' SET '.$fieldname.' = '.$id;
1625
    		$sql .= ' WHERE rowid='.$this->id;
1626
1627
    		if ($this->db->query($sql))
1628
    		{
1629
    			$this->cond_reglement_id = $id;
1630
    			// for supplier
1631
    			if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
1632
    			$this->cond_reglement = $id;	// for compatibility
1633
    			return 1;
1634
    		}
1635
    		else
1636
    		{
1637
    			dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
1638
    			$this->error=$this->db->error();
1639
    			return -1;
1640
    		}
1641
    	}
1642
    	else
1643
    	{
1644
    		dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
1645
    		$this->error='Status of the object is incompatible '.$this->statut;
1646
    		return -2;
1647
    	}
1648
    }
1649
1650
    /**
1651
     *	Define delivery address
1652
     *  @deprecated
1653
     *
1654
     *	@param      int		$id		Address id
1655
     *	@return     int				<0 si ko, >0 si ok
1656
     */
1657
    function setDeliveryAddress($id)
1658
    {
1659
    	$fieldname = 'fk_delivery_address';
1660
    	if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
1661
1662
    	$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
1663
    	$sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
1664
1665
    	if ($this->db->query($sql))
1666
    	{
1667
    		$this->fk_delivery_address = $id;
1668
    		return 1;
1669
    	}
1670
    	else
1671
    	{
1672
    		$this->error=$this->db->error();
1673
    		dol_syslog(get_class($this).'::setDeliveryAddress Erreur '.$sql.' - '.$this->error);
1674
    		return -1;
1675
    	}
1676
    }
1677
1678
1679
    /**
1680
     *  Change the shipping method
1681
     *
1682
     *  @param      int     $shipping_method_id     Id of shipping method
1683
     *  @return     int              1 if OK, 0 if KO
1684
     */
1685
    function setShippingMethod($shipping_method_id)
1686
    {
1687
        if (! $this->table_element) {
1688
            dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined",LOG_ERR);
1689
            return -1;
1690
        }
1691
        if ($shipping_method_id<0) $shipping_method_id='NULL';
1692
        dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
1693
1694
        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1695
        $sql.= " SET fk_shipping_method = ".$shipping_method_id;
1696
        $sql.= " WHERE rowid=".$this->id;
1697
1698
        if ($this->db->query($sql)) {
1699
            $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...
1700
            return 1;
1701
        } else {
1702
            dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
1703
            $this->error=$this->db->error();
1704
            return 0;
1705
        }
1706
    }
1707
1708
1709
    /**
1710
     *  Change the warehouse
1711
     *
1712
     *  @param      int     $warehouse_id     Id of warehouse
1713
     *  @return     int              1 if OK, 0 if KO
1714
     */
1715
    function setWarehouse($warehouse_id)
1716
    {
1717
        if (! $this->table_element) {
1718
            dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined",LOG_ERR);
1719
            return -1;
1720
        }
1721
        if ($warehouse_id<0) $warehouse_id='NULL';
1722
        dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
1723
1724
        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1725
        $sql.= " SET fk_warehouse = ".$warehouse_id;
1726
        $sql.= " WHERE rowid=".$this->id;
1727
1728
        if ($this->db->query($sql)) {
1729
            $this->warehouse_id = ($warehouse_id=='NULL')?null:$warehouse_id;
1730
            return 1;
1731
        } else {
1732
            dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
1733
            $this->error=$this->db->error();
1734
            return 0;
1735
        }
1736
    }
1737
1738
1739
    /**
1740
     *		Set last model used by doc generator
1741
     *
1742
     *		@param		User	$user		User object that make change
1743
     *		@param		string	$modelpdf	Modele name
1744
     *		@return		int					<0 if KO, >0 if OK
1745
     */
1746
    function setDocModel($user, $modelpdf)
1747
    {
1748
        if (! $this->table_element)
1749
        {
1750
            dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined",LOG_ERR);
1751
            return -1;
1752
        }
1753
1754
        $newmodelpdf=dol_trunc($modelpdf,255);
1755
1756
        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1757
        $sql.= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
1758
        $sql.= " WHERE rowid = ".$this->id;
1759
        // if ($this->element == 'facture') $sql.= " AND fk_statut < 2";
1760
        // if ($this->element == 'propal')  $sql.= " AND fk_statut = 0";
1761
1762
        dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
1763
        $resql=$this->db->query($sql);
1764
        if ($resql)
1765
        {
1766
            $this->modelpdf=$modelpdf;
1767
            return 1;
1768
        }
1769
        else
1770
        {
1771
            dol_print_error($this->db);
1772
            return 0;
1773
        }
1774
    }
1775
1776
1777
    /**
1778
     *  Change the bank account
1779
     *
1780
     *  @param		int		$fk_account		Id of bank account
1781
     *  @return		int				1 if OK, 0 if KO
1782
     */
1783
    function setBankAccount($fk_account)
1784
    {
1785
        if (! $this->table_element) {
1786
            dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined",LOG_ERR);
1787
            return -1;
1788
        }
1789
        if ($fk_account<0) $fk_account='NULL';
1790
        dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
1791
1792
        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1793
        $sql.= " SET fk_account = ".$fk_account;
1794
        $sql.= " WHERE rowid=".$this->id;
1795
1796
        if ($this->db->query($sql)) {
1797
            $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...
1798
            return 1;
1799
        } else {
1800
            dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
1801
            $this->error=$this->db->error();
1802
            return 0;
1803
        }
1804
    }
1805
1806
	// TODO: Move line related operations to CommonObjectLine?
1807
1808
    /**
1809
     *  Save a new position (field rang) for details lines.
1810
     *  You can choose to set position for lines with already a position or lines without any position defined.
1811
     *
1812
     * 	@param		boolean		$renum			   True to renum all already ordered lines, false to renum only not already ordered lines.
1813
     * 	@param		string		$rowidorder		   ASC or DESC
1814
     * 	@param		boolean		$fk_parent_line    Table with fk_parent_line field or not
1815
     * 	@return		int                            <0 if KO, >0 if OK
1816
     */
1817
    function line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
1818
    {
1819
        if (! $this->table_element_line)
1820
        {
1821
            dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined",LOG_ERR);
1822
            return -1;
1823
        }
1824
        if (! $this->fk_element)
1825
        {
1826
            dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined",LOG_ERR);
1827
            return -1;
1828
        }
1829
1830
        // Count number of lines to reorder (according to choice $renum)
1831
    	$nl=0;
1832
        $sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
1833
		$sql.= ' WHERE '.$this->fk_element.'='.$this->id;
1834
		if (! $renum) $sql.= ' AND rang = 0';
1835
		if ($renum) $sql.= ' AND rang <> 0';
1836
1837
		dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
1838
		$resql = $this->db->query($sql);
1839
		if ($resql)
1840
		{
1841
			$row = $this->db->fetch_row($resql);
1842
			$nl = $row[0];
1843
		}
1844
		else dol_print_error($this->db);
1845
		if ($nl > 0)
1846
		{
1847
			// The goal of this part is to reorder all lines, with all children lines sharing the same
1848
			// counter that parents.
1849
			$rows=array();
1850
1851
			// We first search all lines that are parent lines (for multilevel details lines)
1852
			$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
1853
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
1854
			if ($fk_parent_line) $sql.= ' AND fk_parent_line IS NULL';
1855
			$sql.= ' ORDER BY rang ASC, rowid '.$rowidorder;
1856
1857
			dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
1858
			$resql = $this->db->query($sql);
1859
			if ($resql)
1860
			{
1861
				$i=0;
1862
				$num = $this->db->num_rows($resql);
1863
				while ($i < $num)
1864
				{
1865
					$row = $this->db->fetch_row($resql);
1866
					$rows[] = $row[0];	// Add parent line into array rows
1867
					$childrens = $this->getChildrenOfLine($row[0]);
1868
					if (! empty($childrens))
1869
					{
1870
						foreach($childrens as $child)
1871
						{
1872
							array_push($rows, $child);
1873
						}
1874
					}
1875
					$i++;
1876
				}
1877
1878
				// Now we set a new number for each lines (parent and children with children included into parent tree)
1879
				if (! empty($rows))
1880
				{
1881
					foreach($rows as $key => $row)
1882
					{
1883
						$this->updateRangOfLine($row, ($key+1));
1884
					}
1885
				}
1886
			}
1887
			else
1888
			{
1889
				dol_print_error($this->db);
1890
			}
1891
		}
1892
		return 1;
1893
	}
1894
1895
	/**
1896
	 * 	Get children of line
1897
	 *
1898
	 * 	@param	int		$id		Id of parent line
1899
	 * 	@return	array			Array with list of children lines id
1900
	 */
1901
	function getChildrenOfLine($id)
1902
	{
1903
		$rows=array();
1904
1905
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
1906
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
1907
		$sql.= ' AND fk_parent_line = '.$id;
1908
		$sql.= ' ORDER BY rang ASC';
1909
1910
		dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG);
1911
		$resql = $this->db->query($sql);
1912
		if ($resql)
1913
		{
1914
			$i=0;
1915
			$num = $this->db->num_rows($resql);
1916
			while ($i < $num)
1917
			{
1918
				$row = $this->db->fetch_row($resql);
1919
				$rows[$i] = $row[0];
1920
				$i++;
1921
			}
1922
		}
1923
1924
		return $rows;
1925
	}
1926
1927
    /**
1928
     * 	Update a line to have a lower rank
1929
     *
1930
     * 	@param 	int			$rowid				Id of line
1931
     * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
1932
     * 	@return	void
1933
     */
1934
    function line_up($rowid, $fk_parent_line=true)
1935
    {
1936
        $this->line_order(false, 'ASC', $fk_parent_line);
1937
1938
        // Get rang of line
1939
        $rang = $this->getRangOfLine($rowid);
1940
1941
        // Update position of line
1942
        $this->updateLineUp($rowid, $rang);
1943
    }
1944
1945
    /**
1946
     * 	Update a line to have a higher rank
1947
     *
1948
     * 	@param	int			$rowid				Id of line
1949
     * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
1950
     * 	@return	void
1951
     */
1952
    function line_down($rowid, $fk_parent_line=true)
1953
    {
1954
        $this->line_order(false, 'ASC', $fk_parent_line);
1955
1956
        // Get rang of line
1957
        $rang = $this->getRangOfLine($rowid);
1958
1959
        // Get max value for rang
1960
        $max = $this->line_max();
1961
1962
        // Update position of line
1963
        $this->updateLineDown($rowid, $rang, $max);
1964
    }
1965
1966
	/**
1967
	 * 	Update position of line (rang)
1968
	 *
1969
	 * 	@param	int		$rowid		Id of line
1970
	 * 	@param	int		$rang		Position
1971
	 * 	@return	void
1972
	 */
1973
	function updateRangOfLine($rowid,$rang)
1974
	{
1975
	    $fieldposition = 'rang';
1976
	    if ($this->table_element_line == 'ecm_files') $fieldposition = 'position';
1977
1978
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
1979
		$sql.= ' WHERE rowid = '.$rowid;
1980
1981
		dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
1982
		if (! $this->db->query($sql))
1983
		{
1984
			dol_print_error($this->db);
1985
		}
1986
	}
1987
1988
    /**
1989
     * 	Update position of line with ajax (rang)
1990
     *
1991
     * 	@param	array	$rows	Array of rows
1992
     * 	@return	void
1993
     */
1994
    function line_ajaxorder($rows)
1995
    {
1996
        $num = count($rows);
1997
        for ($i = 0 ; $i < $num ; $i++)
1998
        {
1999
            $this->updateRangOfLine($rows[$i], ($i+1));
2000
        }
2001
    }
2002
2003
    /**
2004
     * 	Update position of line up (rang)
2005
     *
2006
     * 	@param	int		$rowid		Id of line
2007
     * 	@param	int		$rang		Position
2008
     * 	@return	void
2009
     */
2010
    function updateLineUp($rowid,$rang)
2011
    {
2012
        if ($rang > 1 )
2013
        {
2014
            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.$rang ;
2015
            $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2016
            $sql.= ' AND rang = '.($rang - 1);
2017
            if ($this->db->query($sql) )
2018
            {
2019
                $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang  = '.($rang - 1);
2020
                $sql.= ' WHERE rowid = '.$rowid;
2021
                if (! $this->db->query($sql) )
2022
                {
2023
                    dol_print_error($this->db);
2024
                }
2025
            }
2026
            else
2027
            {
2028
                dol_print_error($this->db);
2029
            }
2030
        }
2031
    }
2032
2033
    /**
2034
     * 	Update position of line down (rang)
2035
     *
2036
     * 	@param	int		$rowid		Id of line
2037
     * 	@param	int		$rang		Position
2038
     * 	@param	int		$max		Max
2039
     * 	@return	void
2040
     */
2041
    function updateLineDown($rowid,$rang,$max)
2042
    {
2043
        if ($rang < $max)
2044
        {
2045
            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.$rang;
2046
            $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2047
            $sql.= ' AND rang = '.($rang+1);
2048
            if ($this->db->query($sql) )
2049
            {
2050
                $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.($rang+1);
2051
                $sql.= ' WHERE rowid = '.$rowid;
2052
                if (! $this->db->query($sql) )
2053
                {
2054
                    dol_print_error($this->db);
2055
                }
2056
            }
2057
            else
2058
            {
2059
                dol_print_error($this->db);
2060
            }
2061
        }
2062
    }
2063
2064
    /**
2065
     * 	Get position of line (rang)
2066
     *
2067
     * 	@param		int		$rowid		Id of line
2068
     *  @return		int     			Value of rang in table of lines
2069
     */
2070
    function getRangOfLine($rowid)
2071
    {
2072
        $sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2073
        $sql.= ' WHERE rowid ='.$rowid;
2074
2075
        dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
2076
        $resql = $this->db->query($sql);
2077
        if ($resql)
2078
        {
2079
            $row = $this->db->fetch_row($resql);
2080
            return $row[0];
2081
        }
2082
    }
2083
2084
    /**
2085
     * 	Get rowid of the line relative to its position
2086
     *
2087
     * 	@param		int		$rang		Rang value
2088
     *  @return     int     			Rowid of the line
2089
     */
2090
    function getIdOfLine($rang)
2091
    {
2092
        $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2093
        $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2094
        $sql.= ' AND rang = '.$rang;
2095
        $resql = $this->db->query($sql);
2096
        if ($resql)
2097
        {
2098
            $row = $this->db->fetch_row($resql);
2099
            return $row[0];
2100
        }
2101
    }
2102
2103
    /**
2104
     * 	Get max value used for position of line (rang)
2105
     *
2106
     * 	@param		int		$fk_parent_line		Parent line id
2107
     *  @return     int  			   			Max value of rang in table of lines
2108
     */
2109
    function line_max($fk_parent_line=0)
2110
    {
2111
        // Search the last rang with fk_parent_line
2112
        if ($fk_parent_line)
2113
        {
2114
            $sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2115
            $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2116
            $sql.= ' AND fk_parent_line = '.$fk_parent_line;
2117
2118
            dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2119
            $resql = $this->db->query($sql);
2120
            if ($resql)
2121
            {
2122
                $row = $this->db->fetch_row($resql);
2123
                if (! empty($row[0]))
2124
                {
2125
                    return $row[0];
2126
                }
2127
                else
2128
                {
2129
                    return $this->getRangOfLine($fk_parent_line);
2130
                }
2131
            }
2132
        }
2133
        // If not, search the last rang of element
2134
        else
2135
        {
2136
            $sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2137
            $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2138
2139
            dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2140
            $resql = $this->db->query($sql);
2141
            if ($resql)
2142
            {
2143
                $row = $this->db->fetch_row($resql);
2144
                return $row[0];
2145
            }
2146
        }
2147
    }
2148
2149
    /**
2150
     *  Update external ref of element
2151
     *
2152
     *  @param      string		$ref_ext	Update field ref_ext
2153
     *  @return     int      		   		<0 if KO, >0 if OK
2154
     */
2155
    function update_ref_ext($ref_ext)
2156
    {
2157
        if (! $this->table_element)
2158
        {
2159
            dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
2160
            return -1;
2161
        }
2162
2163
        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2164
        $sql.= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
2165
        $sql.= " WHERE ".(isset($this->table_rowid)?$this->table_rowid:'rowid')." = ". $this->id;
2166
2167
        dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
2168
        if ($this->db->query($sql))
2169
        {
2170
            $this->ref_ext = $ref_ext;
2171
            return 1;
2172
        }
2173
        else
2174
        {
2175
            $this->error=$this->db->error();
2176
            return -1;
2177
        }
2178
    }
2179
2180
    /**
2181
     *  Update note of element
2182
     *
2183
     *  @param      string		$note		New value for note
2184
     *  @param		string		$suffix		'', '_public' or '_private'
2185
     *  @return     int      		   		<0 if KO, >0 if OK
2186
     */
2187
    function update_note($note,$suffix='')
2188
    {
2189
        global $user;
2190
2191
    	if (! $this->table_element)
2192
    	{
2193
    		dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
2194
    		return -1;
2195
    	}
2196
		if (! in_array($suffix,array('','_public','_private')))
2197
		{
2198
    		dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
2199
			return -2;
2200
		}
2201
        // Special cas
2202
        //var_dump($this->table_element);exit;
2203
		if ($this->table_element == 'product') $suffix='';
2204
2205
    	$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2206
    	$sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL");
2207
    	$sql.= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))?"fk_user_mod":"fk_user_modif")." = ".$user->id;
2208
    	$sql.= " WHERE rowid =". $this->id;
2209
2210
    	dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
2211
    	if ($this->db->query($sql))
2212
    	{
2213
    		if ($suffix == '_public') $this->note_public = $note;
2214
    		else if ($suffix == '_private') $this->note_private = $note;
2215
    		else
2216
    		{
2217
    		    $this->note = $note;      // deprecated
2218
    		    $this->note_private = $note;
2219
    		}
2220
    		return 1;
2221
    	}
2222
    	else
2223
    	{
2224
    		$this->error=$this->db->lasterror();
2225
    		return -1;
2226
    	}
2227
    }
2228
2229
    /**
2230
     * 	Update public note (kept for backward compatibility)
2231
     *
2232
     * @param      string		$note		New value for note
2233
     * @return     int      		   		<0 if KO, >0 if OK
2234
     * @deprecated
2235
     * @see update_note()
2236
     */
2237
    function update_note_public($note)
2238
    {
2239
    	return $this->update_note($note,'_public');
2240
    }
2241
2242
    /**
2243
     *	Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
2244
     *  Must be called at end of methods addline or updateline.
2245
     *
2246
     *	@param	int		$exclspec          	>0 = Exclude special product (product_type=9)
2247
     *  @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
2248
     *  @param	int		$nodatabaseupdate	1=Do not update database. Update only properties of object.
2249
     *  @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.
2250
     *	@return	int    			           	<0 if KO, >0 if OK
2251
     */
2252
    function update_price($exclspec=0,$roundingadjust='none',$nodatabaseupdate=0,$seller=null)
2253
    {
2254
    	global $conf;
2255
2256
        include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2257
2258
        if ($roundingadjust == '-1') $roundingadjust='auto';	// For backward compatibility
2259
2260
        $forcedroundingmode=$roundingadjust;
2261
        if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode=$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
2262
        elseif ($forcedroundingmode == 'auto') $forcedroundingmode='0';
2263
2264
        $error=0;
2265
2266
        // Define constants to find lines to sum
2267
        $fieldtva='total_tva';
2268
        $fieldlocaltax1='total_localtax1';
2269
        $fieldlocaltax2='total_localtax2';
2270
        $fieldup='subprice';
2271
        if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
2272
        {
2273
        	$fieldtva='tva';
2274
        	$fieldup='pu_ht';
2275
        }
2276
        if ($this->element == 'expensereport')
2277
        {
2278
        	$fieldup='value_unit';
2279
        }
2280
2281
        $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,';
2282
        $sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
2283
		if ($this->table_element_line == 'facturedet') $sql.= ', situation_percent';
2284
		$sql.= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
2285
        $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2286
        $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2287
        if ($exclspec)
2288
        {
2289
            $product_field='product_type';
2290
            if ($this->table_element_line == 'contratdet') $product_field='';    // contratdet table has no product_type field
2291
            if ($product_field) $sql.= ' AND '.$product_field.' <> 9';
2292
        }
2293
        $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
2294
2295
        dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2296
        $resql = $this->db->query($sql);
2297
        if ($resql)
2298
        {
2299
            $this->total_ht  = 0;
2300
            $this->total_tva = 0;
2301
            $this->total_localtax1 = 0;
2302
            $this->total_localtax2 = 0;
2303
            $this->total_ttc = 0;
2304
            $total_ht_by_vats  = array();
2305
            $total_tva_by_vats = array();
2306
            $total_ttc_by_vats = array();
2307
			$this->multicurrency_total_ht	= 0;
2308
            $this->multicurrency_total_tva	= 0;
2309
           	$this->multicurrency_total_ttc	= 0;
2310
2311
            $num = $this->db->num_rows($resql);
2312
            $i = 0;
2313
            while ($i < $num)
2314
            {
2315
                $obj = $this->db->fetch_object($resql);
2316
2317
                // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
2318
				$multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
2319
                if ($forcedroundingmode == '0')	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode='auto'
2320
                {
2321
                	$localtax_array=array($obj->localtax1_type,$obj->localtax1_tx,$obj->localtax2_type,$obj->localtax2_tx);
2322
                	$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 2252 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...
2323
                	$diff=price2num($tmpcal[1] - $obj->total_tva, 'MT', 1);
2324
                	if ($diff)
2325
                	{
2326
                		$sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid;
2327
                		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);
2328
						$resqlfix=$this->db->query($sqlfix);
2329
						if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
2330
						$obj->total_tva = $tmpcal[1];
2331
						$obj->total_ttc = $tmpcal[2];
2332
                		//
2333
                	}
2334
                }
2335
2336
                $this->total_ht        += $obj->total_ht;		// The only field visible at end of line detail
2337
                $this->total_tva       += $obj->total_tva;
2338
                $this->total_localtax1 += $obj->total_localtax1;
2339
                $this->total_localtax2 += $obj->total_localtax2;
2340
                $this->total_ttc       += $obj->total_ttc;
2341
2342
                if (! isset($total_ht_by_vats[$obj->vatrate]))  $total_ht_by_vats[$obj->vatrate]=0;
2343
                if (! isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate]=0;
2344
                if (! isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate]=0;
2345
                $total_ht_by_vats[$obj->vatrate]  += $obj->total_ht;
2346
                $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
2347
                $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
2348
2349
                if ($forcedroundingmode == '1')	// Check if we need adjustement onto line for vat
2350
                {
2351
                	$tmpvat=price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
2352
                	$diff=price2num($total_tva_by_vats[$obj->vatrate]-$tmpvat, 'MT', 1);
2353
                	//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";
2354
                	if ($diff)
2355
                	{
2356
                		if (abs($diff) > 0.1) { dol_syslog('A rounding difference was detected into TOTAL but is too high to be corrected', LOG_WARNING); exit; }
2357
                		$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;
2358
                		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);
2359
						$resqlfix=$this->db->query($sqlfix);
2360
						if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
2361
						$this->total_tva -= $diff;
2362
						$this->total_ttc -= $diff;
2363
						$total_tva_by_vats[$obj->vatrate] -= $diff;
2364
						$total_ttc_by_vats[$obj->vatrate] -= $diff;
2365
2366
                	}
2367
                }
2368
2369
                $i++;
2370
            }
2371
2372
            // Add revenue stamp to total
2373
            $this->total_ttc       += isset($this->revenuestamp)?$this->revenuestamp:0;
2374
2375
			// Situations totals
2376
			if ($this->situation_cycle_ref && $this->situation_counter > 1) {
2377
				$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...
2378
2379
				foreach ($prev_sits as $sit) {
2380
					$this->total_ht -= $sit->total_ht;
2381
					$this->total_tva -= $sit->total_tva;
2382
					$this->total_localtax1 -= $sit->total_localtax1;
2383
					$this->total_localtax2 -= $sit->total_localtax2;
2384
					$this->total_ttc -= $sit->total_ttc;
2385
				}
2386
			}
2387
2388
			// Multicurrency
2389
			$this->multicurrency_total_ht	+= $this->total_ht * $multicurrency_tx;
0 ignored issues
show
Bug introduced by
The variable $multicurrency_tx 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...
2390
            $this->multicurrency_total_tva	+= $this->total_tva * $multicurrency_tx;
2391
            $this->multicurrency_total_ttc	+= $this->total_ttc * $multicurrency_tx;
2392
2393
            $this->db->free($resql);
2394
2395
            // Now update global field total_ht, total_ttc and tva
2396
            $fieldht='total_ht';
2397
            $fieldtva='tva';
2398
            $fieldlocaltax1='localtax1';
2399
            $fieldlocaltax2='localtax2';
2400
            $fieldttc='total_ttc';
2401
            // Specific code for backward compatibility with old field names
2402
            if ($this->element == 'facture' || $this->element == 'facturerec')             $fieldht='total';
2403
            if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='total_tva';
2404
            if ($this->element == 'propal')                                                $fieldttc='total';
2405
            if ($this->element == 'expensereport')                                         $fieldtva='total_tva';
2406
            if ($this->element == 'supplier_proposal')                                      $fieldttc='total';
2407
2408
            if (empty($nodatabaseupdate))
2409
            {
2410
                $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2411
                $sql .= " ".$fieldht."='".price2num($this->total_ht)."',";
2412
                $sql .= " ".$fieldtva."='".price2num($this->total_tva)."',";
2413
                $sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',";
2414
                $sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',";
2415
                $sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'";
2416
				$sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'";
2417
				$sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'";
2418
				$sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'";
2419
                $sql .= ' WHERE rowid = '.$this->id;
2420
2421
                //print "xx".$sql;
2422
                dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2423
                $resql=$this->db->query($sql);
2424
                if (! $resql)
2425
                {
2426
                    $error++;
2427
                    $this->error=$this->db->lasterror();
2428
                    $this->errors[]=$this->db->lasterror();
2429
                }
2430
            }
2431
2432
            if (! $error)
2433
            {
2434
                return 1;
2435
            }
2436
            else
2437
            {
2438
                return -1;
2439
            }
2440
        }
2441
        else
2442
        {
2443
            dol_print_error($this->db,'Bad request in update_price');
2444
            return -1;
2445
        }
2446
    }
2447
2448
    /**
2449
     *	Add objects linked in llx_element_element.
2450
     *
2451
     *	@param		string	$origin		Linked element type
2452
     *	@param		int		$origin_id	Linked element id
2453
     *	@return		int					<=0 if KO, >0 if OK
2454
     *	@see		fetchObjectLinked, updateObjectLinked, deleteObjectLinked
2455
     */
2456
    function add_object_linked($origin=null, $origin_id=null)
2457
    {
2458
    	$origin = (! empty($origin) ? $origin : $this->origin);
2459
    	$origin_id = (! empty($origin_id) ? $origin_id : $this->origin_id);
2460
2461
    	// Special case
2462
    	if ($origin == 'order') $origin='commande';
2463
    	if ($origin == 'invoice') $origin='facture';
2464
2465
        $this->db->begin();
2466
2467
        $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
2468
        $sql.= "fk_source";
2469
        $sql.= ", sourcetype";
2470
        $sql.= ", fk_target";
2471
        $sql.= ", targettype";
2472
        $sql.= ") VALUES (";
2473
        $sql.= $origin_id;
2474
        $sql.= ", '".$origin."'";
2475
        $sql.= ", ".$this->id;
2476
        $sql.= ", '".$this->element."'";
2477
        $sql.= ")";
2478
2479
        dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG);
2480
		if ($this->db->query($sql))
2481
	  	{
2482
	  		$this->db->commit();
2483
	  		return 1;
2484
	  	}
2485
	  	else
2486
	  	{
2487
	  		$this->error=$this->db->lasterror();
2488
	  		$this->db->rollback();
2489
	  		return 0;
2490
	  	}
2491
	}
2492
2493
    /**
2494
     *	Fetch array of objects linked to current object. Links are loaded into this->linkedObjects array and this->linkedObjectsIds
2495
     *  Possible usage for parameters:
2496
     *  - all parameters empty -> we look all link to current object (current object can be source or target)
2497
     *  - source id+type -> will get target list linked to source
2498
     *  - target id+type -> will get source list linked to target
2499
     *  - source id+type + target type -> will get target list of the type
2500
     *  - target id+type + target source -> will get source list of the type
2501
     *
2502
     *	@param	int		$sourceid		Object source id (if not defined, id of object)
2503
     *	@param  string	$sourcetype		Object source type (if not defined, element name of object)
2504
     *	@param  int		$targetid		Object target id (if not defined, id of object)
2505
     *	@param  string	$targettype		Object target type (if not defined, elemennt name of object)
2506
     *	@param  string	$clause			'OR' or 'AND' clause used when both source id and target id are provided
2507
     *  @param	int		$alsosametype	0=Return only links to object that differs from source. 1=Include also link to objects of same type.
2508
     *	@return	void
2509
     *  @see	add_object_linked, updateObjectLinked, deleteObjectLinked
2510
     */
2511
	function fetchObjectLinked($sourceid=null,$sourcetype='',$targetid=null,$targettype='',$clause='OR',$alsosametype=1)
2512
    {
2513
        global $conf;
2514
2515
        $this->linkedObjectsIds=array();
2516
        $this->linkedObjects=array();
2517
2518
        $justsource=false;
2519
        $justtarget=false;
2520
        $withtargettype=false;
2521
        $withsourcetype=false;
2522
2523
        if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid))
2524
        {
2525
        	$justsource=true;  // the source (id and type) is a search criteria
2526
        	if (! empty($targettype)) $withtargettype=true;
2527
        }
2528
        if (! empty($targetid) && ! empty($targettype) && empty($sourceid))
2529
        {
2530
        	$justtarget=true;  // the target (id and type) is a search criteria
2531
        	if (! empty($sourcetype)) $withsourcetype=true;
2532
        }
2533
2534
        $sourceid = (! empty($sourceid) ? $sourceid : $this->id);
2535
        $targetid = (! empty($targetid) ? $targetid : $this->id);
2536
        $sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
2537
        $targettype = (! empty($targettype) ? $targettype : $this->element);
2538
2539
        /*if (empty($sourceid) && empty($targetid))
2540
        {
2541
        	dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
2542
        	return -1;
2543
        }*/
2544
2545
        // Links between objects are stored in table element_element
2546
        $sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype';
2547
        $sql.= ' FROM '.MAIN_DB_PREFIX.'element_element';
2548
        $sql.= " WHERE ";
2549
        if ($justsource || $justtarget)
2550
        {
2551
            if ($justsource)
2552
            {
2553
            	$sql.= "fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."'";
2554
            	if ($withtargettype) $sql.= " AND targettype = '".$targettype."'";
2555
            }
2556
            else if ($justtarget)
2557
            {
2558
            	$sql.= "fk_target = ".$targetid." AND targettype = '".$targettype."'";
2559
            	if ($withsourcetype) $sql.= " AND sourcetype = '".$sourcetype."'";
2560
            }
2561
        }
2562
        else
2563
		{
2564
            $sql.= "(fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."')";
2565
            $sql.= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$targettype."')";
2566
        }
2567
        $sql .= ' ORDER BY sourcetype';
2568
        //print $sql;
2569
2570
        dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
2571
        $resql = $this->db->query($sql);
2572
        if ($resql)
2573
        {
2574
            $num = $this->db->num_rows($resql);
2575
            $i = 0;
2576
            while ($i < $num)
2577
            {
2578
                $obj = $this->db->fetch_object($resql);
2579
                if ($justsource || $justtarget)
2580
                {
2581
                    if ($justsource)
2582
                    {
2583
                        $this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
2584
                    }
2585
                    else if ($justtarget)
2586
                    {
2587
                        $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
2588
                    }
2589
                }
2590
                else
2591
                {
2592
                    if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype)
2593
                    {
2594
                        $this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
2595
                    }
2596
                    if ($obj->fk_target == $targetid && $obj->targettype == $targettype)
2597
                    {
2598
                        $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
2599
                    }
2600
                }
2601
                $i++;
2602
            }
2603
2604
            if (! empty($this->linkedObjectsIds))
2605
            {
2606
                foreach($this->linkedObjectsIds as $objecttype => $objectids)       // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
2607
                {
2608
                    // Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
2609
                    $module = $element = $subelement = $objecttype;
2610
                    if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
2611
                        && preg_match('/^([^_]+)_([^_]+)/i',$objecttype,$regs))
2612
                    {
2613
                        $module = $element = $regs[1];
2614
                        $subelement = $regs[2];
2615
                    }
2616
2617
                    $classpath = $element.'/class';
2618
                    // To work with non standard classpath or module name
2619
                    if ($objecttype == 'facture')			{
2620
                        $classpath = 'compta/facture/class';
2621
                    }
2622
                    else if ($objecttype == 'facturerec')			{
2623
                        $classpath = 'compta/facture/class'; $module = 'facture';
2624
                    }
2625
                    else if ($objecttype == 'propal')			{
2626
                        $classpath = 'comm/propal/class';
2627
                    }
2628
                    else if ($objecttype == 'supplier_proposal')			{
2629
                        $classpath = 'supplier_proposal/class';
2630
                    }
2631
                    else if ($objecttype == 'shipping')			{
2632
                        $classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon';
2633
                    }
2634
                    else if ($objecttype == 'delivery')			{
2635
                        $classpath = 'livraison/class'; $subelement = 'livraison'; $module = 'livraison_bon';
2636
                    }
2637
                    else if ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier')	{
2638
                        $classpath = 'fourn/class'; $module = 'fournisseur';
2639
                    }
2640
                    else if ($objecttype == 'fichinter')			{
2641
                        $classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter';
2642
                    }
2643
                    else if ($objecttype == 'subscription')			{
2644
                        $classpath = 'adherents/class'; $module = 'adherent';
2645
                    }
2646
2647
                    // Set classfile
2648
                    $classfile = strtolower($subelement); $classname = ucfirst($subelement);
2649
2650
                    if ($objecttype == 'order') {
2651
                        $classfile = 'commande'; $classname = 'Commande';
2652
                    }
2653
                    else if ($objecttype == 'invoice_supplier') {
2654
                        $classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur';
2655
                    }
2656
                    else if ($objecttype == 'order_supplier')   {
2657
                        $classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur';
2658
                    }
2659
                    else if ($objecttype == 'supplier_proposal')   {
2660
                        $classfile = 'supplier_proposal'; $classname = 'SupplierProposal';
2661
                    }
2662
                    else if ($objecttype == 'facturerec')   {
2663
                        $classfile = 'facture-rec'; $classname = 'FactureRec';
2664
                    }
2665
                    else if ($objecttype == 'subscription')   {
2666
                        $classfile = 'subscription'; $classname = 'Subscription';
2667
                    }
2668
2669
                    // Here $module, $classfile and $classname are set
2670
                    if ($conf->$module->enabled && (($element != $this->element) || $alsosametype))
2671
                    {
2672
                        dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
2673
                        //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
2674
                        if (class_exists($classname))
2675
                        {
2676
	                        foreach($objectids as $i => $objectid)	// $i is rowid into llx_element_element
2677
	                        {
2678
	                            $object = new $classname($this->db);
2679
	                            $ret = $object->fetch($objectid);
2680
	                            if ($ret >= 0)
2681
	                            {
2682
	                                $this->linkedObjects[$objecttype][$i] = $object;
2683
	                            }
2684
	                        }
2685
                        }
2686
                    }
2687
                }
2688
            }
2689
        }
2690
        else
2691
        {
2692
            dol_print_error($this->db);
2693
        }
2694
    }
2695
2696
    /**
2697
     *	Update object linked of a current object
2698
     *
2699
     *	@param	int		$sourceid		Object source id
2700
     *	@param  string	$sourcetype		Object source type
2701
     *	@param  int		$targetid		Object target id
2702
     *	@param  string	$targettype		Object target type
2703
     *	@return							int	>0 if OK, <0 if KO
2704
     *	@see	add_object_linked, fetObjectLinked, deleteObjectLinked
2705
     */
2706
    function updateObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='')
2707
    {
2708
    	$updatesource=false;
2709
    	$updatetarget=false;
2710
2711
    	if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource=true;
2712
    	else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $updatetarget=true;
2713
2714
    	$sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET ";
2715
    	if ($updatesource)
2716
    	{
2717
    		$sql.= "fk_source = ".$sourceid;
2718
    		$sql.= ", sourcetype = '".$this->db->escape($sourcetype)."'";
2719
    		$sql.= " WHERE fk_target = ".$this->id;
2720
    		$sql.= " AND targettype = '".$this->db->escape($this->element)."'";
2721
    	}
2722
    	else if ($updatetarget)
2723
    	{
2724
    		$sql.= "fk_target = ".$targetid;
2725
    		$sql.= ", targettype = '".$this->db->escape($targettype)."'";
2726
    		$sql.= " WHERE fk_source = ".$this->id;
2727
    		$sql.= " AND sourcetype = '".$this->db->escape($this->element)."'";
2728
    	}
2729
2730
    	dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG);
2731
    	if ($this->db->query($sql))
2732
    	{
2733
    		return 1;
2734
    	}
2735
    	else
2736
    	{
2737
    		$this->error=$this->db->lasterror();
2738
    		return -1;
2739
    	}
2740
    }
2741
2742
	/**
2743
	 *	Delete all links between an object $this
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
     *  @param	int		$rowid			Row id of line to delete. If defined, other parameters are not used.
2750
	 *	@return     					int	>0 if OK, <0 if KO
2751
	 *	@see	add_object_linked, updateObjectLinked, fetchObjectLinked
2752
	 */
2753
	function deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='')
2754
	{
2755
		$deletesource=false;
2756
		$deletetarget=false;
2757
2758
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource=true;
2759
		else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $deletetarget=true;
2760
2761
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
2762
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
2763
		$targetid = (! empty($targetid) ? $targetid : $this->id);
2764
		$targettype = (! empty($targettype) ? $targettype : $this->element);
2765
2766
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element";
2767
		$sql.= " WHERE";
2768
		if ($rowid > 0)
2769
		{
2770
			$sql.=" rowid = ".$rowid;
2771
		}
2772
		else
2773
		{
2774
			if ($deletesource)
2775
			{
2776
				$sql.= " fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'";
2777
				$sql.= " AND fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."'";
2778
			}
2779
			else if ($deletetarget)
2780
			{
2781
				$sql.= " fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'";
2782
				$sql.= " AND fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."'";
2783
			}
2784
			else
2785
			{
2786
				$sql.= " (fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."')";
2787
				$sql.= " OR";
2788
				$sql.= " (fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."')";
2789
			}
2790
		}
2791
2792
		dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG);
2793
		if ($this->db->query($sql))
2794
		{
2795
			return 1;
2796
		}
2797
		else
2798
		{
2799
			$this->error=$this->db->lasterror();
2800
			$this->errors[]=$this->error;
2801
			return -1;
2802
		}
2803
	}
2804
2805
    /**
2806
     *      Set status of an object
2807
     *
2808
     *      @param	int		$status			Status to set
2809
     *      @param	int		$elementId		Id of element to force (use this->id by default)
2810
     *      @param	string	$elementType	Type of element to force (use this->table_element by default)
2811
     *      @return int						<0 if KO, >0 if OK
2812
     */
2813
    function setStatut($status,$elementId=null,$elementType='')
2814
    {
2815
    	global $user,$langs,$conf;
2816
2817
    	$savElementId=$elementId;  // To be used later to know if we were using the method using the id of this or not.
2818
2819
        $elementId = (!empty($elementId)?$elementId:$this->id);
2820
        $elementTable = (!empty($elementType)?$elementType:$this->table_element);
2821
2822
        $this->db->begin();
2823
2824
        $fieldstatus="fk_statut";
2825
        if ($elementTable == 'mailing') $fieldstatus="statut";
2826
        if ($elementTable == 'user') $fieldstatus="statut";
2827
        if ($elementTable == 'expensereport') $fieldstatus="fk_statut";
2828
		if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status";
2829
2830
        $sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable;
2831
        $sql.= " SET ".$fieldstatus." = ".$status;
2832
        // If status = 1 = validated, update also fk_user_valid
2833
        if ($status == 1 && $elementTable == 'expensereport') $sql.=", fk_user_valid = ".$user->id;
2834
        $sql.= " WHERE rowid=".$elementId;
2835
2836
        dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
2837
        if ($this->db->query($sql))
2838
        {
2839
            $error = 0;
2840
2841
            $trigkey='';
2842
            if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_SIGN';   // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
2843
            if ($this->element == 'supplier_proposal' && $status == 3) $trigkey='SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
2844
            if ($this->element == 'supplier_proposal' && $status == 4) $trigkey='SUPPLIER_PROPOSAL_CLOSE';  // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
2845
            if ($this->element == 'fichinter' && $status == 3) $trigkey='FICHINTER_CLASSIFY_DONE';
2846
            if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED';
2847
            if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED';
2848
2849
            if ($trigkey)
2850
            {
2851
                // Appel des triggers
2852
                include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
2853
                $interface=new Interfaces($this->db);
2854
                $result=$interface->run_triggers($trigkey,$this,$user,$langs,$conf);
2855
                if ($result < 0) {
2856
                    $error++; $this->errors=$interface->errors;
2857
                }
2858
                // Fin appel triggers
2859
            }
2860
2861
			if (! $error)
2862
			{
2863
				$this->db->commit();
2864
2865
        		if (empty($savElementId))    // If the element we update was $this (so $elementId is null)
2866
        		{
2867
        		    $this->statut = $status;
2868
        		    $this->status = $status;
2869
        		}
2870
2871
                return 1;
2872
			}
2873
			else
2874
			{
2875
				$this->db->rollback();
2876
				dol_syslog(get_class($this)."::setStatus ".$this->error,LOG_ERR);
2877
				return -1;
2878
			}
2879
        }
2880
        else
2881
        {
2882
        	$this->error=$this->db->lasterror();
2883
        	$this->db->rollback();
2884
        	return -1;
2885
        }
2886
    }
2887
2888
2889
    /**
2890
     *  Load type of canvas of an object if it exists
2891
     *
2892
     *  @param      int		$id     Record id
2893
     *  @param      string	$ref    Record ref
2894
     *  @return		int				<0 if KO, 0 if nothing done, >0 if OK
2895
     */
2896
    function getCanvas($id=0,$ref='')
2897
    {
2898
        global $conf;
2899
2900
        if (empty($id) && empty($ref)) return 0;
2901
        if (! empty($conf->global->MAIN_DISABLE_CANVAS)) return 0;    // To increase speed. Not enabled by default.
2902
2903
        // Clean parameters
2904
        $ref = trim($ref);
2905
2906
        $sql = "SELECT rowid, canvas";
2907
        $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
2908
        $sql.= " WHERE entity IN (".getEntity($this->element, 1).")";
2909
        if (! empty($id))  $sql.= " AND rowid = ".$id;
2910
        if (! empty($ref)) $sql.= " AND ref = '".$this->db->escape($ref)."'";
2911
2912
        $resql = $this->db->query($sql);
2913
        if ($resql)
2914
        {
2915
            $obj = $this->db->fetch_object($resql);
2916
            if ($obj)
2917
            {
2918
                $this->canvas   = $obj->canvas;
2919
                return 1;
2920
            }
2921
            else return 0;
2922
        }
2923
        else
2924
        {
2925
            dol_print_error($this->db);
2926
            return -1;
2927
        }
2928
    }
2929
2930
2931
    /**
2932
     * 	Get special code of a line
2933
     *
2934
     * 	@param	int		$lineid		Id of line
2935
     * 	@return	int					Special code
2936
     */
2937
    function getSpecialCode($lineid)
2938
    {
2939
        $sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2940
        $sql.= ' WHERE rowid = '.$lineid;
2941
        $resql = $this->db->query($sql);
2942
        if ($resql)
2943
        {
2944
            $row = $this->db->fetch_row($resql);
2945
            return $row[0];
2946
        }
2947
    }
2948
2949
    /**
2950
     *  Function to check if an object is used by others.
2951
     *  Check is done into this->childtables. There is no check into llx_element_element.
2952
     *
2953
     *  @param	int		$id			Force id of object
2954
     *  @return	int					<0 if KO, 0 if not used, >0 if already used
2955
     */
2956
    function isObjectUsed($id=0)
2957
    {
2958
        if (empty($id)) $id=$this->id;
2959
2960
        // Check parameters
2961
        if (! isset($this->childtables) || ! is_array($this->childtables) || count($this->childtables) == 0)
2962
        {
2963
            dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
2964
            return -1;
2965
        }
2966
2967
        // Test if child exists
2968
        $haschild=0;
2969
        foreach($this->childtables as $table)
2970
        {
2971
            // Check if third party can be deleted
2972
            $sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table;
2973
            $sql.= " WHERE ".$this->fk_element." = ".$id;
2974
            $resql=$this->db->query($sql);
2975
            if ($resql)
2976
            {
2977
                $obj=$this->db->fetch_object($resql);
2978
                $haschild+=$obj->nb;
2979
                //print 'Found into table '.$table;
2980
                if ($haschild) break;    // We found at least on, we stop here
2981
            }
2982
            else
2983
            {
2984
                $this->error=$this->db->lasterror();
2985
                return -1;
2986
            }
2987
        }
2988
        if ($haschild > 0)
2989
        {
2990
            $this->error="ErrorRecordHasChildren";
2991
            return $haschild;
2992
        }
2993
        else return 0;
2994
    }
2995
2996
    /**
2997
     *  Function to say how many lines object contains
2998
     *
2999
     *	@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
3000
     *  @return	int						<0 if KO, 0 if no predefined products, nb of lines with predefined products if found
3001
     */
3002
    function hasProductsOrServices($predefined=-1)
3003
    {
3004
        $nb=0;
3005
3006
        foreach($this->lines as $key => $val)
3007
        {
3008
            $qualified=0;
3009
            if ($predefined == -1) $qualified=1;
3010
            if ($predefined == 1 && $val->fk_product > 0) $qualified=1;
3011
            if ($predefined == 0 && $val->fk_product <= 0) $qualified=1;
3012
            if ($predefined == 2 && $val->fk_product > 0 && $val->product_type==0) $qualified=1;
3013
            if ($predefined == 3 && $val->fk_product > 0 && $val->product_type==1) $qualified=1;
3014
            if ($qualified) $nb++;
3015
        }
3016
        dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
3017
        return $nb;
3018
    }
3019
3020
    /**
3021
     * Function that returns the total amount HT of discounts applied for all lines.
3022
     *
3023
     * @return 	float
3024
     */
3025
    function getTotalDiscount()
3026
    {
3027
    	$total_discount=0.00;
3028
3029
        $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
3030
        $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element."det";
3031
        $sql.= " WHERE ".$this->fk_element." = ".$this->id;
3032
3033
        dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
3034
        $resql = $this->db->query($sql);
3035
        if ($resql)
3036
        {
3037
        	$num=$this->db->num_rows($resql);
3038
        	$i=0;
3039
        	while ($i < $num)
3040
        	{
3041
            	$obj = $this->db->fetch_object($resql);
3042
3043
            	$pu_ht = $obj->pu_ht;
3044
            	$qty= $obj->qty;
3045
            	$total_ht = $obj->total_ht;
3046
3047
        		$total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
3048
        		$total_discount += $total_discount_line;
3049
3050
        		$i++;
3051
        	}
3052
        }
3053
3054
        //print $total_discount; exit;
3055
        return price2num($total_discount);
3056
    }
3057
3058
3059
    /**
3060
     * Return into unit=0, the calculated total of weight and volume of all lines * qty
3061
     * Calculate by adding weight and volume of each product line, so properties ->volume/volume_units/weight/weight_units must be loaded on line.
3062
     *
3063
     * @return  array                           array('weight'=>...,'volume'=>...)
3064
     */
3065
    function getTotalWeightVolume()
3066
    {
3067
        $totalWeight = 0;
3068
        $totalVolume = 0;
3069
	    // defined for shipment only
3070
        $totalOrdered = '';
3071
	    // defined for shipment only
3072
        $totalToShip = '';
3073
3074
        foreach ($this->lines as $line)
3075
        {
3076
            if (isset($line->qty_asked))
3077
            {
3078
                if (empty($totalOrdered)) $totalOrdered=0;  // Avoid warning because $totalOrdered is ''
3079
                $totalOrdered+=$line->qty_asked;    // defined for shipment only
3080
            }
3081
            if (isset($line->qty_shipped))
3082
            {
3083
                if (empty($totalToShip)) $totalToShip=0;    // Avoid warning because $totalToShip is ''
3084
                $totalToShip+=$line->qty_shipped;   // defined for shipment only
3085
            }
3086
3087
	        // Define qty, weight, volume, weight_units, volume_units
3088
	        if ($this->element == 'shipping') {
3089
		        // for shipments
3090
		        $qty = $line->qty_shipped ? $line->qty_shipped : 0;
3091
	        }
3092
	        else {
3093
		        $qty = $line->qty ? $line->qty : 0;
3094
	        }
3095
3096
            $weight = $line->weight ? $line->weight : 0;
3097
            $volume = $line->volume ? $line->volume : 0;
3098
3099
            $weight_units=$line->weight_units;
3100
            $volume_units=$line->volume_units;
3101
3102
            $weightUnit=0;
3103
            $volumeUnit=0;
3104
            if (! empty($weight_units)) $weightUnit = $weight_units;
3105
            if (! empty($volume_units)) $volumeUnit = $volume_units;
3106
3107
            if (empty($totalWeight)) $totalWeight=0;  // Avoid warning because $totalWeight is ''
3108
            if (empty($totalVolume)) $totalVolume=0;  // Avoid warning because $totalVolume is ''
3109
3110
            //var_dump($line->volume_units);
3111
            if ($weight_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3112
            {
3113
                $trueWeightUnit=pow(10, $weightUnit);
3114
                $totalWeight += $weight * $qty * $trueWeightUnit;
3115
            }
3116
            else {
3117
		if ($weight_units == 99) {
3118
			// conversion 1 Pound = 0.45359237 KG
3119
			$trueWeightUnit = 0.45359237;
3120
			$totalWeight += $weight * $qty * $trueWeightUnit;
3121
		} elseif ($weight_units == 98) {
3122
			// conversion 1 Ounce = 0.0283495 KG
3123
			$trueWeightUnit = 0.0283495;
3124
			$totalWeight += $weight * $qty * $trueWeightUnit;
3125
		}
3126
		else
3127
                	$totalWeight += $weight * $qty;   // This may be wrong if we mix different units
3128
            }
3129
            if ($volume_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3130
            {
3131
                //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
3132
                $trueVolumeUnit=pow(10, $volumeUnit);
3133
                //print $line->volume;
3134
                $totalVolume += $volume * $qty * $trueVolumeUnit;
3135
            }
3136
            else
3137
            {
3138
                $totalVolume += $volume * $qty;   // This may be wrong if we mix different units
3139
            }
3140
        }
3141
3142
        return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
3143
    }
3144
3145
3146
    /**
3147
     *	Set extra parameters
3148
     *
3149
     *	@return	int      <0 if KO, >0 if OK
3150
     */
3151
    function setExtraParameters()
3152
    {
3153
    	$this->db->begin();
3154
3155
    	$extraparams = (! empty($this->extraparams) ? json_encode($this->extraparams) : null);
3156
3157
    	$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3158
    	$sql.= " SET extraparams = ".(! empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
3159
    	$sql.= " WHERE rowid = ".$this->id;
3160
3161
    	dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
3162
    	$resql = $this->db->query($sql);
3163
    	if (! $resql)
3164
    	{
3165
    		$this->error=$this->db->lasterror();
3166
    		$this->db->rollback();
3167
    		return -1;
3168
    	}
3169
    	else
3170
    	{
3171
    		$this->db->commit();
3172
    		return 1;
3173
    	}
3174
    }
3175
3176
3177
	/**
3178
     *    Return incoterms informations
3179
     *    TODO Use a cache for label get
3180
     *
3181
     *    @return	string	incoterms info
3182
     */
3183
    function display_incoterms()
3184
    {
3185
        $out = '';
3186
		$this->libelle_incoterms = '';
3187
		if (!empty($this->fk_incoterms))
3188
		{
3189
			$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3190
			$result = $this->db->query($sql);
3191
			if ($result)
3192
			{
3193
				$res = $this->db->fetch_object($result);
3194
				$out .= $res->code;
3195
			}
3196
		}
3197
3198
		$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...
3199
3200
		return $out;
3201
    }
3202
3203
	/**
3204
     *    Return incoterms informations for pdf display
3205
     *
3206
     *    @return	string		incoterms info
3207
     */
3208
	function getIncotermsForPDF()
3209
	{
3210
		$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3211
		$resql = $this->db->query($sql);
3212
		if ($resql)
3213
		{
3214
			$num = $this->db->num_rows($resql);
3215
			if ($num > 0)
3216
			{
3217
				$res = $this->db->fetch_object($resql);
3218
				return 'Incoterm : '.$res->code.' - '.$this->location_incoterms;
3219
			}
3220
			else
3221
			{
3222
				return '';
3223
			}
3224
		}
3225
		else
3226
		{
3227
            $this->errors[] = $this->db->lasterror();
3228
			return false;
3229
		}
3230
	}
3231
3232
	/**
3233
     *    Define incoterms values of current object
3234
     *
3235
     *    @param	int		$id_incoterm     Id of incoterm to set or '' to remove
3236
	 * 	  @param 	string  $location		 location of incoterm
3237
     *    @return	int     		<0 if KO, >0 if OK
3238
     */
3239
    function setIncoterms($id_incoterm, $location)
3240
    {
3241
        if ($this->id && $this->table_element)
3242
        {
3243
            $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3244
            $sql.= " SET fk_incoterms = ".($id_incoterm > 0 ? $id_incoterm : "null");
3245
			$sql.= ", location_incoterms = ".($id_incoterm > 0 ? "'".$this->db->escape($location)."'" : "null");
3246
            $sql.= " WHERE rowid = " . $this->id;
3247
			dol_syslog(get_class($this).'::setIncoterms', LOG_DEBUG);
3248
            $resql=$this->db->query($sql);
3249
            if ($resql)
3250
            {
3251
            	$this->fk_incoterms = $id_incoterm;
3252
				$this->location_incoterms = $location;
3253
3254
				$sql = 'SELECT libelle FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3255
				$res = $this->db->query($sql);
3256
				if ($res)
3257
				{
3258
					$obj = $this->db->fetch_object($res);
3259
					$this->libelle_incoterms = $obj->libelle;
3260
				}
3261
                return 1;
3262
            }
3263
            else
3264
			{
3265
                $this->errors[] = $this->db->lasterror();
3266
                return -1;
3267
            }
3268
        }
3269
        else return -1;
3270
    }
3271
3272
3273
    /**
3274
     *  Return if a country is inside the EEC (European Economic Community)
3275
     *  @deprecated
3276
     *
3277
     *  @return     boolean		true = country inside EEC, false = country outside EEC
3278
     */
3279
    function isInEEC()
3280
    {
3281
        require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
3282
        return isInEEC($this);
3283
    }
3284
3285
3286
    // --------------------
3287
    // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
3288
    // --------------------
3289
3290
    /* This is to show add lines */
3291
3292
    /**
3293
     *	Show add free and predefined products/services form
3294
     *
3295
     *  @param	int		        $dateSelector       1=Show also date range input fields
3296
     *  @param	Societe			$seller				Object thirdparty who sell
3297
     *  @param	Societe			$buyer				Object thirdparty who buy
3298
     *	@return	void
3299
     */
3300
	function formAddObjectLine($dateSelector,$seller,$buyer)
3301
	{
3302
		global $conf,$user,$langs,$object,$hookmanager;
3303
		global $form,$bcnd,$var;
3304
3305
		//Line extrafield
3306
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3307
		$extrafieldsline = new ExtraFields($this->db);
3308
		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3309
3310
		// Output template part (modules that overwrite templates must declare this into descriptor)
3311
        // Use global variables + $dateSelector + $seller and $buyer
3312
		$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3313
		foreach($dirtpls as $reldir)
3314
		{
3315
			$tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
3316
			if (empty($conf->file->strict_mode)) {
3317
				$res=@include $tpl;
3318
			} else {
3319
				$res=include $tpl; // for debug
3320
			}
3321
		    if ($res) break;
3322
		}
3323
    }
3324
3325
3326
3327
    /* This is to show array of line of details */
3328
3329
3330
	/**
3331
	 *	Return HTML table for object lines
3332
	 *	TODO Move this into an output class file (htmlline.class.php)
3333
	 *	If lines are into a template, title must also be into a template
3334
	 *	But for the moment we don't know if it'st possible as we keep a method available on overloaded objects.
3335
	 *
3336
	 *	@param	string		$action				Action code
3337
	 *	@param  string		$seller            	Object of seller third party
3338
	 *	@param  string  	$buyer             	Object of buyer third party
3339
	 *	@param	int			$selected		   	Object line selected
3340
	 *	@param  int	    	$dateSelector      	1=Show also date range input fields
3341
	 *	@return	void
3342
	 */
3343
	function printObjectLines($action, $seller, $buyer, $selected=0, $dateSelector=0)
3344
	{
3345
		global $conf, $hookmanager, $langs, $user;
3346
		// TODO We should not use global var for this !
3347
		global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
3348
3349
		// Define usemargins
3350
		$usemargins=0;
3351
		if (! empty($conf->margin->enabled) && ! empty($this->element) && in_array($this->element,array('facture','propal','commande'))) $usemargins=1;
3352
3353
		$num = count($this->lines);
3354
3355
		//Line extrafield
3356
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3357
		$extrafieldsline = new ExtraFields($this->db);
3358
		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3359
3360
		$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...
3361
		$reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3362
		if (empty($reshook))
3363
		{
3364
    		print '<tr class="liste_titre nodrag nodrop">';
3365
3366
    		if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) print '<td class="linecolnum" align="center" width="5">&nbsp;</td>';
3367
3368
    		// Description
3369
    		print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
3370
3371
    		if ($this->element == 'supplier_proposal')
3372
    		{
3373
    			print '<td class="linerefsupplier" align="right"><span id="title_fourn_ref">'.$langs->trans("SupplierProposalRefFourn").'</span></td>';
3374
    		}
3375
3376
    		// VAT
3377
    		print '<td class="linecolvat" align="right" width="80">'.$langs->trans('VAT').'</td>';
3378
3379
    		// Price HT
3380
    		print '<td class="linecoluht" align="right" width="80">'.$langs->trans('PriceUHT').'</td>';
3381
3382
    		// Multicurrency
3383
    		if (!empty($conf->multicurrency->enabled)) print '<td class="linecoluht_currency" align="right" width="80">'.$langs->trans('PriceUHTCurrency', $this->multicurrency_code).'</td>';
3384
3385
    		if ($inputalsopricewithtax) print '<td align="right" width="80">'.$langs->trans('PriceUTTC').'</td>';
3386
3387
    		// Qty
3388
    		print '<td class="linecolqty" align="right">'.$langs->trans('Qty').'</td>';
3389
3390
    		if($conf->global->PRODUCT_USE_UNITS)
3391
    		{
3392
    			print '<td class="linecoluseunit" align="left">'.$langs->trans('Unit').'</td>';
3393
    		}
3394
3395
    		// Reduction short
3396
    		print '<td class="linecoldiscount" align="right">'.$langs->trans('ReductionShort').'</td>';
3397
3398
    		if ($this->situation_cycle_ref) {
3399
    			print '<td class="linecolcycleref" align="right">' . $langs->trans('Progress') . '</td>';
3400
    		}
3401
3402
    		if ($usemargins && ! empty($conf->margin->enabled) && empty($user->societe_id))
3403
    		{
3404
    			if (!empty($user->rights->margins->creer))
3405
    			{
3406
    				if ($conf->global->MARGIN_TYPE == "1")
3407
    					print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('BuyingPrice').'</td>';
3408
    				else
3409
    					print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('CostPrice').'</td>';
3410
    			}
3411
3412
    			if (! empty($conf->global->DISPLAY_MARGIN_RATES) && $user->rights->margins->liretous)
3413
    				print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarginRate').'</td>';
3414
    			if (! empty($conf->global->DISPLAY_MARK_RATES) && $user->rights->margins->liretous)
3415
    				print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarkRate').'</td>';
3416
    		}
3417
3418
    		// Total HT
3419
    		print '<td class="linecolht" align="right">'.$langs->trans('TotalHTShort').'</td>';
3420
3421
    		// Multicurrency
3422
    		if (!empty($conf->multicurrency->enabled)) print '<td class="linecoltotalht_currency" align="right">'.$langs->trans('TotalHTShortCurrency', $this->multicurrency_code).'</td>';
3423
3424
            if ($outputalsopricetotalwithtax) print '<td align="right" width="80">'.$langs->trans('TotalTTCShort').'</td>';
3425
3426
    		print '<td class="linecoledit"></td>';  // No width to allow autodim
3427
3428
    		print '<td class="linecoldelete" width="10"></td>';
3429
3430
    		print '<td class="linecolmove" width="10"></td>';
3431
3432
    		print "</tr>\n";
3433
		}
3434
3435
		$var = true;
3436
		$i	 = 0;
3437
3438
		foreach ($this->lines as $line)
3439
		{
3440
			//Line extrafield
3441
			$line->fetch_optionals($line->id,$extralabelslines);
3442
3443
3444
3445
			//if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
3446
            if (is_object($hookmanager))   // Old code is commented on preceding line.
3447
			{
3448
				if (empty($line->fk_parent_line))
3449
				{
3450
					$parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
3451
                    $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3452
				}
3453
				else
3454
				{
3455
					$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);
3456
                    $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3457
				}
3458
			}
3459
            if (empty($reshook))
3460
			{
3461
				$this->printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected,$extrafieldsline);
3462
			}
3463
3464
			$i++;
3465
		}
3466
	}
3467
3468
	/**
3469
	 *	Return HTML content of a detail line
3470
	 *	TODO Move this into an output class file (htmlline.class.php)
3471
	 *
3472
	 *	@param	string		$action				GET/POST action
3473
	 *	@param CommonObjectLine $line		       	Selected object line to output
3474
	 *	@param  string	    $var               	Is it a an odd line (true)
3475
	 *	@param  int		    $num               	Number of line (0)
3476
	 *	@param  int		    $i					I
3477
	 *	@param  int		    $dateSelector      	1=Show also date range input fields
3478
	 *	@param  string	    $seller            	Object of seller third party
3479
	 *	@param  string	    $buyer             	Object of buyer third party
3480
	 *	@param	int			$selected		   	Object line selected
3481
	 *  @param  int			$extrafieldsline	Object of extrafield line attribute
3482
	 *	@return	void
3483
	 */
3484
	function printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected=0,$extrafieldsline=0)
3485
	{
3486
		global $conf,$langs,$user,$object,$hookmanager;
3487
		global $form,$bc,$bcdd;
3488
		global $object_rights, $disableedit, $disablemove;   // TODO We should not use global var for this !
3489
3490
		$object_rights = $this->getRights();
3491
3492
		$element=$this->element;
3493
3494
		$text=''; $description=''; $type=0;
3495
3496
		// Show product and description
3497
		$type=(! empty($line->product_type)?$line->product_type:$line->fk_product_type);
3498
		// Try to enhance type detection using date_start and date_end for free lines where type was not saved.
3499
		if (! empty($line->date_start)) $type=1; // deprecated
3500
		if (! empty($line->date_end)) $type=1; // deprecated
3501
3502
		// Ligne en mode visu
3503
		if ($action != 'editline' || $selected != $line->id)
3504
		{
3505
			// Product
3506
			if ($line->fk_product > 0)
3507
			{
3508
				$product_static = new Product($this->db);
3509
				$product_static->fetch($line->fk_product);
3510
3511
                $product_static->ref = $line->ref; //can change ref in hook
3512
                $product_static->label = $line->label; //can change label in hook
3513
				$text=$product_static->getNomUrl(1);
3514
3515
				// Define output language and label
3516
				if (! empty($conf->global->MAIN_MULTILANGS))
3517
				{
3518
					if (! is_object($this->thirdparty))
3519
					{
3520
						dol_print_error('','Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
3521
						return;
3522
					}
3523
3524
					$prod = new Product($this->db);
3525
					$prod->fetch($line->fk_product);
3526
3527
					$outputlangs = $langs;
3528
					$newlang='';
3529
					if (empty($newlang) && GETPOST('lang_id','aZ09')) $newlang=GETPOST('lang_id','aZ09');
3530
					if (! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang)) $newlang=$this->thirdparty->default_lang;		// For language to language of customer
3531
					if (! empty($newlang))
3532
					{
3533
						$outputlangs = new Translate("",$conf);
3534
						$outputlangs->setDefaultLang($newlang);
3535
					}
3536
3537
					$label = (! empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
3538
				}
3539
				else
3540
				{
3541
					$label = $line->product_label;
3542
				}
3543
3544
				$text.= ' - '.(! empty($line->label)?$line->label:$label);
3545
				$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.
3546
			}
3547
3548
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
3549
3550
			// Output template part (modules that overwrite templates must declare this into descriptor)
3551
			// Use global variables + $dateSelector + $seller and $buyer
3552
			$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3553
			foreach($dirtpls as $reldir)
3554
			{
3555
				$tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
3556
				if (empty($conf->file->strict_mode)) {
3557
					$res=@include $tpl;
3558
				} else {
3559
					$res=include $tpl; // for debug
3560
				}
3561
				if ($res) break;
3562
			}
3563
		}
3564
3565
		// Ligne en mode update
3566
		if ($this->statut == 0 && $action == 'editline' && $selected == $line->id)
3567
		{
3568
			$label = (! empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
3569
			if (! empty($conf->global->MAIN_HTML5_PLACEHOLDER)) $placeholder=' placeholder="'.$langs->trans("Label").'"';
3570
			else $placeholder=' title="'.$langs->trans("Label").'"';
3571
3572
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
3573
3574
			// Output template part (modules that overwrite templates must declare this into descriptor)
3575
			// Use global variables + $dateSelector + $seller and $buyer
3576
			$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3577
			foreach($dirtpls as $reldir)
3578
			{
3579
				$tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
3580
				if (empty($conf->file->strict_mode)) {
3581
					$res=@include $tpl;
3582
				} else {
3583
					$res=include $tpl; // for debug
3584
				}
3585
				if ($res) break;
3586
			}
3587
		}
3588
	}
3589
3590
3591
    /* This is to show array of line of details of source object */
3592
3593
3594
    /**
3595
     * 	Return HTML table table of source object lines
3596
     *  TODO Move this and previous function into output html class file (htmlline.class.php).
3597
     *  If lines are into a template, title must also be into a template
3598
     *  But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
3599
     *
3600
     *  @return	void
3601
     */
3602
    function printOriginLinesList()
3603
    {
3604
        global $langs, $hookmanager, $conf;
3605
3606
        print '<tr class="liste_titre">';
3607
        print '<td>'.$langs->trans('Ref').'</td>';
3608
        print '<td>'.$langs->trans('Description').'</td>';
3609
        print '<td align="right">'.$langs->trans('VATRate').'</td>';
3610
        print '<td align="right">'.$langs->trans('PriceUHT').'</td>';
3611
		if (!empty($conf->multicurrency->enabled)) print '<td align="right">'.$langs->trans('PriceUHTCurrency').'</td>';
3612
        print '<td align="right">'.$langs->trans('Qty').'</td>';
3613
	    if($conf->global->PRODUCT_USE_UNITS)
3614
	    {
3615
		    print '<td align="left">'.$langs->trans('Unit').'</td>';
3616
	    }
3617
        print '<td align="right">'.$langs->trans('ReductionShort').'</td></tr>';
3618
3619
        $var = true;
3620
        $i	 = 0;
3621
3622
        foreach ($this->lines as $line)
3623
        {
3624
3625
3626
            if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
3627
            {
3628
                if (empty($line->fk_parent_line))
3629
                {
3630
                    $parameters=array('line'=>$line,'var'=>$var,'i'=>$i);
3631
                    $action='';
3632
                    $hookmanager->executeHooks('printOriginObjectLine',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
3633
                }
3634
            }
3635
            else
3636
            {
3637
                $this->printOriginLine($line,$var);
3638
            }
3639
3640
            $i++;
3641
        }
3642
    }
3643
3644
    /**
3645
     * 	Return HTML with a line of table array of source object lines
3646
     *  TODO Move this and previous function into output html class file (htmlline.class.php).
3647
     *  If lines are into a template, title must also be into a template
3648
     *  But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
3649
     *
3650
     * 	@param	CommonObjectLine	$line		Line
3651
     * 	@param	string				$var		Var
3652
     * 	@return	void
3653
     */
3654
    function printOriginLine($line,$var)
3655
    {
3656
        global $langs, $conf;
3657
3658
        //var_dump($line);
3659
		if (!empty($line->date_start))
3660
		{
3661
			$date_start=$line->date_start;
3662
		}
3663
		else
3664
		{
3665
			$date_start=$line->date_debut_prevue;
3666
			if ($line->date_debut_reel) $date_start=$line->date_debut_reel;
3667
		}
3668
		if (!empty($line->date_end))
3669
		{
3670
			$date_end=$line->date_end;
3671
		}
3672
		else
3673
		{
3674
			$date_end=$line->date_fin_prevue;
3675
			if ($line->date_fin_reel) $date_end=$line->date_fin_reel;
3676
		}
3677
3678
        $this->tpl['label'] = '';
3679
        if (! empty($line->fk_parent_line)) $this->tpl['label'].= img_picto('', 'rightarrow');
3680
3681
        if (($line->info_bits & 2) == 2)  // TODO Not sure this is used for source object
3682
        {
3683
            $discount=new DiscountAbsolute($this->db);
3684
            $discount->fk_soc = $this->socid;
3685
            $this->tpl['label'].= $discount->getNomUrl(0,'discount');
3686
        }
3687
        else if (! empty($line->fk_product))
3688
        {
3689
            $productstatic = new Product($this->db);
3690
            $productstatic->id = $line->fk_product;
3691
            $productstatic->ref = $line->ref;
3692
            $productstatic->type = $line->fk_product_type;
3693
            $this->tpl['label'].= $productstatic->getNomUrl(1);
3694
            $this->tpl['label'].= ' - '.(! empty($line->label)?$line->label:$line->product_label);
3695
            // Dates
3696
            if ($line->product_type == 1 && ($date_start || $date_end))
3697
            {
3698
                $this->tpl['label'].= get_date_range($date_start,$date_end);
3699
            }
3700
        }
3701
        else
3702
        {
3703
            $this->tpl['label'].= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''),'service') : img_object($langs->trans(''),'product')));
3704
            if (!empty($line->desc)) {
3705
            	$this->tpl['label'].=$line->desc;
3706
            }else {
3707
            	$this->tpl['label'].= ($line->label ? '&nbsp;'.$line->label : '');
3708
            }
3709
            // Dates
3710
            if ($line->product_type == 1 && ($date_start || $date_end))
3711
            {
3712
                $this->tpl['label'].= get_date_range($date_start,$date_end);
3713
            }
3714
        }
3715
3716
        if (! empty($line->desc))
3717
        {
3718
            if ($line->desc == '(CREDIT_NOTE)')  // TODO Not sure this is used for source object
3719
            {
3720
                $discount=new DiscountAbsolute($this->db);
3721
                $discount->fetch($line->fk_remise_except);
3722
                $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote",$discount->getNomUrl(0));
3723
            }
3724
            elseif ($line->desc == '(DEPOSIT)')  // TODO Not sure this is used for source object
3725
            {
3726
                $discount=new DiscountAbsolute($this->db);
3727
                $discount->fetch($line->fk_remise_except);
3728
                $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit",$discount->getNomUrl(0));
3729
            }
3730
            elseif ($line->desc == '(EXCESS RECEIVED)')
3731
            {
3732
                $discount=new DiscountAbsolute($this->db);
3733
                $discount->fetch($line->fk_remise_except);
3734
                $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived",$discount->getNomUrl(0));
3735
            }
3736
            else
3737
            {
3738
                $this->tpl['description'] = dol_trunc($line->desc,60);
3739
            }
3740
        }
3741
        else
3742
        {
3743
            $this->tpl['description'] = '&nbsp;';
3744
        }
3745
3746
        // VAT Rate
3747
        $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
3748
        if (! empty($line->vat_src_code) && ! preg_match('/\(/', $this->tpl['vat_rate'])) $this->tpl['vat_rate'].=' ('.$line->vat_src_code.')';
3749
3750
        $this->tpl['price'] = price($line->subprice);
3751
		$this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
3752
        $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
3753
	    if($conf->global->PRODUCT_USE_UNITS) $this->tpl['unit'] = $line->getLabelOfUnit('long');
3754
        $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
3755
3756
        // Output template part (modules that overwrite templates must declare this into descriptor)
3757
        // Use global variables + $dateSelector + $seller and $buyer
3758
        $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3759
        foreach($dirtpls as $reldir)
3760
        {
3761
            $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
3762
            if (empty($conf->file->strict_mode)) {
3763
            	$res=@include $tpl;
3764
            } else {
3765
            	$res=include $tpl; // for debug
3766
            }
3767
            if ($res) break;
3768
        }
3769
    }
3770
3771
3772
	/**
3773
	 *	Add resources to the current object : add entry into llx_element_resources
3774
	 *	Need $this->element & $this->id
3775
	 *
3776
	 *	@param		int		$resource_id		Resource id
3777
	 *	@param		string	$resource_type		'resource'
3778
	 *	@param		int		$busy				Busy or not
3779
	 *	@param		int		$mandatory			Mandatory or not
3780
	 *	@return		int							<=0 if KO, >0 if OK
3781
	 */
3782
	function add_element_resource($resource_id, $resource_type, $busy=0, $mandatory=0)
3783
	{
3784
		$this->db->begin();
3785
3786
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources (";
3787
		$sql.= "resource_id";
3788
		$sql.= ", resource_type";
3789
		$sql.= ", element_id";
3790
		$sql.= ", element_type";
3791
		$sql.= ", busy";
3792
		$sql.= ", mandatory";
3793
		$sql.= ") VALUES (";
3794
		$sql.= $resource_id;
3795
		$sql.= ", '".$resource_type."'";
3796
		$sql.= ", '".$this->id."'";
3797
		$sql.= ", '".$this->element."'";
3798
		$sql.= ", '".$busy."'";
3799
		$sql.= ", '".$mandatory."'";
3800
		$sql.= ")";
3801
3802
		dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
3803
		if ($this->db->query($sql))
3804
		{
3805
			$this->db->commit();
3806
			return 1;
3807
		}
3808
		else
3809
		{
3810
			$this->error=$this->db->lasterror();
3811
			$this->db->rollback();
3812
			return  0;
3813
		}
3814
	}
3815
3816
	/**
3817
	 *    Delete a link to resource line
3818
	 *
3819
	 *    @param	int		$rowid			Id of resource line to delete
3820
	 *    @param	int		$element		element name (for trigger) TODO: use $this->element into commonobject class
3821
	 *    @param	int		$notrigger		Disable all triggers
3822
	 *    @return   int						>0 if OK, <0 if KO
3823
	 */
3824
	function delete_resource($rowid, $element, $notrigger=0)
3825
	{
3826
	    global $user;
3827
3828
	    $this->db->begin();
3829
3830
	    $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources";
3831
	    $sql.= " WHERE rowid=".$rowid;
3832
3833
	    dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
3834
3835
	    $resql=$this->db->query($sql);
3836
        if (! $resql)
3837
        {
3838
            $this->error=$this->db->lasterror();
3839
            $this->db->rollback();
3840
            return -1;
3841
        }
3842
        else
3843
        {
3844
            if (! $notrigger)
3845
            {
3846
                $result=$this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
3847
                if ($result < 0) { $this->db->rollback(); return -1; }
3848
            }
3849
            $this->db->commit();
3850
            return 1;
3851
        }
3852
	}
3853
3854
3855
	/**
3856
	 * Overwrite magic function to solve problem of cloning object that are kept as references
3857
	 *
3858
	 * @return void
3859
	 */
3860
	function __clone()
3861
    {
3862
        // Force a copy of this->lines, otherwise it will point to same object.
3863
        if (isset($this->lines) && is_array($this->lines))
3864
        {
3865
        	$nboflines=count($this->lines);
3866
        	for($i=0; $i < $nboflines; $i++)
3867
        	{
3868
            	$this->lines[$i] = clone $this->lines[$i];
3869
        	}
3870
        }
3871
    }
3872
3873
	/**
3874
	 * Common function for all objects extending CommonObject for generating documents
3875
	 *
3876
	 * @param 	string 		$modelspath 	Relative folder where generators are placed
3877
	 * @param 	string 		$modele 		Generator to use. Caller must set it to obj->modelpdf or GETPOST('modelpdf') for example.
3878
	 * @param 	Translate 	$outputlangs 	Language to use
3879
	 * @param 	int 		$hidedetails 	1 to hide details. 0 by default
3880
	 * @param 	int 		$hidedesc 		1 to hide product description. 0 by default
3881
	 * @param 	int 		$hideref 		1 to hide product reference. 0 by default
3882
	 * @param   null|array  $moreparams     Array to provide more information
3883
	 * @return 	int 						>0 if OK, <0 if KO
3884
	 */
3885
	protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
3886
	{
3887
		global $conf, $langs;
3888
3889
		$srctemplatepath='';
3890
3891
		// Increase limit for PDF build
3892
		$err=error_reporting();
3893
		error_reporting(0);
3894
		@set_time_limit(120);
3895
		error_reporting($err);
3896
3897
		// If selected model is a filename template (then $modele="modelname" or "modelname:filename")
3898
		$tmp=explode(':',$modele,2);
3899
		if (! empty($tmp[1]))
3900
		{
3901
			$modele=$tmp[0];
3902
			$srctemplatepath=$tmp[1];
3903
		}
3904
3905
3906
		// Search template files
3907
		$file=''; $classname=''; $filefound=0;
3908
		$dirmodels=array('/');
3909
		if (is_array($conf->modules_parts['models'])) $dirmodels=array_merge($dirmodels,$conf->modules_parts['models']);
3910
		foreach($dirmodels as $reldir)
3911
		{
3912
			foreach(array('doc','pdf') as $prefix)
3913
			{
3914
			    if (in_array(get_class($this), array('Adherent'))) $file = $prefix."_".$modele.".class.php";     // Member module use prefix_module.class.php
3915
				else $file = $prefix."_".$modele.".modules.php";
3916
3917
				// On verifie l'emplacement du modele
3918
				$file=dol_buildpath($reldir.$modelspath.$file,0);
3919
				if (file_exists($file))
3920
				{
3921
					$filefound=1;
3922
					$classname=$prefix.'_'.$modele;
3923
					break;
3924
				}
3925
			}
3926
			if ($filefound) break;
3927
		}
3928
3929
		// If generator was found
3930
		if ($filefound)
3931
		{
3932
			global $db;  // Required to solve a conception default in commonstickergenerator.class.php making an include of code using $db
3933
3934
			require_once $file;
3935
3936
			$obj = new $classname($this->db);
3937
3938
			// If generator is ODT, we must have srctemplatepath defined, if not we set it.
3939
			if ($obj->type == 'odt' && empty($srctemplatepath))
3940
			{
3941
				$varfortemplatedir=$obj->scandir;
3942
				if ($varfortemplatedir && ! empty($conf->global->$varfortemplatedir))
3943
				{
3944
					$dirtoscan=$conf->global->$varfortemplatedir;
3945
3946
					$listoffiles=array();
3947
3948
					// Now we add first model found in directories scanned
3949
	                $listofdir=explode(',',$dirtoscan);
3950
	                foreach($listofdir as $key=>$tmpdir)
3951
	                {
3952
	                    $tmpdir=trim($tmpdir);
3953
	                    $tmpdir=preg_replace('/DOL_DATA_ROOT/',DOL_DATA_ROOT,$tmpdir);
3954
	                    if (! $tmpdir) { unset($listofdir[$key]); continue; }
3955
	                    if (is_dir($tmpdir))
3956
	                    {
3957
	                        $tmpfiles=dol_dir_list($tmpdir,'files',0,'\.od(s|t)$','','name',SORT_ASC,0);
3958
	                        if (count($tmpfiles)) $listoffiles=array_merge($listoffiles,$tmpfiles);
3959
	                    }
3960
	                }
3961
3962
	                if (count($listoffiles))
3963
	                {
3964
	                	foreach($listoffiles as $record)
3965
	                    {
3966
	                    	$srctemplatepath=$record['fullname'];
3967
	                    	break;
3968
	                    }
3969
	                }
3970
				}
3971
3972
				if (empty($srctemplatepath))
3973
				{
3974
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
3975
					return -1;
3976
				}
3977
			}
3978
3979
            if ($obj->type == 'odt' && ! empty($srctemplatepath))
3980
            {
3981
                if (! dol_is_file($srctemplatepath))
3982
                {
3983
                    $this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
3984
                    return -1;
3985
                }
3986
            }
3987
3988
			// We save charset_output to restore it because write_file can change it if needed for
3989
			// output format that does not support UTF8.
3990
			$sav_charset_output=$outputlangs->charset_output;
3991
3992
			if (in_array(get_class($this), array('Adherent')))
3993
			{
3994
			    $arrayofrecords = array();   // The write_file of templates of adherent class need this
3995
			    $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams);
3996
			}
3997
			else $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
3998
3999
			if ($resultwritefile > 0)
4000
			{
4001
				$outputlangs->charset_output=$sav_charset_output;
4002
4003
				// We delete old preview
4004
				require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4005
				dol_delete_preview($this);
4006
4007
				// Success in building document. We build meta file.
4008
				dol_meta_create($this);
4009
4010
				return 1;
4011
			}
4012
			else
4013
			{
4014
				$outputlangs->charset_output=$sav_charset_output;
4015
				dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors);
4016
				return -1;
4017
			}
4018
4019
		}
4020
		else
4021
		{
4022
			$this->error=$langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists",$file);
4023
			dol_print_error('',$this->error);
4024
			return -1;
4025
		}
4026
	}
4027
4028
	/**
4029
	 *  Build thumb
4030
	 *
4031
	 *  @param      string	$file           Path file in UTF8 to original file to create thumbs from.
4032
	 *	@return		void
4033
	 */
4034
	function addThumbs($file)
4035
	{
4036
		global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality;
4037
4038
		require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';		// This define also $maxwidthsmall, $quality, ...
4039
4040
		$file_osencoded=dol_osencode($file);
4041
		if (file_exists($file_osencoded))
4042
		{
4043
			// Create small thumbs for company (Ratio is near 16/9)
4044
	        // Used on logon for example
4045
	        vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
4046
4047
	        // Create mini thumbs for company (Ratio is near 16/9)
4048
	        // Used on menu or for setup page for example
4049
	        vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
4050
		}
4051
	}
4052
4053
4054
	/* Functions common to commonobject and commonobjectline */
4055
4056
    /* For default values */
4057
4058
    /**
4059
     * Return the default value to use for a field when showing the create form of object.
4060
     * Return values in this order:
4061
     * 1) If parameter is available into POST, we return it first.
4062
     * 2) If not but an alternate value was provided as parameter of function, we return it.
4063
     * 3) If not but a constant $conf->global->OBJECTELEMENT_FIELDNAME is set, we return it (It is better to use the dedicated table).
4064
     * 4) Return value found into database (TODO No yet implemented)
4065
     *
4066
     * @param   string              $fieldname          Name of field
4067
     * @param   string              $alternatevalue     Alternate value to use
4068
     * @return  string|string[]                         Default value (can be an array if the GETPOST return an array)
4069
     **/
4070
	function getDefaultCreateValueFor($fieldname, $alternatevalue=null)
1 ignored issue
show
Coding Style introduced by
getDefaultCreateValueFor uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
4071
    {
4072
        global $conf, $_POST;
4073
4074
        // If param here has been posted, we use this value first.
4075
        if (isset($_POST[$fieldname])) return GETPOST($fieldname, 2);
4076
4077
        if (isset($alternatevalue)) return $alternatevalue;
4078
4079
        $newelement=$this->element;
4080
        if ($newelement == 'facture') $newelement='invoice';
4081
        if ($newelement == 'commande') $newelement='order';
4082
        if (empty($newelement))
4083
        {
4084
            dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
4085
            return '';
4086
        }
4087
4088
        $keyforfieldname=strtoupper($newelement.'_DEFAULT_'.$fieldname);
4089
        //var_dump($keyforfieldname);
4090
        if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname;
4091
4092
        // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
4093
4094
    }
4095
4096
4097
	/* For triggers */
4098
4099
4100
    /**
4101
     * Call trigger based on this instance.
4102
     * Some context information may also be provided into array property this->context.
4103
     * NB: Error from trigger are stacked in interface->errors
4104
     * NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction.
4105
     *
4106
     * @param   string    $trigger_name   trigger's name to execute
4107
     * @param   User      $user           Object user
4108
     * @return  int                       Result of run_triggers
4109
     */
4110
    function call_trigger($trigger_name, $user)
4111
    {
4112
    	global $langs,$conf;
4113
4114
    	include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
4115
    	$interface=new Interfaces($this->db);
4116
    	$result=$interface->run_triggers($trigger_name,$this,$user,$langs,$conf);
4117
4118
    	if ($result < 0)
4119
    	{
4120
    		if (!empty($this->errors))
4121
    		{
4122
    			$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.
4123
    		}
4124
    		else
4125
    		{
4126
    			$this->errors=$interface->errors;
4127
    		}
4128
    	}
4129
    	return $result;
4130
    }
4131
4132
4133
    /* Functions for extrafields */
4134
4135
4136
    /**
4137
     *  Function to get extra fields of a member into $this->array_options
4138
     *  This method is in most cases called by method fetch of objects but you can call it separately.
4139
     *
4140
     *  @param	int		$rowid			Id of line. Use the id of object if not defined. Deprecated. Function must be called without parameters.
4141
     *  @param  array	$optionsArray   Array resulting of call of extrafields->fetch_name_optionals_label(). Deprecated. Function must be called without parameters.
4142
     *  @return	int						<0 if error, 0 if no optionals to find nor found, 1 if a line is found and optional loaded
4143
     */
4144
    function fetch_optionals($rowid=null,$optionsArray=null)
4145
    {
4146
    	if (empty($rowid)) $rowid=$this->id;
4147
4148
        // To avoid SQL errors. Probably not the better solution though
4149
        if (!$this->table_element) {
4150
            return 0;
4151
        }
4152
4153
        if (! is_array($optionsArray))
4154
        {
4155
            // $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
4156
            // TODO Use of existing extrafield is not yet ready (must mutualize code that use extrafields in form first)
4157
            // global $extrafields;
4158
            //if (! is_object($extrafields))
4159
            //{
4160
                require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4161
                $extrafields = new ExtraFields($this->db);
4162
            //}
4163
4164
            // Load array of extrafields for elementype = $this->table_element
4165
            if (empty($extrafields->attributes[$this->table_element]['loaded']))
4166
            {
4167
                $extrafields->fetch_name_optionals_label($this->table_element);
4168
            }
4169
            $optionsArray = $extrafields->attributes[$this->table_element]['label'];
4170
        }
4171
4172
        $table_element = $this->table_element;
4173
        if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4174
4175
        // Request to get complementary values
4176
        if (count($optionsArray) > 0)
4177
        {
4178
            $sql = "SELECT rowid";
4179
            foreach ($optionsArray as $name => $label)
4180
            {
4181
                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...
4182
                {
4183
                    $sql.= ", ".$name;
4184
                }
4185
            }
4186
            $sql.= " FROM ".MAIN_DB_PREFIX.$table_element."_extrafields";
4187
            $sql.= " WHERE fk_object = ".$rowid;
4188
4189
            dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG);
4190
            $resql=$this->db->query($sql);
4191
            if ($resql)
4192
            {
4193
            	$numrows=$this->db->num_rows($resql);
4194
                if ($numrows)
4195
                {
4196
                    $tab = $this->db->fetch_array($resql);
4197
4198
                    foreach ($tab as $key => $value)
4199
                    {
4200
                    	// 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)
4201
                        if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && ! is_int($key))
4202
                        {
4203
                            // we can add this attribute to object
4204
                            $this->array_options["options_".$key]=$value;
4205
                        }
4206
                    }
4207
                }
4208
4209
                $this->db->free($resql);
4210
4211
                if ($numrows) return $numrows;
4212
                else return 0;
4213
            }
4214
            else
4215
            {
4216
                dol_print_error($this->db);
4217
                return -1;
4218
            }
4219
        }
4220
        return 0;
4221
    }
4222
4223
    /**
4224
     *	Delete all extra fields values for the current object.
4225
     *
4226
     *  @return	int		<0 if KO, >0 if OK
4227
     */
4228
	function deleteExtraFields()
4229
	{
4230
		$this->db->begin();
4231
4232
		$table_element = $this->table_element;
4233
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4234
4235
		$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
4236
		dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
4237
		$resql=$this->db->query($sql_del);
4238
		if (! $resql)
4239
		{
4240
			$this->error=$this->db->lasterror();
4241
			$this->db->rollback();
4242
			return -1;
4243
		}
4244
		else
4245
		{
4246
			$this->db->commit();
4247
			return 1;
4248
		}
4249
	}
4250
4251
    /**
4252
     *	Add/Update all extra fields values for the current object.
4253
     *  Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
4254
     *  This function delete record with all extrafields and insert them again from the array $this->array_options.
4255
     *
4256
     *  @return int -1=error, O=did nothing, 1=OK
4257
     */
4258
    function insertExtraFields()
4259
    {
4260
        global $conf,$langs;
4261
4262
		$error=0;
4263
4264
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
4265
4266
        if (! empty($this->array_options))
4267
        {
4268
            // Check parameters
4269
            $langs->load('admin');
4270
            require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4271
            $extrafields = new ExtraFields($this->db);
4272
            $target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
4273
4274
            //Eliminate copied source object extra_fields that do not exist in target object
4275
            $new_array_options=array();
4276
            foreach ($this->array_options as $key => $value) {
4277
                if (in_array(substr($key,8), array_keys($target_extrafields)))
4278
                    $new_array_options[$key] = $value;
4279
            }
4280
4281
            foreach($new_array_options as $key => $value)
4282
            {
4283
               	$attributeKey = substr($key,8);   // Remove 'options_' prefix
4284
               	$attributeType  = $extrafields->attribute_type[$attributeKey];
4285
               	$attributeLabel = $extrafields->attribute_label[$attributeKey];
4286
               	$attributeParam = $extrafields->attribute_param[$attributeKey];
4287
               	switch ($attributeType)
4288
               	{
4289
               		case 'int':
4290
              			if (!is_numeric($value) && $value!='')
4291
               			{
4292
               				$this->errors[]=$langs->trans("ExtraFieldHasWrongValue",$attributeLabel);
4293
               				return -1;
4294
              			}
4295
               			elseif ($value=='')
4296
               			{
4297
               				$this->array_options[$key] = null;
4298
               			}
4299
             			break;
4300
            		case 'price':
4301
            			$this->array_options[$key] = price2num($this->array_options[$key]);
4302
            			break;
4303
            		case 'date':
4304
            			$this->array_options[$key]=$this->db->idate($this->array_options[$key]);
4305
            			break;
4306
            		case 'datetime':
4307
            			$this->array_options[$key]=$this->db->idate($this->array_options[$key]);
4308
            			break;
4309
           			case 'link':
4310
						$param_list=array_keys($attributeParam ['options']);
4311
						// 0 : ObjectName
4312
						// 1 : classPath
4313
						$InfoFieldList = explode(":", $param_list[0]);
4314
						dol_include_once($InfoFieldList[1]);
4315
            			if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
4316
            			{
4317
    						$object = new $InfoFieldList[0]($this->db);
4318
    						if ($value)
4319
    						{
4320
    							if (is_numeric($value)) $res=$object->fetch($value);
4321
								else $res=$object->fetch('',$value);
4322
4323
    							if ($res > 0) $this->array_options[$key]=$object->id;
4324
    							else
4325
    							{
4326
    							    $this->error="Ref '".$value."' for object '".$object->element."' not found";
4327
                                    $this->db->rollback();
4328
                                    return -1;
4329
    							}
4330
    						}
4331
            			}
4332
            			else
4333
            			{
4334
            			    dol_syslog('Error bad setup of extrafield', LOG_WARNING);
4335
            			}
4336
						break;
4337
               	}
4338
            }
4339
            $this->db->begin();
4340
4341
            $table_element = $this->table_element;
4342
            if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4343
4344
            $sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
4345
            dol_syslog(get_class($this)."::insertExtraFields delete", LOG_DEBUG);
4346
            $this->db->query($sql_del);
4347
4348
            $sql = "INSERT INTO ".MAIN_DB_PREFIX.$table_element."_extrafields (fk_object";
4349
            foreach($new_array_options as $key => $value)
4350
            {
4351
            	$attributeKey = substr($key,8);   // Remove 'options_' prefix
4352
                // Add field of attribut
4353
            	if ($extrafields->attribute_type[$attributeKey] != 'separate') // Only for other type of separate
4354
                	$sql.=",".$attributeKey;
4355
            }
4356
            $sql .= ") VALUES (".$this->id;
4357
            foreach($new_array_options as $key => $value)
4358
            {
4359
            	$attributeKey = substr($key,8);   // Remove 'options_' prefix
4360
                // Add field o fattribut
4361
            	if($extrafields->attribute_type[$attributeKey] != 'separate') // Only for other type of separate)
4362
            	{
4363
	                if ($this->array_options[$key] != '')
4364
	                {
4365
	                    $sql.=",'".$this->db->escape($this->array_options[$key])."'";
4366
	                }
4367
	                else
4368
	                {
4369
	                    $sql.=",null";
4370
	                }
4371
            	}
4372
            }
4373
            $sql.=")";
4374
4375
            dol_syslog(get_class($this)."::insertExtraFields insert", LOG_DEBUG);
4376
            $resql = $this->db->query($sql);
4377
            if (! $resql)
4378
            {
4379
                $this->error=$this->db->lasterror();
4380
                $this->db->rollback();
4381
                return -1;
4382
            }
4383
            else
4384
            {
4385
                $this->db->commit();
4386
                return 1;
4387
            }
4388
        }
4389
        else return 0;
4390
    }
4391
4392
    /**
4393
     *	Update an exta field value for the current object.
4394
     *  Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
4395
     *  This function delte record with all extrafields and insert them again from the array $this->array_options.
4396
     *
4397
     *  @param  string      $key    Key of the extrafield
4398
     *  @return int                 -1=error, O=did nothing, 1=OK
4399
     */
4400
    function updateExtraField($key)
4401
    {
4402
        global $conf,$langs;
4403
4404
		$error=0;
4405
4406
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
4407
4408
        if (! empty($this->array_options) && isset($this->array_options["options_".$key]))
4409
        {
4410
            // Check parameters
4411
            $langs->load('admin');
4412
            require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4413
            $extrafields = new ExtraFields($this->db);
4414
            $target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
4415
4416
            $value=$this->array_options["options_".$key];
4417
            $attributeType  = $extrafields->attribute_type[$key];
4418
            $attributeLabel = $extrafields->attribute_label[$key];
4419
            $attributeParam = $extrafields->attribute_param[$key];
4420
            switch ($attributeType)
4421
            {
4422
                case 'int':
4423
                    if (!is_numeric($value) && $value!='')
4424
                    {
4425
                        $this->errors[]=$langs->trans("ExtraFieldHasWrongValue",$attributeLabel);
4426
                        return -1;
4427
                    }
4428
                    elseif ($value=='')
4429
                    {
4430
                        $this->array_options["options_".$key] = null;
4431
                    }
4432
                    break;
4433
                case 'price':
4434
                    $this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
4435
                    break;
4436
                case 'date':
4437
                    $this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
4438
                    break;
4439
                case 'datetime':
4440
                    $this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
4441
                    break;
4442
                case 'link':
4443
                    $param_list=array_keys($attributeParam ['options']);
4444
                    // 0 : ObjectName
4445
                    // 1 : classPath
4446
                    $InfoFieldList = explode(":", $param_list[0]);
4447
                    dol_include_once($InfoFieldList[1]);
4448
                    $object = new $InfoFieldList[0]($this->db);
4449
                    if ($value)
4450
                    {
4451
                        $object->fetch(0,$value);
4452
                        $this->array_options["options_".$key]=$object->id;
4453
                    }
4454
                    break;
4455
            }
4456
4457
            $this->db->begin();
4458
            $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key."='".$this->db->escape($this->array_options["options_".$key])."'";
4459
            $sql .= " WHERE fk_object = ".$this->id;
4460
            $resql = $this->db->query($sql);
4461
            if (! $resql)
4462
            {
4463
                $this->error=$this->db->lasterror();
4464
                $this->db->rollback();
4465
                return -1;
4466
            }
4467
            else
4468
            {
4469
                $this->db->commit();
4470
                return 1;
4471
            }
4472
        }
4473
        else return 0;
4474
    }
4475
4476
   /**
4477
     * Function to show lines of extrafields with output datas
4478
     *
4479
	 * @param Extrafields   $extrafields    Extrafield Object
4480
	 * @param string        $mode           Show output (view) or input (edit) for extrafield
4481
	 * @param array         $params         Optional parameters
4482
	 * @param string        $keyprefix      Prefix string to add into name and id of field (can be used to avoid duplicate names)
4483
     *
4484
     * @return string
4485
     */
4486
    function showOptionals($extrafields, $mode='view', $params=null, $keyprefix='')
1 ignored issue
show
Coding Style introduced by
showOptionals uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
4487
    {
4488
		global $_POST, $conf, $langs;
4489
4490
		$out = '';
4491
4492
		if (count($extrafields->attribute_label) > 0)
4493
		{
4494
			$out .= "\n";
4495
			$out .= '<!-- showOptionalsInput --> ';
4496
			$out .= "\n";
4497
4498
			$e = 0;
4499
			foreach($extrafields->attribute_label as $key=>$label)
4500
			{
4501
				if (is_array($params) && count($params)>0) {
4502
					if (array_key_exists('colspan',$params)) {
4503
						$colspan=$params['colspan'];
4504
					}
4505
				}else {
4506
					$colspan='3';
4507
				}
4508
				switch($mode) {
4509
					case "view":
4510
						$value=$this->array_options["options_".$key];
4511
						break;
4512
					case "edit":
4513
						if (isset($_POST["options_" . $key])) {
4514
							if (is_array($_POST["options_" . $key])) {
4515
								// $_POST["options"] is an array but following code expects a comma separated string
4516
								$value = implode(",", $_POST["options_" . $key]);
4517
							} else {
4518
								$value = $_POST["options_" . $key];
4519
							}
4520
						} else {
4521
							$value = $this->array_options["options_" . $key];
4522
						}
4523
						break;
4524
				}
4525
				if ($extrafields->attribute_type[$key] == 'separate')
4526
				{
4527
					$out .= $extrafields->showSeparator($key);
4528
				}
4529
				else
4530
				{
4531
					$csstyle='';
4532
					$class=(!empty($extrafields->attribute_hidden[$key]) ? 'class="hideobject" ' : '');
4533
					if (is_array($params) && count($params)>0) {
4534
						if (array_key_exists('style',$params)) {
4535
							$csstyle=$params['style'];
4536
						}
4537
					}
4538
					if ( !empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0)
4539
					{
4540
						$out .= '<tr '.$class.$csstyle.' class="'.$this->element.'_extras_'.$key.'">';
4541
						$colspan='0';
4542
					}
4543
					else
4544
					{
4545
						$out .= '<tr '.$class.$csstyle.' class="'.$this->element.'_extras_'.$key.'">';
4546
					}
4547
					// Convert date into timestamp format
4548
					if (in_array($extrafields->attribute_type[$key],array('date','datetime')))
4549
					{
4550
						$value = isset($_POST["options_".$key])?dol_mktime($_POST["options_".$key."hour"], $_POST["options_".$key."min"], 0, $_POST["options_".$key."month"], $_POST["options_".$key."day"], $_POST["options_".$key."year"]):$this->db->jdate($this->array_options['options_'.$key]);
4551
					}
4552
4553
					if($extrafields->attribute_required[$key])
4554
						$label = '<span'.($mode != 'view' ? ' class="fieldrequired"':'').'>'.$label.'</span>';
4555
4556
					$out .= '<td>'.$langs->trans($label).'</td>';
4557
					$html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
4558
					$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...
4559
4560
					switch($mode) {
4561
    					case "view":
4562
    						$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...
4563
    						break;
4564
    					case "edit":
4565
    						$out .= $extrafields->showInputField($key, $value, '', $keyprefix, '', 0, $this->id);
4566
    						break;
4567
					}
4568
4569
					$out .= '</td>';
4570
4571
					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>';
4572
					else $out .= '</tr>';
4573
					$e++;
4574
				}
4575
			}
4576
			$out .= "\n";
4577
			// Add code to manage list depending on others
4578
			if (! empty($conf->use_javascript_ajax))
4579
			$out .= '
4580
				<script type="text/javascript">
4581
				    jQuery(document).ready(function() {
4582
				    	function showOptions(child_list, parent_list)
4583
				    	{
4584
				    		var val = $("select[name=\"options_"+parent_list+"\"]").val();
4585
				    		var parentVal = parent_list + ":" + val;
4586
							if(val > 0) {
4587
					    		$("select[name=\""+child_list+"\"] option[parent]").hide();
4588
					    		$("select[name=\""+child_list+"\"] option[parent=\""+parentVal+"\"]").show();
4589
							} else {
4590
								$("select[name=\""+child_list+"\"] option").show();
4591
							}
4592
				    	}
4593
						function setListDependencies() {
4594
					    	jQuery("select option[parent]").parent().each(function() {
4595
					    		var child_list = $(this).attr("name");
4596
								var parent = $(this).find("option[parent]:first").attr("parent");
4597
								var infos = parent.split(":");
4598
								var parent_list = infos[0];
4599
								$("select[name=\""+parent_list+"\"]").change(function() {
4600
									showOptions(child_list, parent_list);
4601
								});
4602
					    	});
4603
						}
4604
4605
						setListDependencies();
4606
				    });
4607
				</script>'."\n";
4608
			$out .= '<!-- /showOptionalsInput --> '."\n";
4609
		}
4610
		return $out;
4611
	}
4612
4613
	/**
4614
	 * Returns the rights used for this class
4615
	 * @return stdClass
4616
	 */
4617
	public function getRights()
4618
	{
4619
		global $user;
4620
4621
		$element = $this->element;
4622
		if ($element == 'facturerec') $element='facture';
4623
4624
		return $user->rights->{$element};
4625
	}
4626
4627
	/**
4628
	 * Function used to replace a thirdparty id with another one.
4629
	 * This function is meant to be called from replaceThirdparty with the appropiate tables
4630
	 * Column name fk_soc MUST be used to identify thirdparties
4631
	 *
4632
	 * @param  DoliDB 	   $db 			  Database handler
4633
	 * @param  int 		   $origin_id     Old thirdparty id (the thirdparty to delete)
4634
	 * @param  int 		   $dest_id       New thirdparty id (the thirdparty that will received element of the other)
4635
	 * @param  string[]    $tables        Tables that need to be changed
4636
	 * @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)
4637
	 * @return bool
4638
	 */
4639
	public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
4640
	{
4641
		foreach ($tables as $table)
4642
		{
4643
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id;
4644
4645
			if (! $db->query($sql))
4646
			{
4647
			    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.
4648
				//$this->errors = $db->lasterror();
4649
			    return false;
4650
			}
4651
		}
4652
4653
		return true;
4654
	}
4655
4656
	/**
4657
	 * Get buy price to use for margin calculation. This function is called when buy price is unknown.
4658
	 *	 Set buy price = sell price if ForceBuyingPriceIfNull configured,
4659
	 *   else if calculation MARGIN_TYPE = 'costprice' and costprice is defined, use costprice as buyprice
4660
	 *	 else if calculation MARGIN_TYPE = 'pmp' and pmp is calculated, use pmp as buyprice
4661
	 *	 else set min buy price as buy price
4662
	 *
4663
	 * @param float		$unitPrice		 Product unit price
4664
	 * @param float		$discountPercent Line discount percent
4665
	 * @param int		$fk_product		 Product id
4666
	 * @return	float                    <0 if KO, buyprice if OK
4667
	 */
4668
	public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
4669
	{
4670
		global $conf;
4671
4672
		$buyPrice = 0;
4673
4674
		if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
4675
		{
4676
			$buyPrice = $unitPrice * (1 - $discountPercent / 100);
4677
		}
4678
		else
4679
		{
4680
			// Get cost price for margin calculation
4681
			if (! empty($fk_product))
4682
			{
4683
				if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice')
4684
				{
4685
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
4686
					$product = new Product($this->db);
4687
					$result = $product->fetch($fk_product);
4688
					if ($result <= 0)
4689
					{
4690
						$this->errors[] = 'ErrorProductIdDoesNotExists';
4691
						return -1;
4692
					}
4693
					if ($product->cost_price > 0)
4694
					{
4695
						$buyPrice = $product->cost_price;
4696
					}
4697
					else if ($product->pmp > 0)
4698
					{
4699
						$buyPrice = $product->pmp;
4700
					}
4701
				}
4702
				else if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')
4703
				{
4704
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
4705
					$product = new Product($this->db);
4706
					$result = $product->fetch($fk_product);
4707
					if ($result <= 0)
4708
					{
4709
						$this->errors[] = 'ErrorProductIdDoesNotExists';
4710
						return -1;
4711
					}
4712
					if ($product->pmp > 0)
4713
					{
4714
						$buyPrice = $product->pmp;
4715
					}
4716
				}
4717
4718
				if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1','pmp','costprice')))
4719
				{
4720
					require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
4721
					$productFournisseur = new ProductFournisseur($this->db);
4722
					if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0)
4723
					{
4724
						$buyPrice = $productFournisseur->fourn_unitprice;
4725
					}
4726
					else if ($result < 0)
4727
					{
4728
						$this->errors[] = $productFournisseur->error;
4729
						return -2;
4730
					}
4731
				}
4732
			}
4733
		}
4734
		return $buyPrice;
4735
	}
4736
4737
4738
4739
4740
	/**
4741
	 * Function test if type is date
4742
	 *
4743
	 * @param   array   $info   content informations of field
4744
	 * @return                  bool
4745
	 */
4746
	protected function isDate($info)
4747
	{
4748
		if(isset($info['type']) && ($info['type']=='date' || $info['type']=='datetime' || $info['type']=='timestamp')) return true;
4749
		else return false;
4750
	}
4751
4752
	/**
4753
	 * Function test if type is array
4754
	 *
4755
	 * @param   array   $info   content informations of field
4756
	 * @return                  bool
4757
	 */
4758
	protected function isArray($info)
4759
	{
4760
		if(is_array($info))
4761
		{
4762
			if(isset($info['type']) && $info['type']=='array') return true;
4763
			else return false;
4764
		}
4765
		else return false;
4766
	}
4767
4768
	/**
4769
	 * Function test if type is null
4770
	 *
4771
	 * @param   array   $info   content informations of field
4772
	 * @return                  bool
4773
	 */
4774
	protected function isNull($info)
4775
	{
4776
		if(is_array($info))
4777
		{
4778
			if(isset($info['type']) && $info['type']=='null') return true;
4779
			else return false;
4780
		}
4781
		else return false;
4782
	}
4783
4784
	/**
4785
	 * Function test if type is integer
4786
	 *
4787
	 * @param   array   $info   content informations of field
4788
	 * @return                  bool
4789
	 */
4790
	protected function isInt($info)
4791
	{
4792
		if(is_array($info))
4793
		{
4794
			if(isset($info['type']) && ($info['type']=='int' || $info['type']=='integer' )) return true;
4795
			else return false;
4796
		}
4797
		else return false;
4798
	}
4799
4800
	/**
4801
	 * Function test if type is float
4802
	 *
4803
	 * @param   array   $info   content informations of field
4804
	 * @return                  bool
4805
	 */
4806
	protected function isFloat($info)
4807
	{
4808
		if(is_array($info))
4809
		{
4810
			if(isset($info['type']) && $info['type']=='float') return true;
4811
			else return false;
4812
		}
4813
		else return false;
4814
	}
4815
4816
	/**
4817
	 * Function test if type is text
4818
	 *
4819
	 * @param   array   $info   content informations of field
4820
	 * @return                  bool
4821
	 */
4822
	protected function isText($info)
4823
	{
4824
		if(is_array($info))
4825
		{
4826
			if(isset($info['type']) && $info['type']=='text') return true;
4827
			else return false;
4828
		}
4829
		else return false;
4830
	}
4831
4832
	/**
4833
	 * Function test if is indexed
4834
	 *
4835
	 * @param   array   $info   content informations of field
4836
	 * @return                  bool
4837
	 */
4838
	protected function isIndex($info)
4839
	{
4840
		if(is_array($info))
4841
		{
4842
			if(isset($info['index']) && $info['index']==true) return true;
4843
			else return false;
4844
		}
4845
		else return false;
4846
	}
4847
4848
	/**
4849
	 * Function to prepare the values to insert
4850
	 *
4851
	 * @return array
4852
	 */
4853
	private function set_save_query()
4854
	{
4855
		$query=array();
4856
		foreach ($this->fields as $field=>$info)
4857
		{
4858
			if($this->isDate($info))
4859
			{
4860
				if(empty($this->{$field}))
4861
				{
4862
					$query[$field] = NULL;
4863
				}
4864
				else
4865
				{
4866
					$query[$field] = $this->db->idate($this->{$field});
4867
				}
4868
			}
4869
			else if($this->isArray($info))
4870
			{
4871
				$query[$field] = serialize($this->{$field});
4872
			}
4873
			else if($this->isInt($info))
4874
			{
4875
				$query[$field] = (int) price2num($this->{$field});
4876
			}
4877
			else if($this->isFloat($info))
4878
			{
4879
				$query[$field] = (double) price2num($this->{$field});
4880
			}
4881
			elseif($this->isNull($info))
4882
			{
4883
				$query[$field] = (is_null($this->{$field}) || (empty($this->{$field}) && $this->{$field}!==0 && $this->{$field}!=='0') ? null : $this->{$field});
4884
			}
4885
			else
4886
			{
4887
				$query[$field] = $this->{$field};
4888
			}
4889
		}
4890
4891
		return $query;
4892
	}
4893
4894
	/**
4895
	 * Function to load data into current object this
4896
	 *
4897
	 * @param   stdClass    $obj    Contain data of object from database
4898
	 */
4899
	private function set_vars_by_db(&$obj)
4900
	{
4901
	    foreach ($this->fields as $field => $info)
4902
	    {
4903
	        if($this->isDate($info))
4904
	        {
4905
	            if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
4906
	            else $this->{$field} = strtotime($obj->{$field});
4907
	        }
4908
	        elseif($this->isArray($info))
4909
	        {
4910
	            $this->{$field} = @unserialize($obj->{$field});
4911
	            // Hack for data not in UTF8
4912
	            if($this->{$field } === FALSE) @unserialize(utf8_decode($obj->{$field}));
4913
	        }
4914
	        elseif($this->isInt($info))
4915
	        {
4916
	            $this->{$field} = (int) $obj->{$field};
4917
	        }
4918
	        elseif($this->isFloat($info))
4919
	        {
4920
	            $this->{$field} = (double) $obj->{$field};
4921
	        }
4922
	        elseif($this->isNull($info))
4923
	        {
4924
	            $val = $obj->{$field};
4925
	            // zero is not null
4926
	            $this->{$field} = (is_null($val) || (empty($val) && $val!==0 && $val!=='0') ? null : $val);
4927
	        }
4928
	        else
4929
	        {
4930
	            $this->{$field} = $obj->{$field};
4931
	        }
4932
4933
	    }
4934
	}
4935
4936
	/**
4937
	 * Function to concat keys of fields
4938
	 *
4939
	 * @return string
4940
	 */
4941
	private function get_field_list()
4942
	{
4943
	    $keys = array_keys($this->fields);
4944
	    return implode(',', $keys);
4945
	}
4946
4947
	/**
4948
	 * Add quote to field value if necessary
4949
	 *
4950
	 * @param string|int	$value	value to protect
4951
	 * @return string|int
4952
	 */
4953
	protected function quote($value) {
4954
4955
	    if(is_null($value)) return 'NULL';
4956
	    else if(is_numeric($value)) return $value;
4957
	    else return "'".$this->db->escape( $value )."'";
4958
4959
	}
4960
4961
4962
	/**
4963
	 * Create object into database
4964
	 *
4965
	 * @param  User $user      User that creates
4966
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
4967
	 * @return int             <0 if KO, Id of created object if OK
4968
	 */
4969
	public function createCommon(User $user, $notrigger = false)
4970
	{
4971
        $error = 0;
4972
4973
	    $fields = array_merge(array('datec'=>$this->db->idate(dol_now())), $this->set_save_query());
4974
4975
	    $keys=array();
4976
	    $values = array();
4977
	    foreach ($fields as $k => $v) {
4978
	    	$keys[] = $k;
4979
	    	$values[] = $this->quote($v);
4980
	    }
4981
4982
	    $this->db->begin();
4983
4984
	    if (! $error)
4985
	    {
4986
    	    $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element.'
4987
    					( '.implode( ",", $keys ).' )
4988
    					VALUES ( '.implode( ",", $values ).' ) ';
4989
    	    $res = $this->db->query( $sql );
4990
    	    if ($res===false) {
4991
    	        $error++;
4992
    	        $this->errors[] = $this->db->lasterror();
4993
    	    }
4994
	    }
4995
4996
        if (! $error && ! $notrigger) {
4997
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
4998
4999
            if (!$notrigger) {
5000
                // Call triggers
5001
                $result=$this->call_trigger(strtoupper(get_class(self)).'_CREATE',$user);
5002
                if ($result < 0) { $error++; }
5003
                // End call triggers
5004
            }
5005
        }
5006
5007
		// Commit or rollback
5008
		if ($error) {
5009
			$this->db->rollback();
5010
			return -1;
5011
		} else {
5012
			$this->db->commit();
5013
			return $this->id;
5014
		}
5015
	}
5016
5017
	/**
5018
	 * Load an object from its id and create a new one in database
5019
	 *
5020
	 * @param  User $user      User that creates
5021
	 * @param  int $fromid     Id of object to clone
5022
	 * @return int             New id of clone
5023
	 */
5024
	public function createFromCloneCommon(User $user, $fromid)
5025
	{
5026
	    global $user;
5027
5028
	    $error = 0;
5029
5030
	    dol_syslog(__METHOD__, LOG_DEBUG);
5031
5032
	    $object = new self($this->db);
5033
5034
	    $this->db->begin();
5035
5036
	    // Load source object
5037
	    $object->fetchCommon($fromid);
5038
	    // Reset object
5039
	    $object->id = 0;
5040
5041
	    // Clear fields
5042
	    // ...
5043
5044
	    // Create clone
5045
	    $result = $object->createCommon($user);
5046
5047
	    // Other options
5048
	    if ($result < 0) {
5049
	        $error ++;
5050
	        $this->errors = $object->errors;
5051
	        dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
5052
	    }
5053
5054
	    // End
5055
	    if (!$error) {
5056
	        $this->db->commit();
5057
	        return $object->id;
5058
	    } else {
5059
	        $this->db->rollback();
5060
	        return -1;
5061
	    }
5062
	}
5063
5064
	/**
5065
	 * Load object in memory from the database
5066
	 *
5067
	 * @param int    $id   Id object
5068
	 * @param string $ref  Ref
5069
	 * @return int         <0 if KO, 0 if not found, >0 if OK
5070
	 */
5071
	public function fetchCommon($id, $ref = null)
5072
	{
5073
		if (empty($id) && empty($ref)) return false;
5074
5075
		$sql = 'SELECT '.$this->get_field_list().', datec, tms';
5076
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
5077
5078
		if(!empty($id)) $sql.= ' WHERE rowid = '.$id;
5079
		else  $sql.= ' WHERE ref = \''.$this->quote($ref).'\'';
5080
5081
		$res = $this->db->query($sql);
5082
		if ($res)
5083
		{
5084
    		if ($obj = $this->db->fetch_object($res))
5085
    		{
5086
    		    if ($obj)
5087
    		    {
5088
        			$this->id = $id;
5089
        			$this->set_vars_by_db($obj);
5090
5091
        			$this->datec = $this->db->idate($obj->datec);
5092
        			$this->tms = $this->db->idate($obj->tms);
5093
5094
        			return $this->id;
5095
    		    }
5096
    		    else
5097
    		    {
5098
    		        return 0;
5099
    		    }
5100
    		}
5101
    		else
5102
    		{
5103
    			$this->error = $this->db->lasterror();
5104
    			$this->errors[] = $this->error;
5105
    			return -1;
5106
    		}
5107
		}
5108
		else
5109
		{
5110
		    $this->error = $this->db->lasterror();
5111
		    $this->errors[] = $this->error;
5112
		    return -1;
5113
		}
5114
	}
5115
5116
	/**
5117
	 * Update object into database
5118
	 *
5119
	 * @param  User $user      User that modifies
5120
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
5121
	 * @return int             <0 if KO, >0 if OK
5122
	 */
5123
	public function updateCommon(User $user, $notrigger = false)
5124
	{
5125
	    $error = 0;
5126
5127
		$fields = $this->set_save_query();
5128
5129
		foreach ($fields as $k => $v) {
5130
			if (is_array($key)){
5131
				$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...
5132
				if ( $i !== false) {
5133
					$where[] = $key[$i].'=' . $this->quote( $v ) ;
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...
5134
					continue;
5135
				}
5136
			} else {
5137
				if ( $k == $key) {
5138
					$where[] = $k.'=' .$this->quote( $v ) ;
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...
5139
					continue;
5140
				}
5141
			}
5142
			$tmp[] = $k.'='.$this->quote($v);
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...
5143
		}
5144
		$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...
5145
5146
		$this->db->begin();
5147
5148
		if (! $error)
5149
		{
5150
    		$res = $this->db->query($sql);
5151
    		if ($res===false)
5152
    		{
5153
    		    $error++;
5154
    	        $this->errors[] = $this->db->lasterror();
5155
    		}
5156
		}
5157
5158
		if (! $error && ! $notrigger) {
5159
		    // Call triggers
5160
		    $result=$this->call_trigger(strtoupper(get_class(self)).'_MODIFY',$user);
5161
		    if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail
5162
		    // End call triggers
5163
		}
5164
5165
		// Commit or rollback
5166
		if ($error) {
5167
		    $this->db->rollback();
5168
		    return -1;
5169
		} else {
5170
		    $this->db->commit();
5171
		    return $this->id;
5172
		}
5173
	}
5174
5175
	/**
5176
	 * Delete object in database
5177
	 *
5178
	 * @param User $user       User that deletes
5179
	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
5180
	 * @return int             <0 if KO, >0 if OK
5181
	 */
5182
	public function deleteCommon(User $user, $notrigger = false)
5183
	{
5184
	    $error=0;
5185
5186
	    $this->db->begin();
5187
5188
	    if (! $error) {
5189
	        if (! $notrigger) {
5190
	            // Call triggers
5191
	            $result=$this->call_trigger(strtoupper(get_class(self)).'_DELETE', $user);
5192
	            if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail
5193
	            // End call triggers
5194
	        }
5195
	    }
5196
5197
	    if (! $error)
5198
	    {
5199
    		$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
5200
5201
    		$res = $this->db->query($sql);
5202
    		if($res===false) {
5203
    		    $error++;
5204
    		    $this->errors[] = $this->db->lasterror();
5205
    		}
5206
	    }
5207
5208
    	// Commit or rollback
5209
		if ($error) {
5210
		    $this->db->rollback();
5211
		    return -1;
5212
		} else {
5213
		    $this->db->commit();
5214
		    return 1;
5215
		}
5216
	}
5217
5218
	/**
5219
	 * Initialise object with example values
5220
	 * Id must be 0 if object instance is a specimen
5221
	 *
5222
	 * @return void
5223
	 */
5224
	public function initAsSpecimenCommon()
5225
	{
5226
	    $this->id = 0;
5227
5228
	    // TODO...
5229
	}
5230
5231
}
5232