Passed
Branch develop (5cbde9)
by
unknown
26:38
created

CommonObject::copy_linked_contact()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
c 0
b 0
f 0
nc 3
nop 2
dl 0
loc 13
rs 10
1
<?php
2
/* Copyright (C) 2006-2015 Laurent Destailleur  <[email protected]>
3
 * Copyright (C) 2005-2013 Regis Houssin        <[email protected]>
4
 * Copyright (C) 2010-2015 Juanjo Menent        <[email protected]>
5
 * Copyright (C) 2012-2013 Christophe Battarel  <[email protected]>
6
 * Copyright (C) 2011-2019 Philippe Grand       <[email protected]>
7
 * Copyright (C) 2012-2015 Marcos García        <[email protected]>
8
 * Copyright (C) 2012-2015 Raphaël Doursenaud   <[email protected]>
9
 * Copyright (C) 2012      Cedric Salvador      <[email protected]>
10
 * Copyright (C) 2015      Alexandre Spangaro   <[email protected]>
11
 * Copyright (C) 2016      Bahfir abbes         <[email protected]>
12
 * Copyright (C) 2017      ATM Consulting       <[email protected]>
13
 * Copyright (C) 2017-2019 Nicolas ZABOURI      <[email protected]>
14
 * Copyright (C) 2017      Rui Strecht		    <[email protected]>
15
 * Copyright (C) 2018      Frédéric France      <[email protected]>
16
 * Copyright (C) 2018      Josep Lluís Amador   <[email protected]>
17
 *
18
 * This program is free software; you can redistribute it and/or modify
19
 * it under the terms of the GNU General Public License as published by
20
 * the Free Software Foundation; either version 3 of the License, or
21
 * (at your option) any later version.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26
 * GNU General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU General Public License
29
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30
 */
31
32
/**
33
 *	\file       htdocs/core/class/commonobject.class.php
34
 *	\ingroup    core
35
 *	\brief      File of parent class of all other business classes (invoices, contracts, proposals, orders, ...)
36
 */
37
38
39
/**
40
 *	Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
41
 */
42
abstract class CommonObject
43
{
44
	/**
45
	 * @var DoliDb		Database handler (result of a new DoliDB)
46
	 */
47
	public $db;
48
49
	/**
50
	 * @var int The object identifier
51
	 */
52
	public $id;
53
54
	/**
55
	 * @var string 		Error string
56
	 * @see             $errors
57
	 */
58
	public $error;
59
60
	/**
61
	 * @var string[]	Array of error strings
62
	 */
63
	public $errors=array();
64
65
	/**
66
	 * @var string ID to identify managed object
67
	 */
68
	public $element;
69
70
	/**
71
	 * @var string Name of table without prefix where object is stored
72
	 */
73
	public $table_element;
74
75
	/**
76
	 * @var int    Name of subtable line
77
	 */
78
	public $table_element_line='';
79
80
	/**
81
	 * @var string		Key value used to track if data is coming from import wizard
82
	 */
83
	public $import_key;
84
85
	/**
86
	 * @var mixed		Contains data to manage extrafields
87
	 */
88
	public $array_options=array();
89
90
	/**
91
	 * @var int[][]		Array of linked objects ids. Loaded by ->fetchObjectLinked
92
	 */
93
	public $linkedObjectsIds;
94
95
	/**
96
	 * @var mixed		Array of linked objects. Loaded by ->fetchObjectLinked
97
	 */
98
	public $linkedObjects;
99
100
	/**
101
	 * @var Object      To store a cloned copy of object before to edit it and keep track of old properties
102
	 */
103
	public $oldcopy;
104
105
	/**
106
	 * @var string		Column name of the ref field.
107
	 */
108
	protected $table_ref_field = '';
109
110
111
112
	// Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
113
114
	/**
115
	 * @var array<string,mixed>		Can be used to pass information when only object is provided to method
116
	 */
117
	public $context=array();
118
119
	/**
120
	 * @var string		Contains canvas name if record is an alternative canvas record
121
	 */
122
	public $canvas;
123
124
	/**
125
	 * @var Project The related project
126
	 * @see fetch_projet()
127
	 */
128
	public $project;
129
130
	/**
131
	 * @var int The related project ID
132
	 * @see setProject(), project
133
	 */
134
	public $fk_project;
135
136
	/**
137
	 * @deprecated
138
	 * @see project
139
	 */
140
	public $projet;
141
142
	/**
143
	 * @var Contact a related contact
144
	 * @see fetch_contact()
145
	 */
146
	public $contact;
147
148
	/**
149
	 * @var int The related contact ID
150
	 * @see fetch_contact()
151
	 */
152
	public $contact_id;
153
154
	/**
155
	 * @var Societe A related thirdparty
156
	 * @see fetch_thirdparty()
157
	 */
158
	public $thirdparty;
159
160
	/**
161
	 * @var User A related user
162
	 * @see fetch_user()
163
	 */
164
	public $user;
165
166
	/**
167
	 * @var string 	The type of originating object ('commande', 'facture', ...)
168
	 * @see fetch_origin()
169
	 */
170
	public $origin;
171
172
	/**
173
	 * @var int 	The id of originating object
174
	 * @see fetch_origin()
175
	 */
176
	public $origin_id;
177
178
	/**
179
	 * @var string The object's reference
180
	 */
181
	public $ref;
182
183
	/**
184
	 * @var string The object's previous reference
185
	 */
186
	public $ref_previous;
187
188
	/**
189
	 * @var string The object's next reference
190
	 */
191
	public $ref_next;
192
193
	/**
194
	 * @var string An external reference for the object
195
	 */
196
	public $ref_ext;
197
198
	/**
199
	 * @var int The object's status
200
	 * @see setStatut()
201
	 */
202
	public $statut;
203
204
	/**
205
	 * @var string
206
	 * @see getFullAddress()
207
	 */
208
	public $country;
209
210
	/**
211
	 * @var int
212
	 * @see getFullAddress(), country
213
	 */
214
	public $country_id;
215
216
	/**
217
	 * @var string
218
	 * @see getFullAddress(), isInEEC(), country
219
	 */
220
    public $country_code;
221
222
    /**
223
	 * @var string
224
	 * @see getFullAddress()
225
	 */
226
	public $state;
227
228
	/**
229
	 * @var int
230
	 * @see getFullAddress(), state
231
	 */
232
	public $state_id;
233
234
	/**
235
	 * @var string
236
	 * @see getFullAddress(), state
237
	 */
238
    public $state_code;
239
240
    /**
241
	 * @var string
242
	 * @see getFullAddress(), region
243
	 */
244
	public $region;
245
246
	/**
247
	 * @var string
248
	 * @see getFullAddress(), region
249
	 */
250
    public $region_code;
251
252
	/**
253
	 * @var int
254
	 * @see fetch_barcode()
255
	 */
256
	public $barcode_type;
257
258
	/**
259
	 * @var string
260
	 * @see fetch_barcode(), barcode_type
261
	 */
262
	public $barcode_type_code;
263
264
	/**
265
	 * @var string
266
	 * @see fetch_barcode(), barcode_type
267
	 */
268
	public $barcode_type_label;
269
270
	/**
271
	 * @var string
272
	 * @see fetch_barcode(), barcode_type
273
	 */
274
	public $barcode_type_coder;
275
276
	/**
277
	 * @var int Payment method ID (cheque, cash, ...)
278
	 * @see setPaymentMethods()
279
	 */
280
	public $mode_reglement_id;
281
282
	/**
283
	 * @var int Payment terms ID
284
	 * @see setPaymentTerms()
285
	 */
286
	public $cond_reglement_id;
287
288
	/**
289
	 * @var int Payment terms ID
290
	 * @deprecated Kept for compatibility
291
	 * @see cond_reglement_id;
292
	 */
293
	public $cond_reglement;
294
295
	/**
296
	 * @var int Delivery address ID
297
	 * @deprecated
298
	 * @see setDeliveryAddress()
299
	 */
300
	public $fk_delivery_address;
301
302
	/**
303
	 * @var int Shipping method ID
304
	 * @see setShippingMethod()
305
	 */
306
	public $shipping_method_id;
307
308
	/**
309
	 * @var string
310
	 * @see SetDocModel()
311
	 */
312
	public $modelpdf;
313
314
	/**
315
	 * @var string
316
	 * Contains relative path of last generated main file
317
	 */
318
	public $last_main_doc;
319
320
	/**
321
	 * @var int Bank account ID
322
	 * @see SetBankAccount()
323
	 */
324
	public $fk_account;
325
326
	/**
327
	 * @var string Public note
328
	 * @see update_note()
329
	 */
330
	public $note_public;
331
332
	/**
333
	 * @var string Private note
334
	 * @see update_note()
335
	 */
336
	public $note_private;
337
338
	/**
339
	 * @deprecated
340
	 * @see $note_public
341
	 */
342
	public $note;
343
344
	/**
345
	 * @var float Total amount before taxes
346
	 * @see update_price()
347
	 */
348
	public $total_ht;
349
350
	/**
351
	 * @var float Total VAT amount
352
	 * @see update_price()
353
	 */
354
	public $total_tva;
355
356
	/**
357
	 * @var float Total local tax 1 amount
358
	 * @see update_price()
359
	 */
360
	public $total_localtax1;
361
362
	/**
363
	 * @var float Total local tax 2 amount
364
	 * @see update_price()
365
	 */
366
	public $total_localtax2;
367
368
	/**
369
	 * @var float Total amount with taxes
370
	 * @see update_price()
371
	 */
372
	public $total_ttc;
373
374
	/**
375
	 * @var CommonObjectLine[]
376
	 */
377
	public $lines;
378
379
	/**
380
	 * @var mixed		Contains comments
381
	 * @see fetchComments()
382
	 */
383
	public $comments=array();
384
385
	/**
386
	 * @var int
387
	 * @see setIncoterms()
388
	 */
389
	public $fk_incoterms;
390
391
	/**
392
	 * @var string
393
	 * @see SetIncoterms()
394
	 */
395
	public $label_incoterms;
396
397
	/**
398
	 * @var string
399
	 * @see display_incoterms()
400
	 */
401
	public $location_incoterms;
402
403
	/**
404
	 * @var string The name
405
	 */
406
	public $name;
407
408
    /**
409
     * @var string The lastname
410
     */
411
	public $lastname;
412
413
    /**
414
     * @var string The firstname
415
     */
416
	public $firstname;
417
418
    /**
419
     * @var string The civility code, not an integer
420
     */
421
	public $civility_id;
422
423
	// Dates
424
	public $date_creation;			// Date creation
425
	public $date_validation;		// Date validation
426
	public $date_modification;		// Date last change (tms field)
427
428
429
430
	// No constructor as it is an abstract class
431
432
	/**
433
	 * Check an object id/ref exists
434
	 * If you don't need/want to instantiate object and just need to know if object exists, use this method instead of fetch
435
	 *
436
	 *  @param	string	$element   	String of element ('product', 'facture', ...)
437
	 *  @param	int		$id      	Id of object
438
	 *  @param  string	$ref     	Ref of object to check
439
	 *  @param	string	$ref_ext	Ref ext of object to check
440
	 *  @return int     			<0 if KO, 0 if OK but not found, >0 if OK and exists
441
	 */
442
	public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
443
	{
444
		global $db,$conf;
445
446
		$sql = "SELECT rowid, ref, ref_ext";
447
		$sql.= " FROM ".MAIN_DB_PREFIX.$element;
448
		$sql.= " WHERE entity IN (".getEntity($element).")" ;
449
450
		if ($id > 0) $sql.= " AND rowid = ".$db->escape($id);
451
		elseif ($ref) $sql.= " AND ref = '".$db->escape($ref)."'";
452
		elseif ($ref_ext) $sql.= " AND ref_ext = '".$db->escape($ref_ext)."'";
453
		else {
454
			$error='ErrorWrongParameters';
455
			dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
456
			return -1;
457
		}
458
		if ($ref || $ref_ext) $sql.= " AND entity = ".$conf->entity;
459
460
		dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
461
		$resql = $db->query($sql);
462
		if ($resql)
463
		{
464
			$num=$db->num_rows($resql);
465
			if ($num > 0) return 1;
466
			else return 0;
467
		}
468
		return -1;
469
	}
470
471
	/**
472
	 * Method to output saved errors
473
	 *
474
	 * @return	string		String with errors
475
	 */
476
	public function errorsToString()
477
	{
478
		return $this->error.(is_array($this->errors)?(($this->error!=''?', ':'').join(', ', $this->errors)):'');
1 ignored issue
show
introduced by
The condition is_array($this->errors) is always true.
Loading history...
479
	}
480
481
	/**
482
	 * Return customer ref for screen output.
483
	 *
484
	 * @param  string      $objref        Customer ref
485
	 * @return string                     Customer ref formated
486
	 */
487
	public function getFormatedCustomerRef($objref)
488
	{
489
	    global $hookmanager;
490
491
	    $parameters=array('objref'=>$objref);
492
	    $action='';
493
	    $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
494
	    if ($reshook > 0)
495
	    {
496
	        return $hookmanager->resArray['objref'];
497
	    }
498
	    return $objref.(isset($hookmanager->resArray['objref'])?$hookmanager->resArray['objref']:'');
499
	}
500
501
	/**
502
	 * Return supplier ref for screen output.
503
	 *
504
	 * @param  string      $objref        Supplier ref
505
	 * @return string                     Supplier ref formated
506
	 */
507
	public function getFormatedSupplierRef($objref)
508
	{
509
	    global $hookmanager;
510
511
	    $parameters=array('objref'=>$objref);
512
	    $action='';
513
	    $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
514
	    if ($reshook > 0)
515
	    {
516
	        return $hookmanager->resArray['objref'];
517
	    }
518
	    return $objref.(isset($hookmanager->resArray['objref'])?$hookmanager->resArray['objref']:'');
519
	}
520
521
	/**
522
	 *	Return full name (civility+' '+name+' '+lastname)
523
	 *
524
	 *	@param	Translate	$langs			Language object for translation of civility (used only if option is 1)
525
	 *	@param	int			$option			0=No option, 1=Add civility
526
	 * 	@param	int			$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname, 3=Firstname if defined else lastname
527
	 * 	@param	int			$maxlen			Maximum length
528
	 * 	@return	string						String with full name
529
	 */
530
	public function getFullName($langs, $option = 0, $nameorder = -1, $maxlen = 0)
531
	{
532
		//print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n";
533
		$lastname=$this->lastname;
534
		$firstname=$this->firstname;
535
		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:'')))));
536
537
		$ret='';
538
		if ($option && $this->civility_id)
539
		{
540
			if ($langs->transnoentitiesnoconv("Civility".$this->civility_id)!="Civility".$this->civility_id) $ret.=$langs->transnoentitiesnoconv("Civility".$this->civility_id).' ';
541
			else $ret.=$this->civility_id.' ';
542
		}
543
544
		$ret.=dolGetFirstLastname($firstname, $lastname, $nameorder);
545
546
		return dol_trunc($ret, $maxlen);
547
	}
548
549
	/**
550
	 * 	Return full address of contact
551
	 *
552
	 * 	@param		int			$withcountry		1=Add country into address string
553
	 *  @param		string		$sep				Separator to use to build string
554
	 *  @param		int		    $withregion			1=Add region into address string
555
	 *	@return		string							Full address string
556
	 */
557
	public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0)
558
	{
559
		if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country)))
560
		{
561
			require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
562
			$tmparray=getCountry($this->country_id, 'all');
563
			$this->country_code=$tmparray['code'];
564
			$this->country     =$tmparray['label'];
565
		}
566
567
        if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_cpde)))
0 ignored issues
show
Bug introduced by
The property region_cpde does not exist on CommonObject. Did you mean region?
Loading history...
568
    	{
569
    		require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
570
    		$tmparray=getState($this->state_id, 'all', 0, 1);
571
			$this->state_code   =$tmparray['code'];
572
			$this->state        =$tmparray['label'];
573
			$this->region_code  =$tmparray['region_code'];
574
			$this->region       =$tmparray['region'];
575
        }
576
577
		return dol_format_address($this, $withcountry, $sep);
578
	}
579
580
581
	/**
582
	 * 	Return full address for banner
583
	 *
584
	 * 	@param		string		$htmlkey            HTML id to make banner content unique
585
	 *  @param      Object      $object				Object (thirdparty, thirdparty of contact for contact, null for a member)
586
	 *	@return		string							Full address string
587
	 */
588
	public function getBannerAddress($htmlkey, $object)
589
	{
590
		global $conf, $langs;
591
592
		$countriesusingstate=array('AU','US','IN','GB','ES','UK','TR');    // See also option MAIN_FORCE_STATE_INTO_ADDRESS
593
594
		$contactid=0;
595
		$thirdpartyid=0;
596
		if ($this->element == 'societe')
597
		{
598
			$thirdpartyid=$this->id;
599
		}
600
		if ($this->element == 'contact')
601
		{
602
			$contactid=$this->id;
603
			$thirdpartyid=$object->fk_soc;
604
		}
605
		if ($this->element == 'user')
606
		{
607
			$contactid=$this->contact_id;
608
			$thirdpartyid=$object->fk_soc;
609
		}
610
611
		$out='<!-- BEGIN part to show address block -->';
612
613
		$outdone=0;
614
		$coords = $this->getFullAddress(1, ', ', $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT);
615
		if ($coords)
616
		{
617
			if (! empty($conf->use_javascript_ajax))
618
			{
619
				$namecoords = $this->getFullName($langs, 1).'<br>'.$coords;
620
				// hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile
621
				$out.='<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">';
622
				$out.=img_picto($langs->trans("Address"), 'object_address.png');
623
				$out.='</a> ';
624
			}
625
			$out.=dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); $outdone++;
626
			$outdone++;
627
		}
628
629
		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
630
				&& empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state)
631
		{
632
            if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) {
633
                $out.=($outdone?' - ':'').$this->region.' - '.$this->state;
634
            }
635
            else {
636
                $out.=($outdone?' - ':'').$this->state;
637
            }
638
			$outdone++;
639
		}
640
641
		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>':'');
642
		if (! empty($this->phone) && empty($this->phone_pro)) {		// For objects that store pro phone into ->phone
643
			$out.=dol_print_phone($this->phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro")); $outdone++;
644
		}
645
		if (! empty($this->phone_pro)) {
646
			$out.=dol_print_phone($this->phone_pro, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro")); $outdone++;
647
		}
648
		if (! empty($this->phone_mobile)) {
649
			$out.=dol_print_phone($this->phone_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'mobile', $langs->trans("PhoneMobile")); $outdone++;
650
		}
651
		if (! empty($this->phone_perso)) {
652
			$out.=dol_print_phone($this->phone_perso, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePerso")); $outdone++;
653
		}
654
		if (! empty($this->office_phone)) {
655
			$out.=dol_print_phone($this->office_phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro")); $outdone++;
656
		}
657
		if (! empty($this->user_mobile)) {
658
			$out.=dol_print_phone($this->user_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'mobile', $langs->trans("PhoneMobile")); $outdone++;
659
		}
660
		if (! empty($this->fax)) {
661
			$out.=dol_print_phone($this->fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', '&nbsp;', 'fax', $langs->trans("Fax")); $outdone++;
662
		}
663
		if (! empty($this->office_fax)) {
664
			$out.=dol_print_phone($this->office_fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', '&nbsp;', 'fax', $langs->trans("Fax")); $outdone++;
665
		}
666
667
		$out.='<div style="clear: both;"></div>';
668
		$outdone=0;
669
		if (! empty($this->email))
670
		{
671
			$out.=dol_print_email($this->email, $this->id, $object->id, 'AC_EMAIL', 0, 0, 1);
672
			$outdone++;
673
		}
674
		if (! empty($this->url))
675
		{
676
            //$out.=dol_print_url($this->url,'_goout',0,1);//steve changed to blank
677
		    $out.=dol_print_url($this->url, '_blank', 0, 1);
678
			$outdone++;
679
		}
680
		$out.='<div style="clear: both;">';
681
		if (! empty($conf->socialnetworks->enabled))
682
		{
683
			if ($this->skype) $out.=dol_print_socialnetworks($this->skype, $this->id, $object->id, 'skype');
684
			$outdone++;
685
			if ($this->jabberid) $out.=dol_print_socialnetworks($this->jabberid, $this->id, $object->id, 'jabber');
686
			$outdone++;
687
			if ($this->twitter) $out.=dol_print_socialnetworks($this->twitter, $this->id, $object->id, 'twitter');
688
			$outdone++;
689
			if ($this->facebook) $out.=dol_print_socialnetworks($this->facebook, $this->id, $object->id, 'facebook');
690
			$outdone++;
691
			if ($this->linkedin) $out.=dol_print_socialnetworks($this->linkedin, $this->id, $object->id, 'linkedin');
692
			$outdone++;
693
		}
694
		$out.='</div>';
695
696
		$out.='<!-- END Part to show address block -->';
697
698
		return $out;
699
	}
700
701
	/**
702
	 * Return the link of last main doc file for direct public download.
703
	 *
704
	 * @param	string	$modulepart			Module related to document
705
	 * @param	int		$initsharekey		Init the share key if it was not yet defined
706
	 * @param	int		$relativelink		0=Return full external link, 1=Return link relative to root of file
707
	 * @return	string						Link or empty string if there is no download link
708
	 */
709
	public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
710
	{
711
		global $user, $dolibarr_main_url_root;
712
713
		if (empty($this->last_main_doc))
714
		{
715
			return '';		// No way to known which document name to use
716
		}
717
718
		include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
719
		$ecmfile=new EcmFiles($this->db);
720
		$result = $ecmfile->fetch(0, '', $this->last_main_doc);
721
		if ($result < 0)
722
		{
723
			$this->error = $ecmfile->error;
724
			$this->errors = $ecmfile->errors;
725
			return -1;
726
		}
727
728
		if (empty($ecmfile->id))
729
		{
730
			// Add entry into index
731
			if ($initsharekey)
732
			{
733
				require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
734
				// TODO We can't, we dont' have full path of file, only last_main_doc adn ->element, so we must rebuild full path first
735
				/*
736
				$ecmfile->filepath = $rel_dir;
737
				$ecmfile->filename = $filename;
738
				$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
739
				$ecmfile->fullpath_orig = '';
740
				$ecmfile->gen_or_uploaded = 'generated';
741
				$ecmfile->description = '';    // indexed content
742
				$ecmfile->keyword = '';        // keyword content
743
				$ecmfile->share = getRandomPassword(true);
744
				$result = $ecmfile->create($user);
745
				if ($result < 0)
746
				{
747
					$this->error = $ecmfile->error;
748
					$this->errors = $ecmfile->errors;
749
				}
750
				*/
751
			}
752
			else return '';
753
		}
754
		elseif (empty($ecmfile->share))
755
		{
756
			// Add entry into index
757
			if ($initsharekey)
758
			{
759
				require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
760
				$ecmfile->share = getRandomPassword(true);
761
				$ecmfile->update($user);
762
			}
763
			else return '';
764
		}
765
		// Define $urlwithroot
766
		$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
767
		// This is to use external domain name found into config file
768
		//if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
769
		//else
770
		$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;
771
		//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
772
773
		$forcedownload=0;
774
775
		$paramlink='';
776
		//if (! empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart;		// For sharing with hash (so public files), modulepart is not required.
777
		//if (! empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; 					// For sharing with hash (so public files), entity is not required.
778
		//$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath);								// No need of name of file for public link, we will use the hash
779
		if (! empty($ecmfile->share)) $paramlink.=($paramlink?'&':'').'hashp='.$ecmfile->share;			// Hash for public share
780
		if ($forcedownload) $paramlink.=($paramlink?'&':'').'attachment=1';
781
782
		if ($relativelink)
783
		{
784
			$linktoreturn='document.php'.($paramlink?'?'.$paramlink:'');
785
		}
786
		else
787
		{
788
			$linktoreturn=$urlwithroot.'/document.php'.($paramlink?'?'.$paramlink:'');
789
		}
790
791
		// Here $ecmfile->share is defined
792
		return $linktoreturn;
793
	}
794
795
796
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
797
	/**
798
	 *  Add a link between element $this->element and a contact
799
	 *
800
	 *  @param	int		$fk_socpeople       Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
801
	 *  @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
802
	 *  @param  string	$source             external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
803
	 *  @param  int		$notrigger			Disable all triggers
804
	 *  @return int                 		<0 if KO, >0 if OK
805
	 */
806
	public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
807
	{
808
        // phpcs:enable
809
		global $user,$langs;
810
811
812
		dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
813
814
		// Check parameters
815
		if ($fk_socpeople <= 0)
816
		{
817
			$langs->load("errors");
818
			$this->error=$langs->trans("ErrorWrongValueForParameterX", "1");
819
			dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
820
			return -1;
821
		}
822
		if (! $type_contact)
823
		{
824
			$langs->load("errors");
825
			$this->error=$langs->trans("ErrorWrongValueForParameterX", "2");
826
			dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
827
			return -2;
828
		}
829
830
		$id_type_contact=0;
831
		if (is_numeric($type_contact))
1 ignored issue
show
introduced by
The condition is_numeric($type_contact) is always true.
Loading history...
832
		{
833
			$id_type_contact=$type_contact;
834
		}
835
		else
836
		{
837
			// We look for id type_contact
838
			$sql = "SELECT tc.rowid";
839
			$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
840
			$sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
841
			$sql.= " AND tc.source='".$this->db->escape($source)."'";
842
			$sql.= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
843
			//print $sql;
844
			$resql=$this->db->query($sql);
845
			if ($resql)
846
			{
847
				$obj = $this->db->fetch_object($resql);
848
				if ($obj) $id_type_contact=$obj->rowid;
849
			}
850
		}
851
852
		if ($id_type_contact == 0)
853
		{
854
			$this->error='CODE_NOT_VALID_FOR_THIS_ELEMENT';
855
			dol_syslog("CODE_NOT_VALID_FOR_THIS_ELEMENT: Code type of contact '".$type_contact."' does not exists or is not active for element ".$this->element.", we can ignore it");
856
			return -3;
857
		}
858
859
		$datecreate = dol_now();
860
861
		// Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
862
		$TListeContacts=$this->liste_contact(-1, $source);
863
		$already_added=false;
864
		if(!empty($TListeContacts)) {
865
			foreach($TListeContacts as $array_contact) {
0 ignored issues
show
Bug introduced by
The expression $TListeContacts of type integer is not traversable.
Loading history...
866
				if($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
867
					$already_added=true;
868
					break;
869
				}
870
			}
871
		}
872
873
		if(!$already_added) {
874
875
			$this->db->begin();
876
877
			// Insert into database
878
			$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact";
879
			$sql.= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
880
			$sql.= " VALUES (".$this->id.", ".$fk_socpeople." , " ;
881
			$sql.= "'".$this->db->idate($datecreate)."'";
882
			$sql.= ", 4, ". $id_type_contact;
883
			$sql.= ")";
884
885
			$resql=$this->db->query($sql);
886
			if ($resql)
887
			{
888
				if (! $notrigger)
889
				{
890
					$result=$this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
891
					if ($result < 0)
892
					{
893
						$this->db->rollback();
894
						return -1;
895
					}
896
				}
897
898
				$this->db->commit();
899
				return 1;
900
			}
901
			else
902
			{
903
				if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
904
				{
905
					$this->error=$this->db->errno();
906
					$this->db->rollback();
907
					echo 'err rollback';
908
					return -2;
909
				}
910
				else
911
				{
912
					$this->error=$this->db->error();
913
					$this->db->rollback();
914
					return -1;
915
				}
916
			}
917
		} else return 0;
918
	}
919
920
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
921
	/**
922
	 *    Copy contact from one element to current
923
	 *
924
	 *    @param    CommonObject    $objFrom    Source element
925
	 *    @param    string          $source     Nature of contact ('internal' or 'external')
926
	 *    @return   int                         >0 if OK, <0 if KO
927
	 */
928
	public function copy_linked_contact($objFrom, $source = 'internal')
929
	{
930
        // phpcs:enable
931
		$contacts = $objFrom->liste_contact(-1, $source);
932
		foreach($contacts as $contact)
0 ignored issues
show
Bug introduced by
The expression $contacts of type integer is not traversable.
Loading history...
933
		{
934
			if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0)
935
			{
936
				$this->error=$this->db->lasterror();
937
				return -1;
938
			}
939
		}
940
		return 1;
941
	}
942
943
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
944
	/**
945
	 *      Update a link to contact line
946
	 *
947
	 *      @param	int		$rowid              Id of line contact-element
948
	 * 		@param	int		$statut	            New status of link
949
	 *      @param  int		$type_contact_id    Id of contact type (not modified if 0)
950
	 *      @param  int		$fk_socpeople	    Id of soc_people to update (not modified if 0)
951
	 *      @return int                 		<0 if KO, >= 0 if OK
952
	 */
953
	public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
954
	{
955
        // phpcs:enable
956
		// Insert into database
957
		$sql = "UPDATE ".MAIN_DB_PREFIX."element_contact set";
958
		$sql.= " statut = ".$statut;
959
		if ($type_contact_id) $sql.= ", fk_c_type_contact = '".$type_contact_id ."'";
960
		if ($fk_socpeople) $sql.= ", fk_socpeople = '".$fk_socpeople ."'";
961
		$sql.= " where rowid = ".$rowid;
962
		$resql=$this->db->query($sql);
963
		if ($resql)
964
		{
965
			return 0;
966
		}
967
		else
968
		{
969
			$this->error=$this->db->lasterror();
970
			return -1;
971
		}
972
	}
973
974
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
975
	/**
976
	 *    Delete a link to contact line
977
	 *
978
	 *    @param	int		$rowid			Id of contact link line to delete
979
	 *    @param	int		$notrigger		Disable all triggers
980
	 *    @return   int						>0 if OK, <0 if KO
981
	 */
982
	public function delete_contact($rowid, $notrigger = 0)
983
	{
984
        // phpcs:enable
985
		global $user;
986
987
988
		$this->db->begin();
989
990
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
991
		$sql.= " WHERE rowid =".$rowid;
992
993
		dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
994
		if ($this->db->query($sql))
995
		{
996
			if (! $notrigger)
997
			{
998
				$result=$this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
999
				if ($result < 0) { $this->db->rollback(); return -1; }
1000
			}
1001
1002
			$this->db->commit();
1003
			return 1;
1004
		}
1005
		else
1006
		{
1007
			$this->error=$this->db->lasterror();
1008
			$this->db->rollback();
1009
			return -1;
1010
		}
1011
	}
1012
1013
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1014
	/**
1015
	 *    Delete all links between an object $this and all its contacts
1016
	 *
1017
	 *	  @param	string	$source		'' or 'internal' or 'external'
1018
	 *	  @param	string	$code		Type of contact (code or id)
1019
	 *    @return   int					>0 if OK, <0 if KO
1020
	 */
1021
	public function delete_linked_contact($source = '', $code = '')
1022
	{
1023
        // phpcs:enable
1024
		$temp = array();
1025
		$typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1026
1027
		foreach($typeContact as $key => $value)
1028
		{
1029
			array_push($temp, $key);
1030
		}
1031
		$listId = implode(",", $temp);
1032
1033
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1034
		$sql.= " WHERE element_id = ".$this->id;
1035
		if ($listId)
1036
			$sql.= " AND fk_c_type_contact IN (".$listId.")";
1037
1038
		dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1039
		if ($this->db->query($sql))
1040
		{
1041
			return 1;
1042
		}
1043
		else
1044
		{
1045
			$this->error=$this->db->lasterror();
1046
			return -1;
1047
		}
1048
	}
1049
1050
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1051
	/**
1052
	 *    Get array of all contacts for an object
1053
	 *
1054
	 *    @param	int			$statut		Status of links to get (-1=all)
1055
	 *    @param	string		$source		Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)
1056
	 *    @param	int         $list       0:Return array contains all properties, 1:Return array contains just id
1057
	 *    @param    string      $code       Filter on this code of contact type ('SHIPPING', 'BILLING', ...)
1058
	 *    @return	array|int		        Array of contacts, -1 if error
1059
	 */
1060
	public function liste_contact($statut = -1, $source = 'external', $list = 0, $code = '')
1061
	{
1062
        // phpcs:enable
1063
		global $langs;
1064
1065
		$tab=array();
1066
1067
		$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
1068
		if ($source == 'internal') $sql.=", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
1069
		if ($source == 'external' || $source == 'thirdparty') $sql.=", t.fk_soc as socid, t.statut as statuscontact";
1070
		$sql.= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1071
		$sql.= ", tc.source, tc.element, tc.code, tc.libelle";
1072
		$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
1073
		$sql.= ", ".MAIN_DB_PREFIX."element_contact ec";
1074
		if ($source == 'internal') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
1075
		if ($source == 'external'|| $source == 'thirdparty') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
1076
		$sql.= " WHERE ec.element_id =".$this->id;
1077
		$sql.= " AND ec.fk_c_type_contact=tc.rowid";
1078
		$sql.= " AND tc.element='".$this->db->escape($this->element)."'";
1079
		if ($code) $sql.= " AND tc.code = '".$this->db->escape($code)."'";
1080
		if ($source == 'internal') $sql.= " AND tc.source = 'internal'";
1081
		if ($source == 'external' || $source == 'thirdparty') $sql.= " AND tc.source = 'external'";
1082
		$sql.= " AND tc.active=1";
1083
		if ($statut >= 0) $sql.= " AND ec.statut = '".$statut."'";
1084
		$sql.=" ORDER BY t.lastname ASC";
1085
1086
		dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1087
		$resql=$this->db->query($sql);
1088
		if ($resql)
1089
		{
1090
			$num=$this->db->num_rows($resql);
1091
			$i=0;
1092
			while ($i < $num)
1093
			{
1094
				$obj = $this->db->fetch_object($resql);
1095
1096
				if (! $list)
1097
				{
1098
					$transkey="TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1099
					$libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1100
					$tab[$i]=array('source'=>$obj->source,'socid'=>$obj->socid,'id'=>$obj->id,
1101
								   'nom'=>$obj->lastname,      // For backward compatibility
1102
								   'civility'=>$obj->civility, 'lastname'=>$obj->lastname, 'firstname'=>$obj->firstname, 'email'=>$obj->email, 'login'=>$obj->login, 'photo'=>$obj->photo, 'statuscontact'=>$obj->statuscontact,
1103
								   'rowid'=>$obj->rowid, 'code'=>$obj->code, 'libelle'=>$libelle_type, 'status'=>$obj->statuslink, 'fk_c_type_contact'=>$obj->fk_c_type_contact);
1104
				}
1105
				else
1106
				{
1107
					$tab[$i]=$obj->id;
1108
				}
1109
1110
				$i++;
1111
			}
1112
1113
			return $tab;
1114
		}
1115
		else
1116
		{
1117
			$this->error=$this->db->lasterror();
1118
			dol_print_error($this->db);
1119
			return -1;
1120
		}
1121
	}
1122
1123
1124
	/**
1125
	 * 		Update status of a contact linked to object
1126
	 *
1127
	 * 		@param	int		$rowid		Id of link between object and contact
1128
	 * 		@return	int					<0 if KO, >=0 if OK
1129
	 */
1130
	public function swapContactStatus($rowid)
1131
	{
1132
		$sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1133
		$sql.= " tc.code, tc.libelle";
1134
		//$sql.= ", s.fk_soc";
1135
		$sql.= " FROM (".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as tc)";
1136
		//$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
1137
		$sql.= " WHERE ec.rowid =".$rowid;
1138
		$sql.= " AND ec.fk_c_type_contact=tc.rowid";
1139
		$sql.= " AND tc.element = '".$this->db->escape($this->element)."'";
1140
1141
		dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1142
		$resql=$this->db->query($sql);
1143
		if ($resql)
1144
		{
1145
			$obj = $this->db->fetch_object($resql);
1146
			$newstatut = ($obj->statut == 4) ? 5 : 4;
1147
			$result = $this->update_contact($rowid, $newstatut);
1148
			$this->db->free($resql);
1149
			return $result;
1150
		}
1151
		else
1152
		{
1153
			$this->error=$this->db->error();
1154
			dol_print_error($this->db);
1155
			return -1;
1156
		}
1157
	}
1158
1159
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1160
	/**
1161
	 *      Return array with list of possible values for type of contacts
1162
	 *
1163
	 *      @param	string	$source     'internal', 'external' or 'all'
1164
	 *      @param	string	$order		Sort order by : 'position', 'code', 'rowid'...
1165
	 *      @param  int		$option     0=Return array id->label, 1=Return array code->label
1166
	 *      @param  int		$activeonly 0=all status of contact, 1=only the active
1167
	 *		@param	string	$code		Type of contact (Example: 'CUSTOMER', 'SERVICE')
1168
	 *      @return array       		Array list of type of contacts (id->label if option=0, code->label if option=1)
1169
	 */
1170
	public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1171
	{
1172
        // phpcs:enable
1173
		global $langs;
1174
1175
		if (empty($order)) $order='position';
1176
		if ($order == 'position') $order.=',code';
1177
1178
		$tab = array();
1179
		$sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1180
		$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
1181
		$sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
1182
		if ($activeonly == 1) $sql.= " AND tc.active=1"; // only the active types
1183
		if (! empty($source) && $source != 'all') $sql.= " AND tc.source='".$this->db->escape($source)."'";
1184
		if (! empty($code)) $sql.= " AND tc.code='".$this->db->escape($code)."'";
1185
		$sql.= $this->db->order($order, 'ASC');
1186
1187
		//print "sql=".$sql;
1188
		$resql=$this->db->query($sql);
1189
		if ($resql)
1190
		{
1191
			$num=$this->db->num_rows($resql);
1192
			$i=0;
1193
			while ($i < $num)
1194
			{
1195
				$obj = $this->db->fetch_object($resql);
1196
1197
				$transkey="TypeContact_".$this->element."_".$source."_".$obj->code;
1198
				$libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1199
				if (empty($option)) $tab[$obj->rowid]=$libelle_type;
1200
				else $tab[$obj->code]=$libelle_type;
1201
				$i++;
1202
			}
1203
			return $tab;
1204
		}
1205
		else
1206
		{
1207
			$this->error=$this->db->lasterror();
1208
			//dol_print_error($this->db);
1209
			return null;
1210
		}
1211
	}
1212
1213
	/**
1214
	 *      Return id of contacts for a source and a contact code.
1215
	 *      Example: contact client de facturation ('external', 'BILLING')
1216
	 *      Example: contact client de livraison ('external', 'SHIPPING')
1217
	 *      Example: contact interne suivi paiement ('internal', 'SALESREPFOLL')
1218
	 *
1219
	 *		@param	string	$source		'external' or 'internal'
1220
	 *		@param	string	$code		'BILLING', 'SHIPPING', 'SALESREPFOLL', ...
1221
	 *		@param	int		$status		limited to a certain status
1222
	 *      @return array       		List of id for such contacts
1223
	 */
1224
	public function getIdContact($source, $code, $status = 0)
1225
	{
1226
		global $conf;
1227
1228
		$result=array();
1229
		$i=0;
1230
		//cas particulier pour les expeditions
1231
		if ($this->element=='shipping' && $this->origin_id != 0) {
1232
			$id=$this->origin_id;
1233
			$element='commande';
1234
		} elseif ($this->element=='reception' && $this->origin_id != 0) {
1235
			$id=$this->origin_id;
1236
			$element='order_supplier';
1237
		} else {
1238
			$id=$this->id;
1239
			$element=$this->element;
1240
		}
1241
1242
		$sql = "SELECT ec.fk_socpeople";
1243
		$sql.= " FROM ".MAIN_DB_PREFIX."element_contact as ec,";
1244
		if ($source == 'internal') $sql.= " ".MAIN_DB_PREFIX."user as c,";
1245
		if ($source == 'external') $sql.= " ".MAIN_DB_PREFIX."socpeople as c,";
1246
		$sql.= " ".MAIN_DB_PREFIX."c_type_contact as tc";
1247
		$sql.= " WHERE ec.element_id = ".$id;
1248
		$sql.= " AND ec.fk_socpeople = c.rowid";
1249
		if ($source == 'internal') $sql.= " AND c.entity IN (".getEntity('user').")";
1250
		if ($source == 'external') $sql.= " AND c.entity IN (".getEntity('societe').")";
1251
		$sql.= " AND ec.fk_c_type_contact = tc.rowid";
1252
		$sql.= " AND tc.element = '".$element."'";
1253
		$sql.= " AND tc.source = '".$source."'";
1254
		if ($code) $sql.= " AND tc.code = '".$code."'";
1255
		$sql.= " AND tc.active = 1";
1256
		if ($status) $sql.= " AND ec.statut = ".$status;
1257
1258
		dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1259
		$resql=$this->db->query($sql);
1260
		if ($resql)
1261
		{
1262
			while ($obj = $this->db->fetch_object($resql))
1263
			{
1264
				$result[$i]=$obj->fk_socpeople;
1265
				$i++;
1266
			}
1267
		}
1268
		else
1269
		{
1270
			$this->error=$this->db->error();
1271
			return null;
1272
		}
1273
1274
		return $result;
1275
	}
1276
1277
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1278
	/**
1279
	 *		Load object contact with id=$this->contactid into $this->contact
1280
	 *
1281
	 *		@param	int		$contactid      Id du contact. Use this->contactid if empty.
1282
	 *		@return	int						<0 if KO, >0 if OK
1283
	 */
1284
	public function fetch_contact($contactid = null)
1285
	{
1286
        // phpcs:enable
1287
		if (empty($contactid)) $contactid=$this->contactid;
1288
1289
		if (empty($contactid)) return 0;
1290
1291
		require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1292
		$contact = new Contact($this->db);
1293
		$result=$contact->fetch($contactid);
1294
		$this->contact = $contact;
1295
		return $result;
1296
	}
1297
1298
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1299
	/**
1300
	 *    	Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty
1301
	 *
1302
	 *		@param		int		$force_thirdparty_id	Force thirdparty id
1303
	 *		@return		int								<0 if KO, >0 if OK
1304
	 */
1305
	public function fetch_thirdparty($force_thirdparty_id = 0)
1306
	{
1307
        // phpcs:enable
1308
		global $conf;
1309
1310
		if (empty($this->socid) && empty($this->fk_soc) && empty($this->fk_thirdparty) && empty($force_thirdparty_id))
0 ignored issues
show
Bug introduced by
The property fk_thirdparty does not exist on CommonObject. Did you mean thirdparty?
Loading history...
1311
			return 0;
1312
1313
		require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1314
1315
		$idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : $this->fk_thirdparty);
1316
		if ($force_thirdparty_id)
1317
			$idtofetch = $force_thirdparty_id;
1318
1319
		if ($idtofetch) {
1320
			$thirdparty = new Societe($this->db);
1321
			$result = $thirdparty->fetch($idtofetch);
1322
			$this->thirdparty = $thirdparty;
1323
1324
			// Use first price level if level not defined for third party
1325
			if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1326
				$this->thirdparty->price_level = 1;
1327
			}
1328
1329
			return $result;
1330
		} else
1331
			return -1;
1332
	}
1333
1334
1335
	/**
1336
	 * Looks for an object with ref matching the wildcard provided
1337
	 * It does only work when $this->table_ref_field is set
1338
	 *
1339
	 * @param string $ref Wildcard
1340
	 * @return int >1 = OK, 0 = Not found or table_ref_field not defined, <0 = KO
1341
	 */
1342
	public function fetchOneLike($ref)
1343
	{
1344
		if (!$this->table_ref_field) {
1345
			return 0;
1346
		}
1347
1348
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE '.$this->table_ref_field.' LIKE "'.$this->db->escape($ref).'" LIMIT 1';
1349
1350
		$query = $this->db->query($sql);
1351
1352
		if (!$this->db->num_rows($query)) {
1353
			return 0;
1354
		}
1355
1356
		$result = $this->db->fetch_object($query);
1357
1358
		return $this->fetch($result->rowid);
1 ignored issue
show
Bug introduced by
The method fetch() does not exist on CommonObject. Did you maybe mean fetch_user()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1358
		return $this->/** @scrutinizer ignore-call */ fetch($result->rowid);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1359
	}
1360
1361
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1362
	/**
1363
	 *	Load data for barcode into properties ->barcode_type*
1364
	 *	Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
1365
	 *  if it is not defined, ->element must be defined to know default barcode type.
1366
	 *
1367
	 *	@return		int			<0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
1368
	 */
1369
	public function fetch_barcode()
1370
	{
1371
        // phpcs:enable
1372
		global $conf;
1373
1374
		dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1375
1376
		$idtype=$this->barcode_type;
1377
		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
1378
		{
1379
			if ($this->element == 'product')      $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1380
			elseif ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1381
			else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1382
		}
1383
1384
		if ($idtype > 0)
1385
		{
1386
			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
1387
			{
1388
				$sql = "SELECT rowid, code, libelle as label, coder";
1389
				$sql.= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1390
				$sql.= " WHERE rowid = ".$idtype;
1391
				dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1392
				$resql = $this->db->query($sql);
1393
				if ($resql)
1394
				{
1395
					$obj = $this->db->fetch_object($resql);
1396
					$this->barcode_type       = $obj->rowid;
1397
					$this->barcode_type_code  = $obj->code;
1398
					$this->barcode_type_label = $obj->label;
1399
					$this->barcode_type_coder = $obj->coder;
1400
					return 1;
1401
				}
1402
				else
1403
				{
1404
					dol_print_error($this->db);
1405
					return -1;
1406
				}
1407
			}
1408
		}
1409
		return 0;
1410
	}
1411
1412
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1413
	/**
1414
	 *		Load the project with id $this->fk_project into this->project
1415
	 *
1416
	 *		@return		int			<0 if KO, >=0 if OK
1417
	 */
1418
	public function fetch_projet()
1419
	{
1420
        // phpcs:enable
1421
		include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1422
1423
		if (empty($this->fk_project) && ! empty($this->fk_projet)) $this->fk_project = $this->fk_projet;	// For backward compatibility
1424
		if (empty($this->fk_project)) return 0;
1425
1426
		$project = new Project($this->db);
1427
		$result = $project->fetch($this->fk_project);
1428
1429
		$this->projet = $project;	// deprecated
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$projet has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1429
		/** @scrutinizer ignore-deprecated */ $this->projet = $project;	// deprecated
Loading history...
1430
		$this->project = $project;
1431
		return $result;
1432
	}
1433
1434
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1435
	/**
1436
	 *		Load the product with id $this->fk_product into this->product
1437
	 *
1438
	 *		@return		int			<0 if KO, >=0 if OK
1439
	 */
1440
	public function fetch_product()
1441
	{
1442
        // phpcs:enable
1443
		include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1444
1445
		if (empty($this->fk_product)) return 0;
1446
1447
		$product = new Product($this->db);
1448
		$result = $product->fetch($this->fk_product);
1449
1450
		$this->product = $product;
1451
		return $result;
1452
	}
1453
1454
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1455
	/**
1456
	 *		Load the user with id $userid into this->user
1457
	 *
1458
	 *		@param	int		$userid 		Id du contact
1459
	 *		@return	int						<0 if KO, >0 if OK
1460
	 */
1461
	public function fetch_user($userid)
1462
	{
1463
        // phpcs:enable
1464
		$user = new User($this->db);
1465
		$result=$user->fetch($userid);
1466
		$this->user = $user;
1467
		return $result;
1468
	}
1469
1470
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1471
	/**
1472
	 *	Read linked origin object
1473
	 *
1474
	 *	@return		void
1475
	 */
1476
	public function fetch_origin()
1477
	{
1478
        // phpcs:enable
1479
		if ($this->origin == 'shipping') $this->origin = 'expedition';
1480
		if ($this->origin == 'delivery') $this->origin = 'livraison';
1481
        if ($this->origin == 'order_supplier') $this->origin = 'commandeFournisseur';
1482
1483
		$origin = $this->origin;
1484
1485
		$classname = ucfirst($origin);
1486
		$this->$origin = new $classname($this->db);
1487
		$this->$origin->fetch($this->origin_id);
1488
	}
1489
1490
	/**
1491
     *  Load object from specific field
1492
     *
1493
     *  @param	string	$table		Table element or element line
1494
     *  @param	string	$field		Field selected
1495
     *  @param	string	$key		Import key
1496
     *  @param	string	$element	Element name
1497
     *	@return	int					<0 if KO, >0 if OK
1498
     */
1499
	public function fetchObjectFrom($table, $field, $key, $element = null)
1500
	{
1501
		global $conf;
1502
1503
		$result=false;
1504
1505
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$table;
1506
		$sql.= " WHERE ".$field." = '".$key."'";
1507
		if (! empty($element)) {
1508
			$sql.= " AND entity IN (".getEntity($element).")";
1509
		} else {
1510
			$sql.= " AND entity = ".$conf->entity;
1511
		}
1512
1513
		dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1514
		$resql = $this->db->query($sql);
1515
		if ($resql)
1516
		{
1517
			$row = $this->db->fetch_row($resql);
1518
			// Test for avoid error -1
1519
			if ($row[0] > 0) {
1520
				$result = $this->fetch($row[0]);
1521
			}
1522
		}
1523
1524
		return $result;
1525
	}
1526
1527
	/**
1528
	 *	Getter generic. Load value from a specific field
1529
	 *
1530
	 *	@param	string	$table		Table of element or element line
1531
	 *	@param	int		$id			Element id
1532
	 *	@param	string	$field		Field selected
1533
	 *	@return	int					<0 if KO, >0 if OK
1534
	 */
1535
	public function getValueFrom($table, $id, $field)
1536
	{
1537
		$result=false;
1538
		if (!empty($id) && !empty($field) && !empty($table)) {
1539
			$sql = "SELECT ".$field." FROM ".MAIN_DB_PREFIX.$table;
1540
			$sql.= " WHERE rowid = ".$id;
1541
1542
			dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1543
			$resql = $this->db->query($sql);
1544
			if ($resql)
1545
			{
1546
				$row = $this->db->fetch_row($resql);
1547
				$result = $row[0];
1548
			}
1549
		}
1550
		return $result;
1551
	}
1552
1553
	/**
1554
	 *	Setter generic. Update a specific field into database.
1555
	 *  Warning: Trigger is run only if param trigkey is provided.
1556
	 *
1557
	 *	@param	string		$field			Field to update
1558
	 *	@param	mixed		$value			New value
1559
	 *	@param	string		$table			To force other table element or element line (should not be used)
1560
	 *	@param	int			$id				To force other object id (should not be used)
1561
	 *	@param	string		$format			Data format ('text', 'date'). 'text' is used if not defined
1562
	 *	@param	string		$id_field		To force rowid field name. 'rowid' is used if not defined
1563
	 *	@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'
1564
	 *  @param  string      $trigkey    	Trigger key to run (in most cases something like 'XXX_MODIFY')
1565
	 *  @param	string		$fk_user_field	Name of field to save user id making change
1566
	 *	@return	int							<0 if KO, >0 if OK
1567
	 *  @see updateExtraField()
1568
	 */
1569
	public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
1570
	{
1571
		global $user,$langs,$conf;
1572
1573
		if (empty($table)) 	  $table=$this->table_element;
1574
		if (empty($id))    	  $id=$this->id;
1575
		if (empty($format))   $format='text';
1576
		if (empty($id_field)) $id_field='rowid';
1577
1578
		$error=0;
1579
1580
		$this->db->begin();
1581
1582
		// Special case
1583
		if ($table == 'product' && $field == 'note_private') $field='note';
1584
		if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) $fk_user_field = 'fk_user_mod';
1585
1586
		$sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET ";
1587
1588
		if ($format == 'text') $sql.= $field." = '".$this->db->escape($value)."'";
1589
		elseif ($format == 'int') $sql.= $field." = ".$this->db->escape($value);
1590
		elseif ($format == 'date') $sql.= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
1591
1592
		if ($fk_user_field)
1593
		{
1594
			if (! empty($fuser) && is_object($fuser)) $sql.=", ".$fk_user_field." = ".$fuser->id;
1595
			elseif (empty($fuser) || $fuser != 'none') $sql.=", ".$fk_user_field." = ".$user->id;
1596
		}
1597
1598
		$sql.= " WHERE ".$id_field." = ".$id;
1599
1600
		dol_syslog(get_class($this)."::".__FUNCTION__."", LOG_DEBUG);
1601
		$resql = $this->db->query($sql);
1602
		if ($resql)
1603
		{
1604
			if ($trigkey)
1605
			{
1606
				// call trigger with updated object values
1607
				if (empty($this->fields) && method_exists($this, 'fetch'))
1608
				{
1609
					$result = $this->fetch($id);
1610
				}
1611
				else
1612
				{
1613
					$result = $this->fetchCommon($id);
1614
				}
1615
				if ($result >= 0) $result=$this->call_trigger($trigkey, (! empty($fuser) && is_object($fuser)) ? $fuser : $user);   // This may set this->errors
1616
				if ($result < 0) $error++;
1617
			}
1618
1619
			if (! $error)
1620
			{
1621
				if (property_exists($this, $field)) $this->$field = $value;
1622
				$this->db->commit();
1623
				return 1;
1624
			}
1625
			else
1626
			{
1627
				$this->db->rollback();
1628
				return -2;
1629
			}
1630
		}
1631
		else
1632
		{
1633
			$this->error=$this->db->lasterror();
1634
			$this->db->rollback();
1635
			return -1;
1636
		}
1637
	}
1638
1639
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1640
	/**
1641
	 *      Load properties id_previous and id_next by comparing $fieldid with $this->ref
1642
	 *
1643
	 *      @param	string	$filter		Optional filter. Example: " AND (t.field1 = 'aa' OR t.field2 = 'bb')"
1644
	 *	 	@param  string	$fieldid   	Name of field to use for the select MAX and MIN
1645
	 *		@param	int		$nodbprefix	Do not include DB prefix to forge table name
1646
	 *      @return int         		<0 if KO, >0 if OK
1647
	 */
1648
	public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
1649
	{
1650
        // phpcs:enable
1651
		global $conf, $user;
1652
1653
		if (! $this->table_element)
1654
		{
1655
			dol_print_error('', get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
1656
			return -1;
1657
		}
1658
		if ($fieldid == 'none') return 1;
1659
1660
		// Security on socid
1661
		$socid = 0;
1662
		if ($user->societe_id > 0) $socid = $user->societe_id;
1663
1664
		// this->ismultientitymanaged contains
1665
		// 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
1666
		$alias = 's';
1667
		if ($this->element == 'societe') $alias = 'te';
1668
1669
		$sql = "SELECT MAX(te.".$fieldid.")";
1670
		$sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1671
		if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1672
			$sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1673
		}
1674
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to entity
1675
		elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to socid
1676
		elseif ($this->restrictiononfksoc == 2 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON te.fk_soc = s.rowid";	// If we need to link to societe to limit select to socid
1677
		if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid)  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1678
		$sql.= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1679
		if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1680
		if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1681
		if (! empty($filter))
1682
		{
1683
			if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1684
			$sql.=$filter;
1685
		}
1686
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to entity
1687
		elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to socid
1688
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1689
			if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1690
				if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1691
					$sql.= " AND te.entity IS NOT NULL"; // Show all users
1692
				} else {
1693
					$sql.= " AND ug.fk_user = te.rowid";
1694
					$sql.= " AND ug.entity IN (".getEntity($this->element).")";
1695
				}
1696
			} else {
1697
				$sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1698
			}
1699
		}
1700
		if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1701
		if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1702
		if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1703
		//print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1704
1705
		$result = $this->db->query($sql);
1706
		if (! $result)
1707
		{
1708
			$this->error=$this->db->lasterror();
1709
			return -1;
1710
		}
1711
		$row = $this->db->fetch_row($result);
1712
		$this->ref_previous = $row[0];
1713
1714
1715
		$sql = "SELECT MIN(te.".$fieldid.")";
1716
		$sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1717
		if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1718
			$sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1719
		}
1720
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to entity
1721
		elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to socid
1722
		elseif ($this->restrictiononfksoc == 2 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON te.fk_soc = s.rowid";	// If we need to link to societe to limit select to socid
1723
		if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1724
		$sql.= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1725
		if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1726
		if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1727
		if (! empty($filter))
1728
		{
1729
			if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1730
			$sql.=$filter;
1731
		}
1732
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to entity
1733
		elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to socid
1734
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1735
			if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1736
				if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1737
					$sql.= " AND te.entity IS NOT NULL"; // Show all users
1738
				} else {
1739
					$sql.= " AND ug.fk_user = te.rowid";
1740
					$sql.= " AND ug.entity IN (".getEntity($this->element).")";
1741
				}
1742
			} else {
1743
				$sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1744
			}
1745
		}
1746
		if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1747
		if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1748
		if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1749
		//print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1750
		// 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
1751
1752
		$result = $this->db->query($sql);
1753
		if (! $result)
1754
		{
1755
			$this->error=$this->db->lasterror();
1756
			return -2;
1757
		}
1758
		$row = $this->db->fetch_row($result);
1759
		$this->ref_next = $row[0];
1760
1761
		return 1;
1762
	}
1763
1764
1765
	/**
1766
	 *      Return list of id of contacts of object
1767
	 *
1768
	 *      @param	string	$source     Source of contact: external (llx_socpeople) or internal (llx_user) or thirdparty (llx_societe)
1769
	 *      @return array				Array of id of contacts (if source=external or internal)
1770
	 * 									Array of id of third parties with at least one contact on object (if source=thirdparty)
1771
	 */
1772
	public function getListContactId($source = 'external')
1773
	{
1774
		$contactAlreadySelected = array();
1775
		$tab = $this->liste_contact(-1, $source);
1776
		$num=count($tab);
1777
		$i = 0;
1778
		while ($i < $num)
1779
		{
1780
			if ($source == 'thirdparty') $contactAlreadySelected[$i] = $tab[$i]['socid'];
1781
			else  $contactAlreadySelected[$i] = $tab[$i]['id'];
1782
			$i++;
1783
		}
1784
		return $contactAlreadySelected;
1785
	}
1786
1787
1788
	/**
1789
	 *	Link element with a project
1790
	 *
1791
	 *	@param     	int		$projectid		Project id to link element to
1792
	 *	@return		int						<0 if KO, >0 if OK
1793
	 */
1794
	public function setProject($projectid)
1795
	{
1796
		if (! $this->table_element)
1797
		{
1798
			dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR);
1799
			return -1;
1800
		}
1801
1802
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1803
		if ($this->table_element == 'actioncomm')
1804
		{
1805
			if ($projectid) $sql.= ' SET fk_project = '.$projectid;
1806
			else $sql.= ' SET fk_project = NULL';
1807
			$sql.= ' WHERE id = '.$this->id;
1808
		}
1809
		else
1810
		{
1811
			if ($projectid) $sql.= ' SET fk_projet = '.$projectid;
1812
			else $sql.= ' SET fk_projet = NULL';
1813
			$sql.= ' WHERE rowid = '.$this->id;
1814
		}
1815
1816
		dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
1817
		if ($this->db->query($sql))
1818
		{
1819
			$this->fk_project = $projectid;
1820
			return 1;
1821
		}
1822
		else
1823
		{
1824
			dol_print_error($this->db);
1825
			return -1;
1826
		}
1827
	}
1828
1829
	/**
1830
	 *  Change the payments methods
1831
	 *
1832
	 *  @param		int		$id		Id of new payment method
1833
	 *  @return		int				>0 if OK, <0 if KO
1834
	 */
1835
	public function setPaymentMethods($id)
1836
	{
1837
		dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
1838
		if ($this->statut >= 0 || $this->element == 'societe')
1839
		{
1840
			// TODO uniformize field name
1841
			$fieldname = 'fk_mode_reglement';
1842
			if ($this->element == 'societe') $fieldname = 'mode_reglement';
1843
			if (get_class($this) == 'Fournisseur') $fieldname = 'mode_reglement_supplier';
1844
1845
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1846
			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
1847
			$sql .= ' WHERE rowid='.$this->id;
1848
1849
			if ($this->db->query($sql))
1850
			{
1851
				$this->mode_reglement_id = $id;
1852
				// for supplier
1853
				if (get_class($this) == 'Fournisseur') $this->mode_reglement_supplier_id = $id;
1854
				return 1;
1855
			}
1856
			else
1857
			{
1858
				dol_syslog(get_class($this).'::setPaymentMethods Erreur '.$sql.' - '.$this->db->error());
1859
				$this->error=$this->db->error();
1860
				return -1;
1861
			}
1862
		}
1863
		else
1864
		{
1865
			dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
1866
			$this->error='Status of the object is incompatible '.$this->statut;
1867
			return -2;
1868
		}
1869
	}
1870
1871
	/**
1872
	 *  Change the multicurrency code
1873
	 *
1874
	 *  @param		string	$code	multicurrency code
1875
	 *  @return		int				>0 if OK, <0 if KO
1876
	 */
1877
	public function setMulticurrencyCode($code)
1878
	{
1879
		dol_syslog(get_class($this).'::setMulticurrencyCode('.$id.')');
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $id seems to be never defined.
Loading history...
1880
		if ($this->statut >= 0 || $this->element == 'societe')
1881
		{
1882
			$fieldname = 'multicurrency_code';
1883
1884
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1885
			$sql .= ' SET '.$fieldname." = '".$this->db->escape($code)."'";
1886
			$sql .= ' WHERE rowid='.$this->id;
1887
1888
			if ($this->db->query($sql))
1889
			{
1890
				$this->multicurrency_code = $code;
1891
1892
				list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
1893
				if ($rate) $this->setMulticurrencyRate($rate, 2);
1894
1895
				return 1;
1896
			}
1897
			else
1898
			{
1899
				dol_syslog(get_class($this).'::setMulticurrencyCode Erreur '.$sql.' - '.$this->db->error());
1900
				$this->error=$this->db->error();
1901
				return -1;
1902
			}
1903
		}
1904
		else
1905
		{
1906
			dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
1907
			$this->error='Status of the object is incompatible '.$this->statut;
1908
			return -2;
1909
		}
1910
	}
1911
1912
	/**
1913
	 *  Change the multicurrency rate
1914
	 *
1915
	 *  @param		double	$rate	multicurrency rate
1916
	 *  @param		int		$mode	mode 1 : amounts in company currency will be recalculated, mode 2 : amounts in foreign currency
1917
	 *  @return		int				>0 if OK, <0 if KO
1918
	 */
1919
	public function setMulticurrencyRate($rate, $mode = 1)
1920
	{
1921
		dol_syslog(get_class($this).'::setMulticurrencyRate('.$id.')');
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $id seems to be never defined.
Loading history...
1922
		if ($this->statut >= 0 || $this->element == 'societe')
1923
		{
1924
			$fieldname = 'multicurrency_tx';
1925
1926
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1927
			$sql .= ' SET '.$fieldname.' = '.$rate;
1928
			$sql .= ' WHERE rowid='.$this->id;
1929
1930
			if ($this->db->query($sql))
1931
			{
1932
				$this->multicurrency_tx = $rate;
1933
1934
				// Update line price
1935
				if (!empty($this->lines))
1936
				{
1937
					foreach ($this->lines as &$line)
1938
					{
1939
						if($mode == 1) {
1940
							$line->subprice = 0;
1941
						}
1942
1943
						switch ($this->element) {
1944
							case 'propal':
1945
								$this->updateline(
1 ignored issue
show
Bug introduced by
The method updateline() does not exist on CommonObject. Did you maybe mean updateLineUp()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1945
								$this->/** @scrutinizer ignore-call */ 
1946
               updateline(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1946
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1947
									($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1948
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start,
1949
									$line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1950
								);
1951
								break;
1952
							case 'commande':
1953
								$this->updateline(
1954
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1955
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end,
1956
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1957
									$line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1958
								);
1959
								break;
1960
							case 'facture':
1961
								$this->updateline(
1962
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1963
									$line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,
1964
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1965
									$line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice
1966
								);
1967
								break;
1968
							case 'supplier_proposal':
1969
								$this->updateline(
1970
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1971
									($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1972
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options,
1973
									$line->ref_fourn, $line->multicurrency_subprice
1974
								);
1975
								break;
1976
							case 'order_supplier':
1977
								$this->updateline(
1978
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1979
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false,
1980
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1981
								);
1982
								break;
1983
							case 'invoice_supplier':
1984
								$this->updateline(
1985
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx,
1986
									$line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false,
1987
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1988
								);
1989
								break;
1990
							default:
1991
								dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
1992
								break;
1993
						}
1994
					}
1995
				}
1996
1997
				return 1;
1998
			}
1999
			else
2000
			{
2001
				dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
2002
				$this->error=$this->db->error();
2003
				return -1;
2004
			}
2005
		}
2006
		else
2007
		{
2008
			dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2009
			$this->error='Status of the object is incompatible '.$this->statut;
2010
			return -2;
2011
		}
2012
	}
2013
2014
	/**
2015
	 *  Change the payments terms
2016
	 *
2017
	 *  @param		int		$id		Id of new payment terms
2018
	 *  @return		int				>0 if OK, <0 if KO
2019
	 */
2020
	public function setPaymentTerms($id)
2021
	{
2022
		dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
2023
		if ($this->statut >= 0 || $this->element == 'societe')
2024
		{
2025
			// TODO uniformize field name
2026
			$fieldname = 'fk_cond_reglement';
2027
			if ($this->element == 'societe') $fieldname = 'cond_reglement';
2028
			if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
2029
2030
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2031
			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
2032
			$sql .= ' WHERE rowid='.$this->id;
2033
2034
			if ($this->db->query($sql))
2035
			{
2036
				$this->cond_reglement_id = $id;
2037
				// for supplier
2038
				if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
2039
				$this->cond_reglement = $id;	// for compatibility
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$cond_reglement has been deprecated: Kept for compatibility ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

2039
				/** @scrutinizer ignore-deprecated */ $this->cond_reglement = $id;	// for compatibility

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
2040
				return 1;
2041
			}
2042
			else
2043
			{
2044
				dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
2045
				$this->error=$this->db->error();
2046
				return -1;
2047
			}
2048
		}
2049
		else
2050
		{
2051
			dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2052
			$this->error='Status of the object is incompatible '.$this->statut;
2053
			return -2;
2054
		}
2055
	}
2056
2057
	/**
2058
	 *	Define delivery address
2059
	 *  @deprecated
2060
	 *
2061
	 *	@param      int		$id		Address id
2062
	 *	@return     int				<0 si ko, >0 si ok
2063
	 */
2064
	public function setDeliveryAddress($id)
2065
	{
2066
		$fieldname = 'fk_delivery_address';
2067
		if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
2068
2069
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
2070
		$sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
2071
2072
		if ($this->db->query($sql))
2073
		{
2074
			$this->fk_delivery_address = $id;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$fk_delivery_address has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

2074
			/** @scrutinizer ignore-deprecated */ $this->fk_delivery_address = $id;
Loading history...
2075
			return 1;
2076
		}
2077
		else
2078
		{
2079
			$this->error=$this->db->error();
2080
			dol_syslog(get_class($this).'::setDeliveryAddress Erreur '.$sql.' - '.$this->error);
2081
			return -1;
2082
		}
2083
	}
2084
2085
2086
	/**
2087
	 *  Change the shipping method
2088
	 *
2089
	 *  @param      int     $shipping_method_id     Id of shipping method
2090
	 *  @param      bool    $notrigger              false=launch triggers after, true=disable triggers
2091
	 *  @param      User	$userused               Object user
2092
	 *
2093
	 *  @return     int              1 if OK, 0 if KO
2094
	 */
2095
	public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null)
2096
	{
2097
		global $user;
2098
2099
		if (empty($userused)) $userused=$user;
2100
2101
		$error = 0;
2102
2103
		if (! $this->table_element) {
2104
			dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR);
2105
			return -1;
2106
		}
2107
2108
		$this->db->begin();
2109
2110
		if ($shipping_method_id<0) $shipping_method_id='NULL';
2111
		dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2112
2113
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2114
		$sql.= " SET fk_shipping_method = ".$shipping_method_id;
2115
		$sql.= " WHERE rowid=".$this->id;
2116
		$resql = $this->db->query($sql);
2117
		if (! $resql) {
2118
			dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2119
			$this->error = $this->db->lasterror();
2120
			$error++;
2121
		} else {
2122
			if (!$notrigger)
2123
			{
2124
				// Call trigger
2125
				$this->context=array('shippingmethodupdate'=>1);
2126
				$result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2127
				if ($result < 0) $error++;
2128
				// End call trigger
2129
			}
2130
		}
2131
		if ($error)
2132
		{
2133
			$this->db->rollback();
2134
			return -1;
2135
		} else {
2136
			$this->shipping_method_id = ($shipping_method_id=='NULL')?null:$shipping_method_id;
1 ignored issue
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...
2137
			$this->db->commit();
2138
			return 1;
2139
		}
2140
	}
2141
2142
2143
	/**
2144
	 *  Change the warehouse
2145
	 *
2146
	 *  @param      int     $warehouse_id     Id of warehouse
2147
	 *  @return     int              1 if OK, 0 if KO
2148
	 */
2149
	public function setWarehouse($warehouse_id)
2150
	{
2151
		if (! $this->table_element) {
2152
			dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR);
2153
			return -1;
2154
		}
2155
		if ($warehouse_id<0) $warehouse_id='NULL';
2156
		dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2157
2158
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2159
		$sql.= " SET fk_warehouse = ".$warehouse_id;
2160
		$sql.= " WHERE rowid=".$this->id;
2161
2162
		if ($this->db->query($sql)) {
2163
			$this->warehouse_id = ($warehouse_id=='NULL')?null:$warehouse_id;
2164
			return 1;
2165
		} else {
2166
			dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2167
			$this->error=$this->db->error();
2168
			return 0;
2169
		}
2170
	}
2171
2172
2173
	/**
2174
	 *		Set last model used by doc generator
2175
	 *
2176
	 *		@param		User	$user		User object that make change
2177
	 *		@param		string	$modelpdf	Modele name
2178
	 *		@return		int					<0 if KO, >0 if OK
2179
	 */
2180
	public function setDocModel($user, $modelpdf)
2181
	{
2182
		if (! $this->table_element)
2183
		{
2184
			dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR);
2185
			return -1;
2186
		}
2187
2188
		$newmodelpdf=dol_trunc($modelpdf, 255);
2189
2190
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2191
		$sql.= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2192
		$sql.= " WHERE rowid = ".$this->id;
2193
		// if ($this->element == 'facture') $sql.= " AND fk_statut < 2";
2194
		// if ($this->element == 'propal')  $sql.= " AND fk_statut = 0";
2195
2196
		dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2197
		$resql=$this->db->query($sql);
2198
		if ($resql)
2199
		{
2200
			$this->modelpdf=$modelpdf;
2201
			return 1;
2202
		}
2203
		else
2204
		{
2205
			dol_print_error($this->db);
2206
			return 0;
2207
		}
2208
	}
2209
2210
2211
	/**
2212
	 *  Change the bank account
2213
	 *
2214
	 *  @param		int		$fk_account		Id of bank account
2215
	 *  @param      bool    $notrigger      false=launch triggers after, true=disable triggers
2216
	 *  @param      User	$userused		Object user
2217
	 *  @return		int				1 if OK, 0 if KO
2218
	 */
2219
	public function setBankAccount($fk_account, $notrigger = false, $userused = null)
2220
	{
2221
		global $user;
2222
2223
		if (empty($userused)) $userused=$user;
2224
2225
		$error = 0;
2226
2227
		if (! $this->table_element) {
2228
			dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR);
2229
			return -1;
2230
		}
2231
		$this->db->begin();
2232
2233
		if ($fk_account<0) $fk_account='NULL';
2234
		dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2235
2236
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2237
		$sql.= " SET fk_account = ".$fk_account;
2238
		$sql.= " WHERE rowid=".$this->id;
2239
2240
		$resql = $this->db->query($sql);
2241
		if (! $resql)
2242
		{
2243
			dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2244
			$this->error = $this->db->lasterror();
2245
			$error++;
2246
		}
2247
		else
2248
		{
2249
			if (!$notrigger)
2250
			{
2251
				// Call trigger
2252
				$this->context=array('bankaccountupdate'=>1);
2253
				$result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2254
				if ($result < 0) $error++;
2255
				// End call trigger
2256
			}
2257
		}
2258
		if ($error)
2259
		{
2260
			$this->db->rollback();
2261
			return -1;
2262
		}
2263
		else
2264
		{
2265
			$this->fk_account = ($fk_account=='NULL')?null:$fk_account;
1 ignored issue
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...
2266
			$this->db->commit();
2267
			return 1;
2268
		}
2269
	}
2270
2271
2272
	// TODO: Move line related operations to CommonObjectLine?
2273
2274
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2275
	/**
2276
	 *  Save a new position (field rang) for details lines.
2277
	 *  You can choose to set position for lines with already a position or lines without any position defined.
2278
	 *
2279
	 * 	@param		boolean		$renum			   True to renum all already ordered lines, false to renum only not already ordered lines.
2280
	 * 	@param		string		$rowidorder		   ASC or DESC
2281
	 * 	@param		boolean		$fk_parent_line    Table with fk_parent_line field or not
2282
	 * 	@return		int                            <0 if KO, >0 if OK
2283
	 */
2284
	public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
2285
	{
2286
        // phpcs:enable
2287
		if (! $this->table_element_line)
2288
		{
2289
			dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR);
2290
			return -1;
2291
		}
2292
		if (! $this->fk_element)
2293
		{
2294
			dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR);
2295
			return -1;
2296
		}
2297
2298
		// Count number of lines to reorder (according to choice $renum)
2299
		$nl=0;
2300
		$sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2301
		$sql.= ' WHERE '.$this->fk_element.'='.$this->id;
2302
		if (! $renum) $sql.= ' AND rang = 0';
2303
		if ($renum) $sql.= ' AND rang <> 0';
2304
2305
		dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
2306
		$resql = $this->db->query($sql);
2307
		if ($resql)
2308
		{
2309
			$row = $this->db->fetch_row($resql);
2310
			$nl = $row[0];
2311
		}
2312
		else dol_print_error($this->db);
2313
		if ($nl > 0)
2314
		{
2315
			// The goal of this part is to reorder all lines, with all children lines sharing the same
2316
			// counter that parents.
2317
			$rows=array();
2318
2319
			// We first search all lines that are parent lines (for multilevel details lines)
2320
			$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2321
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2322
			if ($fk_parent_line) $sql.= ' AND fk_parent_line IS NULL';
2323
			$sql.= ' ORDER BY rang ASC, rowid '.$rowidorder;
2324
2325
			dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
2326
			$resql = $this->db->query($sql);
2327
			if ($resql)
2328
			{
2329
				$i=0;
2330
				$num = $this->db->num_rows($resql);
2331
				while ($i < $num)
2332
				{
2333
					$row = $this->db->fetch_row($resql);
2334
					$rows[] = $row[0];	// Add parent line into array rows
2335
					$childrens = $this->getChildrenOfLine($row[0]);
2336
					if (! empty($childrens))
2337
					{
2338
						foreach($childrens as $child)
2339
						{
2340
							array_push($rows, $child);
2341
						}
2342
					}
2343
					$i++;
2344
				}
2345
2346
				// Now we set a new number for each lines (parent and children with children included into parent tree)
2347
				if (! empty($rows))
2348
				{
2349
					foreach($rows as $key => $row)
2350
					{
2351
						$this->updateRangOfLine($row, ($key+1));
2352
					}
2353
				}
2354
			}
2355
			else
2356
			{
2357
				dol_print_error($this->db);
2358
			}
2359
		}
2360
		return 1;
2361
	}
2362
2363
	/**
2364
	 * 	Get children of line
2365
	 *
2366
	 * 	@param	int		$id		Id of parent line
2367
	 * 	@return	array			Array with list of children lines id
2368
	 */
2369
	public function getChildrenOfLine($id)
2370
	{
2371
		$rows=array();
2372
2373
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2374
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2375
		$sql.= ' AND fk_parent_line = '.$id;
2376
		$sql.= ' ORDER BY rang ASC';
2377
2378
		dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG);
2379
		$resql = $this->db->query($sql);
2380
		if ($resql)
2381
		{
2382
			$i=0;
2383
			$num = $this->db->num_rows($resql);
2384
			while ($i < $num)
2385
			{
2386
				$row = $this->db->fetch_row($resql);
2387
				$rows[$i] = $row[0];
2388
				$i++;
2389
			}
2390
		}
2391
2392
		return $rows;
2393
	}
2394
2395
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2396
	/**
2397
	 * 	Update a line to have a lower rank
2398
	 *
2399
	 * 	@param 	int			$rowid				Id of line
2400
	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2401
	 * 	@return	void
2402
	 */
2403
	public function line_up($rowid, $fk_parent_line = true)
2404
	{
2405
        // phpcs:enable
2406
		$this->line_order(false, 'ASC', $fk_parent_line);
2407
2408
		// Get rang of line
2409
		$rang = $this->getRangOfLine($rowid);
2410
2411
		// Update position of line
2412
		$this->updateLineUp($rowid, $rang);
2413
	}
2414
2415
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2416
	/**
2417
	 * 	Update a line to have a higher rank
2418
	 *
2419
	 * 	@param	int			$rowid				Id of line
2420
	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2421
	 * 	@return	void
2422
	 */
2423
	public function line_down($rowid, $fk_parent_line = true)
2424
	{
2425
        // phpcs:enable
2426
		$this->line_order(false, 'ASC', $fk_parent_line);
2427
2428
		// Get rang of line
2429
		$rang = $this->getRangOfLine($rowid);
2430
2431
		// Get max value for rang
2432
		$max = $this->line_max();
2433
2434
		// Update position of line
2435
		$this->updateLineDown($rowid, $rang, $max);
2436
	}
2437
2438
	/**
2439
	 * 	Update position of line (rang)
2440
	 *
2441
	 * 	@param	int		$rowid		Id of line
2442
	 * 	@param	int		$rang		Position
2443
	 * 	@return	void
2444
	 */
2445
	public function updateRangOfLine($rowid, $rang)
2446
	{
2447
		$fieldposition = 'rang';	// @TODO Rename 'rang' into 'position'
2448
		if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2449
		if (in_array($this->table_element_line, array('bom_bomline'))) $fieldposition = 'position';
2450
2451
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2452
		$sql.= ' WHERE rowid = '.$rowid;
2453
2454
		dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
2455
		if (! $this->db->query($sql))
2456
		{
2457
			dol_print_error($this->db);
2458
		}
2459
	}
2460
2461
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2462
	/**
2463
	 * 	Update position of line with ajax (rang)
2464
	 *
2465
	 * 	@param	array	$rows	Array of rows
2466
	 * 	@return	void
2467
	 */
2468
	public function line_ajaxorder($rows)
2469
	{
2470
        // phpcs:enable
2471
		$num = count($rows);
2472
		for ($i = 0 ; $i < $num ; $i++)
2473
		{
2474
			$this->updateRangOfLine($rows[$i], ($i+1));
2475
		}
2476
	}
2477
2478
	/**
2479
	 * 	Update position of line up (rang)
2480
	 *
2481
	 * 	@param	int		$rowid		Id of line
2482
	 * 	@param	int		$rang		Position
2483
	 * 	@return	void
2484
	 */
2485
	public function updateLineUp($rowid, $rang)
2486
	{
2487
		if ($rang > 1)
2488
		{
2489
			$fieldposition = 'rang';
2490
			if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2491
2492
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang ;
2493
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2494
			$sql.= ' AND rang = '.($rang - 1);
2495
			if ($this->db->query($sql) )
2496
			{
2497
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang - 1);
2498
				$sql.= ' WHERE rowid = '.$rowid;
2499
				if (! $this->db->query($sql) )
2500
				{
2501
					dol_print_error($this->db);
2502
				}
2503
			}
2504
			else
2505
			{
2506
				dol_print_error($this->db);
2507
			}
2508
		}
2509
	}
2510
2511
	/**
2512
	 * 	Update position of line down (rang)
2513
	 *
2514
	 * 	@param	int		$rowid		Id of line
2515
	 * 	@param	int		$rang		Position
2516
	 * 	@param	int		$max		Max
2517
	 * 	@return	void
2518
	 */
2519
	public function updateLineDown($rowid, $rang, $max)
2520
	{
2521
		if ($rang < $max)
2522
		{
2523
			$fieldposition = 'rang';
2524
			if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2525
2526
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2527
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2528
			$sql.= ' AND rang = '.($rang+1);
2529
			if ($this->db->query($sql) )
2530
			{
2531
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang+1);
2532
				$sql.= ' WHERE rowid = '.$rowid;
2533
				if (! $this->db->query($sql) )
2534
				{
2535
					dol_print_error($this->db);
2536
				}
2537
			}
2538
			else
2539
			{
2540
				dol_print_error($this->db);
2541
			}
2542
		}
2543
	}
2544
2545
	/**
2546
	 * 	Get position of line (rang)
2547
	 *
2548
	 * 	@param		int		$rowid		Id of line
2549
	 *  @return		int     			Value of rang in table of lines
2550
	 */
2551
	public function getRangOfLine($rowid)
2552
	{
2553
		$sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2554
		$sql.= ' WHERE rowid ='.$rowid;
2555
2556
		dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
2557
		$resql = $this->db->query($sql);
2558
		if ($resql)
2559
		{
2560
			$row = $this->db->fetch_row($resql);
2561
			return $row[0];
2562
		}
2563
	}
2564
2565
	/**
2566
	 * 	Get rowid of the line relative to its position
2567
	 *
2568
	 * 	@param		int		$rang		Rang value
2569
	 *  @return     int     			Rowid of the line
2570
	 */
2571
	public function getIdOfLine($rang)
2572
	{
2573
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2574
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2575
		$sql.= ' AND rang = '.$rang;
2576
		$resql = $this->db->query($sql);
2577
		if ($resql)
2578
		{
2579
			$row = $this->db->fetch_row($resql);
2580
			return $row[0];
2581
		}
2582
	}
2583
2584
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2585
	/**
2586
	 * 	Get max value used for position of line (rang)
2587
	 *
2588
	 * 	@param		int		$fk_parent_line		Parent line id
2589
	 *  @return     int  			   			Max value of rang in table of lines
2590
	 */
2591
	public function line_max($fk_parent_line = 0)
2592
	{
2593
        // phpcs:enable
2594
		// Search the last rang with fk_parent_line
2595
		if ($fk_parent_line)
2596
		{
2597
			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2598
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2599
			$sql.= ' AND fk_parent_line = '.$fk_parent_line;
2600
2601
			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2602
			$resql = $this->db->query($sql);
2603
			if ($resql)
2604
			{
2605
				$row = $this->db->fetch_row($resql);
2606
				if (! empty($row[0]))
2607
				{
2608
					return $row[0];
2609
				}
2610
				else
2611
				{
2612
					return $this->getRangOfLine($fk_parent_line);
2613
				}
2614
			}
2615
		}
2616
		// If not, search the last rang of element
2617
		else
2618
		{
2619
			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2620
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2621
2622
			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2623
			$resql = $this->db->query($sql);
2624
			if ($resql)
2625
			{
2626
				$row = $this->db->fetch_row($resql);
2627
				return $row[0];
2628
			}
2629
		}
2630
	}
2631
2632
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2633
	/**
2634
	 *  Update external ref of element
2635
	 *
2636
	 *  @param      string		$ref_ext	Update field ref_ext
2637
	 *  @return     int      		   		<0 if KO, >0 if OK
2638
	 */
2639
	public function update_ref_ext($ref_ext)
2640
	{
2641
        // phpcs:enable
2642
		if (! $this->table_element)
2643
		{
2644
			dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
2645
			return -1;
2646
		}
2647
2648
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2649
		$sql.= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
2650
		$sql.= " WHERE ".(isset($this->table_rowid)?$this->table_rowid:'rowid')." = ". $this->id;
2651
2652
		dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
2653
		if ($this->db->query($sql))
2654
		{
2655
			$this->ref_ext = $ref_ext;
2656
			return 1;
2657
		}
2658
		else
2659
		{
2660
			$this->error=$this->db->error();
2661
			return -1;
2662
		}
2663
	}
2664
2665
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2666
	/**
2667
	 *  Update note of element
2668
	 *
2669
	 *  @param      string		$note		New value for note
2670
	 *  @param		string		$suffix		'', '_public' or '_private'
2671
	 *  @return     int      		   		<0 if KO, >0 if OK
2672
	 */
2673
	public function update_note($note, $suffix = '')
2674
	{
2675
        // phpcs:enable
2676
		global $user;
2677
2678
		if (! $this->table_element)
2679
		{
2680
			$this->error='update_note was called on objet with property table_element not defined';
2681
			dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
2682
			return -1;
2683
		}
2684
		if (! in_array($suffix, array('','_public','_private')))
2685
		{
2686
			$this->error='update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
2687
			dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
2688
			return -2;
2689
		}
2690
		// Special cas
2691
		//var_dump($this->table_element);exit;
2692
		if ($this->table_element == 'product') $suffix='';
2693
2694
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2695
		$sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL");
2696
		$sql.= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))?"fk_user_mod":"fk_user_modif")." = ".$user->id;
2697
		$sql.= " WHERE rowid =". $this->id;
2698
2699
		dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
2700
		if ($this->db->query($sql))
2701
		{
2702
			if ($suffix == '_public') $this->note_public = $note;
2703
			elseif ($suffix == '_private') $this->note_private = $note;
2704
			else
2705
			{
2706
				$this->note = $note;      // deprecated
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$note has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

2706
				/** @scrutinizer ignore-deprecated */ $this->note = $note;      // deprecated
Loading history...
2707
				$this->note_private = $note;
2708
			}
2709
			return 1;
2710
		}
2711
		else
2712
		{
2713
			$this->error=$this->db->lasterror();
2714
			return -1;
2715
		}
2716
	}
2717
2718
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2719
	/**
2720
	 * 	Update public note (kept for backward compatibility)
2721
	 *
2722
	 * @param      string		$note		New value for note
2723
	 * @return     int      		   		<0 if KO, >0 if OK
2724
	 * @deprecated
2725
	 * @see update_note()
2726
	 */
2727
	public function update_note_public($note)
2728
	{
2729
        // phpcs:enable
2730
		return $this->update_note($note, '_public');
2731
	}
2732
2733
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2734
	/**
2735
	 *	Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
2736
	 *  Must be called at end of methods addline or updateline.
2737
	 *
2738
	 *	@param	int		$exclspec          	>0 = Exclude special product (product_type=9)
2739
	 *  @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
2740
	 *  @param	int		$nodatabaseupdate	1=Do not update database. Update only properties of object.
2741
	 *  @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.
2742
	 *	@return	int    			           	<0 if KO, >0 if OK
2743
	 */
2744
	public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null)
2745
	{
2746
        // phpcs:enable
2747
		global $conf, $hookmanager, $action;
2748
2749
		// Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
2750
		$MODULE = "";
2751
		if ($this->element == 'propal')
2752
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
2753
		elseif ($this->element == 'commande' || $this->element == 'order')
2754
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
2755
		elseif ($this->element == 'facture')
2756
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
2757
		elseif ($this->element == 'facture_fourn')
2758
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
2759
		elseif ($this->element == 'order_supplier')
2760
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
2761
		elseif ($this->element == 'supplier_proposal')
2762
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
2763
2764
		if (! empty($MODULE)) {
2765
			if (! empty($conf->global->$MODULE)) {
2766
				$modsactivated = explode(',', $conf->global->$MODULE);
2767
				foreach ($modsactivated as $mod) {
2768
					if ($conf->$mod->enabled)
2769
						return 1; // update was disabled by specific setup
2770
				}
2771
			}
2772
		}
2773
2774
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2775
2776
		if ($roundingadjust == '-1') $roundingadjust='auto';	// For backward compatibility
2777
2778
		$forcedroundingmode=$roundingadjust;
2779
		if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode=$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
2780
		elseif ($forcedroundingmode == 'auto') $forcedroundingmode='0';
2781
2782
		$error=0;
2783
2784
		$multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
2785
2786
		// Define constants to find lines to sum
2787
		$fieldtva='total_tva';
2788
		$fieldlocaltax1='total_localtax1';
2789
		$fieldlocaltax2='total_localtax2';
2790
		$fieldup='subprice';
2791
		if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
2792
		{
2793
			$fieldtva='tva';
2794
			$fieldup='pu_ht';
2795
		}
2796
		if ($this->element == 'expensereport')
2797
		{
2798
			$fieldup='value_unit';
2799
		}
2800
2801
		$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,';
2802
		$sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
2803
			if ($this->table_element_line == 'facturedet') $sql.= ', situation_percent';
2804
			$sql.= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
2805
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2806
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2807
		if ($exclspec)
2808
		{
2809
			$product_field='product_type';
2810
			if ($this->table_element_line == 'contratdet') $product_field='';    // contratdet table has no product_type field
2811
			if ($product_field) $sql.= ' AND '.$product_field.' <> 9';
2812
		}
2813
		$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
2814
2815
		dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2816
		$resql = $this->db->query($sql);
2817
		if ($resql)
2818
		{
2819
			$this->total_ht  = 0;
2820
			$this->total_tva = 0;
2821
			$this->total_localtax1 = 0;
2822
			$this->total_localtax2 = 0;
2823
			$this->total_ttc = 0;
2824
			$total_ht_by_vats  = array();
2825
			$total_tva_by_vats = array();
2826
			$total_ttc_by_vats = array();
2827
			$this->multicurrency_total_ht	= 0;
2828
			$this->multicurrency_total_tva	= 0;
2829
			$this->multicurrency_total_ttc	= 0;
2830
2831
			$num = $this->db->num_rows($resql);
2832
			$i = 0;
2833
			while ($i < $num)
2834
			{
2835
				$obj = $this->db->fetch_object($resql);
2836
2837
				// Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
2838
				$parameters=array('fk_element' => $obj->rowid);
2839
				$reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2840
2841
				if (empty($reshook) && $forcedroundingmode == '0')	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode='auto'
2842
				{
2843
					$localtax_array=array($obj->localtax1_type,$obj->localtax1_tx,$obj->localtax2_type,$obj->localtax2_tx);
2844
					$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);
2845
					$diff=price2num($tmpcal[1] - $obj->total_tva, 'MT', 1);
2846
					if ($diff)
2847
					{
2848
						$sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid;
2849
						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);
2850
								$resqlfix=$this->db->query($sqlfix);
2851
								if (! $resqlfix) dol_print_error($this->db, 'Failed to update line');
2852
								$obj->total_tva = $tmpcal[1];
2853
								$obj->total_ttc = $tmpcal[2];
2854
						//
2855
					}
2856
				}
2857
2858
				$this->total_ht        += $obj->total_ht;		// The field visible at end of line detail
2859
				$this->total_tva       += $obj->total_tva;
2860
				$this->total_localtax1 += $obj->total_localtax1;
2861
				$this->total_localtax2 += $obj->total_localtax2;
2862
				$this->total_ttc       += $obj->total_ttc;
2863
				$this->multicurrency_total_ht        += $obj->multicurrency_total_ht;		// The field visible at end of line detail
2864
				$this->multicurrency_total_tva       += $obj->multicurrency_total_tva;
2865
				$this->multicurrency_total_ttc       += $obj->multicurrency_total_ttc;
2866
2867
				if (! isset($total_ht_by_vats[$obj->vatrate]))  $total_ht_by_vats[$obj->vatrate]=0;
2868
				if (! isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate]=0;
2869
				if (! isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate]=0;
2870
				$total_ht_by_vats[$obj->vatrate]  += $obj->total_ht;
2871
				$total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
2872
				$total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
2873
2874
				if ($forcedroundingmode == '1')	// Check if we need adjustement onto line for vat. TODO This works on the company currency but not on multicurrency
2875
				{
2876
					$tmpvat=price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
2877
					$diff=price2num($total_tva_by_vats[$obj->vatrate]-$tmpvat, 'MT', 1);
2878
					//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";
2879
					if ($diff)
2880
					{
2881
						if (abs($diff) > 0.1) { dol_syslog('A rounding difference was detected into TOTAL but is too high to be corrected', LOG_WARNING); exit; }
1 ignored issue
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2882
						$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;
2883
						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);
2884
								$resqlfix=$this->db->query($sqlfix);
2885
								if (! $resqlfix) dol_print_error($this->db, 'Failed to update line');
2886
								$this->total_tva -= $diff;
2887
								$this->total_ttc -= $diff;
2888
								$total_tva_by_vats[$obj->vatrate] -= $diff;
2889
								$total_ttc_by_vats[$obj->vatrate] -= $diff;
2890
					}
2891
				}
2892
2893
				$i++;
2894
			}
2895
2896
			// Add revenue stamp to total
2897
			$this->total_ttc       			+= isset($this->revenuestamp)?$this->revenuestamp:0;
2898
			$this->multicurrency_total_ttc  += isset($this->revenuestamp)?($this->revenuestamp * $multicurrency_tx):0;
2899
2900
			// Situations totals
2901
			if ($this->situation_cycle_ref && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE )
2902
			{
2903
				$prev_sits = $this->get_prev_sits();
2904
2905
				foreach ($prev_sits as $sit) {				// $sit is an object Facture loaded with a fetch.
2906
					$this->total_ht -= $sit->total_ht;
2907
					$this->total_tva -= $sit->total_tva;
2908
					$this->total_localtax1 -= $sit->total_localtax1;
2909
					$this->total_localtax2 -= $sit->total_localtax2;
2910
					$this->total_ttc -= $sit->total_ttc;
2911
					$this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
2912
					$this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
2913
					$this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
2914
				}
2915
			}
2916
2917
			$this->db->free($resql);
2918
2919
			// Now update global field total_ht, total_ttc and tva
2920
			$fieldht='total_ht';
2921
			$fieldtva='tva';
2922
			$fieldlocaltax1='localtax1';
2923
			$fieldlocaltax2='localtax2';
2924
			$fieldttc='total_ttc';
2925
			// Specific code for backward compatibility with old field names
2926
			if ($this->element == 'facture' || $this->element == 'facturerec')             $fieldht='total';
2927
			if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='total_tva';
2928
			if ($this->element == 'propal')                                                $fieldttc='total';
2929
			if ($this->element == 'expensereport')                                         $fieldtva='total_tva';
2930
			if ($this->element == 'supplier_proposal')                                     $fieldttc='total';
2931
2932
			if (empty($nodatabaseupdate))
2933
			{
2934
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2935
				$sql .= " ".$fieldht."='".price2num($this->total_ht)."',";
2936
				$sql .= " ".$fieldtva."='".price2num($this->total_tva)."',";
2937
				$sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',";
2938
				$sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',";
2939
				$sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'";
2940
						$sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'";
2941
						$sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'";
2942
						$sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'";
2943
				$sql .= ' WHERE rowid = '.$this->id;
2944
2945
2946
				dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2947
				$resql=$this->db->query($sql);
2948
				if (! $resql)
2949
				{
2950
					$error++;
2951
					$this->error=$this->db->lasterror();
2952
					$this->errors[]=$this->db->lasterror();
2953
				}
2954
			}
2955
2956
			if (! $error)
2957
			{
2958
				return 1;
2959
			}
2960
			else
2961
			{
2962
				return -1;
2963
			}
2964
		}
2965
		else
2966
		{
2967
			dol_print_error($this->db, 'Bad request in update_price');
2968
			return -1;
2969
		}
2970
	}
2971
2972
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2973
	/**
2974
	 *	Add objects linked in llx_element_element.
2975
	 *
2976
	 *	@param		string	$origin		Linked element type
2977
	 *	@param		int		$origin_id	Linked element id
2978
	 *	@return		int					<=0 if KO, >0 if OK
2979
	 *	@see		fetchObjectLinked(), updateObjectLinked(), deleteObjectLinked()
2980
	 */
2981
	public function add_object_linked($origin = null, $origin_id = null)
2982
	{
2983
		// phpcs:enable
2984
		$origin = (! empty($origin) ? $origin : $this->origin);
2985
		$origin_id = (! empty($origin_id) ? $origin_id : $this->origin_id);
2986
2987
		// Special case
2988
		if ($origin == 'order') $origin='commande';
2989
		if ($origin == 'invoice') $origin='facture';
2990
		if ($origin == 'invoice_template') $origin='facturerec';
2991
		if ($origin == 'supplierorder') $origin='order_supplier';
2992
		$this->db->begin();
2993
2994
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
2995
		$sql.= "fk_source";
2996
		$sql.= ", sourcetype";
2997
		$sql.= ", fk_target";
2998
		$sql.= ", targettype";
2999
		$sql.= ") VALUES (";
3000
		$sql.= $origin_id;
3001
		$sql.= ", '".$this->db->escape($origin)."'";
3002
		$sql.= ", ".$this->id;
3003
		$sql.= ", '".$this->db->escape($this->element)."'";
3004
		$sql.= ")";
3005
3006
		dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG);
3007
		if ($this->db->query($sql))
3008
		{
3009
			$this->db->commit();
3010
			return 1;
3011
		}
3012
		else
3013
		{
3014
			$this->error=$this->db->lasterror();
3015
			$this->db->rollback();
3016
			return 0;
3017
		}
3018
	}
3019
3020
	/**
3021
	 *	Fetch array of objects linked to current object (object of enabled modules only). Links are loaded into
3022
	 *		this->linkedObjectsIds array and
3023
	 *		this->linkedObjects array if $loadalsoobjects = 1
3024
	 *  Possible usage for parameters:
3025
	 *  - all parameters empty -> we look all link to current object (current object can be source or target)
3026
	 *  - source id+type -> will get target list linked to source
3027
	 *  - target id+type -> will get source list linked to target
3028
	 *  - source id+type + target type -> will get target list of the type
3029
	 *  - target id+type + target source -> will get source list of the type
3030
	 *
3031
	 *	@param	int		$sourceid			Object source id (if not defined, id of object)
3032
	 *	@param  string	$sourcetype			Object source type (if not defined, element name of object)
3033
	 *	@param  int		$targetid			Object target id (if not defined, id of object)
3034
	 *	@param  string	$targettype			Object target type (if not defined, elemennt name of object)
3035
	 *	@param  string	$clause				'OR' or 'AND' clause used when both source id and target id are provided
3036
	 *  @param  int		$alsosametype		0=Return only links to object that differs from source type. 1=Include also link to objects of same type.
3037
	 *  @param  string	$orderby			SQL 'ORDER BY' clause
3038
	 *  @param	int		$loadalsoobjects	Load also array this->linkedObjects (Use 0 to increase performances)
3039
	 *	@return int							<0 if KO, >0 if OK
3040
	 *  @see	add_object_linked(), updateObjectLinked(), deleteObjectLinked()
3041
	 */
3042
	public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
3043
	{
3044
		global $conf;
3045
3046
		$this->linkedObjectsIds=array();
3047
		$this->linkedObjects=array();
3048
3049
		$justsource=false;
3050
		$justtarget=false;
3051
		$withtargettype=false;
3052
		$withsourcetype=false;
3053
3054
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid))
3055
		{
3056
			$justsource=true;  // the source (id and type) is a search criteria
3057
			if (! empty($targettype)) $withtargettype=true;
3058
		}
3059
		if (! empty($targetid) && ! empty($targettype) && empty($sourceid))
3060
		{
3061
			$justtarget=true;  // the target (id and type) is a search criteria
3062
			if (! empty($sourcetype)) $withsourcetype=true;
3063
		}
3064
3065
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3066
		$targetid = (! empty($targetid) ? $targetid : $this->id);
3067
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3068
		$targettype = (! empty($targettype) ? $targettype : $this->element);
3069
3070
		/*if (empty($sourceid) && empty($targetid))
3071
		 {
3072
		 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
3073
		 return -1;
3074
		 }*/
3075
3076
		// Links between objects are stored in table element_element
3077
		$sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype';
3078
		$sql.= ' FROM '.MAIN_DB_PREFIX.'element_element';
3079
		$sql.= " WHERE ";
3080
		if ($justsource || $justtarget)
3081
		{
3082
			if ($justsource)
3083
			{
3084
				$sql.= "fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."'";
3085
				if ($withtargettype) $sql.= " AND targettype = '".$targettype."'";
3086
			}
3087
			elseif ($justtarget)
3088
			{
3089
				$sql.= "fk_target = ".$targetid." AND targettype = '".$targettype."'";
3090
				if ($withsourcetype) $sql.= " AND sourcetype = '".$sourcetype."'";
3091
			}
3092
		}
3093
		else
3094
		{
3095
			$sql.= "(fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."')";
3096
			$sql.= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$targettype."')";
3097
		}
3098
		$sql .= ' ORDER BY '.$orderby;
3099
3100
		dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3101
		$resql = $this->db->query($sql);
3102
		if ($resql)
3103
		{
3104
			$num = $this->db->num_rows($resql);
3105
			$i = 0;
3106
			while ($i < $num)
3107
			{
3108
				$obj = $this->db->fetch_object($resql);
3109
				if ($justsource || $justtarget)
3110
				{
3111
					if ($justsource)
3112
					{
3113
						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3114
					}
3115
					elseif ($justtarget)
3116
					{
3117
						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3118
					}
3119
				}
3120
				else
3121
				{
3122
					if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype)
3123
					{
3124
						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3125
					}
3126
					if ($obj->fk_target == $targetid && $obj->targettype == $targettype)
3127
					{
3128
						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3129
					}
3130
				}
3131
				$i++;
3132
			}
3133
3134
			if (! empty($this->linkedObjectsIds))
3135
			{
3136
				$tmparray = $this->linkedObjectsIds;
3137
				foreach($tmparray as $objecttype => $objectids)       // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
3138
				{
3139
					// Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
3140
					$module = $element = $subelement = $objecttype;
3141
					if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
3142
						&& preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs))
3143
					{
3144
						$module = $element = $regs[1];
3145
						$subelement = $regs[2];
3146
					}
3147
3148
					$classpath = $element.'/class';
3149
					// To work with non standard classpath or module name
3150
					if ($objecttype == 'facture')			{
3151
						$classpath = 'compta/facture/class';
3152
					}
3153
					elseif ($objecttype == 'facturerec')			{
3154
						$classpath = 'compta/facture/class'; $module = 'facture';
3155
					}
3156
					elseif ($objecttype == 'propal')			{
3157
						$classpath = 'comm/propal/class';
3158
					}
3159
					elseif ($objecttype == 'supplier_proposal')			{
3160
						$classpath = 'supplier_proposal/class';
3161
					}
3162
					elseif ($objecttype == 'shipping')			{
3163
						$classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon';
3164
					}
3165
					elseif ($objecttype == 'delivery')			{
3166
						$classpath = 'livraison/class'; $subelement = 'livraison'; $module = 'livraison_bon';
3167
					}
3168
					elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier')	{
3169
						$classpath = 'fourn/class'; $module = 'fournisseur';
3170
					}
3171
					elseif ($objecttype == 'fichinter')			{
3172
						$classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter';
3173
					}
3174
					elseif ($objecttype == 'subscription')			{
3175
						$classpath = 'adherents/class'; $module = 'adherent';
3176
					}
3177
3178
					// Set classfile
3179
					$classfile = strtolower($subelement); $classname = ucfirst($subelement);
3180
3181
					if ($objecttype == 'order') {
3182
						$classfile = 'commande'; $classname = 'Commande';
3183
					}
3184
					elseif ($objecttype == 'invoice_supplier') {
3185
						$classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur';
3186
					}
3187
					elseif ($objecttype == 'order_supplier')   {
3188
						$classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur';
3189
					}
3190
					elseif ($objecttype == 'supplier_proposal')   {
3191
						$classfile = 'supplier_proposal'; $classname = 'SupplierProposal';
3192
					}
3193
					elseif ($objecttype == 'facturerec')   {
3194
						$classfile = 'facture-rec'; $classname = 'FactureRec';
3195
					}
3196
					elseif ($objecttype == 'subscription')   {
3197
						$classfile = 'subscription'; $classname = 'Subscription';
3198
					}
3199
3200
					// Here $module, $classfile and $classname are set
3201
					if ($conf->$module->enabled && (($element != $this->element) || $alsosametype))
3202
					{
3203
						if ($loadalsoobjects)
3204
						{
3205
							dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
3206
							//print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
3207
							if (class_exists($classname))
3208
							{
3209
								foreach($objectids as $i => $objectid)	// $i is rowid into llx_element_element
3210
								{
3211
									$object = new $classname($this->db);
3212
									$ret = $object->fetch($objectid);
3213
									if ($ret >= 0)
3214
									{
3215
										$this->linkedObjects[$objecttype][$i] = $object;
3216
									}
3217
								}
3218
							}
3219
						}
3220
					}
3221
					else
3222
					{
3223
						unset($this->linkedObjectsIds[$objecttype]);
3224
					}
3225
				}
3226
			}
3227
			return 1;
3228
		}
3229
		else
3230
		{
3231
			dol_print_error($this->db);
3232
			return -1;
3233
		}
3234
	}
3235
3236
	/**
3237
	 *	Update object linked of a current object
3238
	 *
3239
	 *	@param	int		$sourceid		Object source id
3240
	 *	@param  string	$sourcetype		Object source type
3241
	 *	@param  int		$targetid		Object target id
3242
	 *	@param  string	$targettype		Object target type
3243
	 *	@return							int	>0 if OK, <0 if KO
3244
	 *	@see	add_object_linked(), fetObjectLinked(), deleteObjectLinked()
3245
	 */
3246
	public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '')
3247
	{
3248
		$updatesource=false;
3249
		$updatetarget=false;
3250
3251
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource=true;
3252
		elseif (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $updatetarget=true;
3253
3254
		$sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET ";
3255
		if ($updatesource)
3256
		{
3257
			$sql.= "fk_source = ".$sourceid;
3258
			$sql.= ", sourcetype = '".$this->db->escape($sourcetype)."'";
3259
			$sql.= " WHERE fk_target = ".$this->id;
3260
			$sql.= " AND targettype = '".$this->db->escape($this->element)."'";
3261
		}
3262
		elseif ($updatetarget)
3263
		{
3264
			$sql.= "fk_target = ".$targetid;
3265
			$sql.= ", targettype = '".$this->db->escape($targettype)."'";
3266
			$sql.= " WHERE fk_source = ".$this->id;
3267
			$sql.= " AND sourcetype = '".$this->db->escape($this->element)."'";
3268
		}
3269
3270
		dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG);
3271
		if ($this->db->query($sql))
3272
		{
3273
			return 1;
3274
		}
3275
		else
3276
		{
3277
			$this->error=$this->db->lasterror();
3278
			return -1;
3279
		}
3280
	}
3281
3282
	/**
3283
	 *	Delete all links between an object $this
3284
	 *
3285
	 *	@param	int		$sourceid		Object source id
3286
	 *	@param  string	$sourcetype		Object source type
3287
	 *	@param  int		$targetid		Object target id
3288
	 *	@param  string	$targettype		Object target type
3289
	 *  @param	int		$rowid			Row id of line to delete. If defined, other parameters are not used.
3290
	 *	@return     					int	>0 if OK, <0 if KO
3291
	 *	@see	add_object_linked(), updateObjectLinked(), fetchObjectLinked()
3292
	 */
3293
	public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = '')
3294
	{
3295
		$deletesource=false;
3296
		$deletetarget=false;
3297
3298
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource=true;
3299
		elseif (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $deletetarget=true;
3300
3301
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3302
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3303
		$targetid = (! empty($targetid) ? $targetid : $this->id);
3304
		$targettype = (! empty($targettype) ? $targettype : $this->element);
3305
3306
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element";
3307
		$sql.= " WHERE";
3308
		if ($rowid > 0)
3309
		{
3310
			$sql.=" rowid = ".$rowid;
3311
		}
3312
		else
3313
		{
3314
			if ($deletesource)
3315
			{
3316
				$sql.= " fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3317
				$sql.= " AND fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."'";
3318
			}
3319
			elseif ($deletetarget)
3320
			{
3321
				$sql.= " fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'";
3322
				$sql.= " AND fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."'";
3323
			}
3324
			else
3325
			{
3326
				$sql.= " (fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."')";
3327
				$sql.= " OR";
3328
				$sql.= " (fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."')";
3329
			}
3330
		}
3331
3332
		dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG);
3333
		if ($this->db->query($sql))
3334
		{
3335
			return 1;
3336
		}
3337
		else
3338
		{
3339
			$this->error=$this->db->lasterror();
3340
			$this->errors[]=$this->error;
3341
			return -1;
3342
		}
3343
	}
3344
3345
	/**
3346
	 *      Set status of an object
3347
	 *
3348
	 *      @param	int		$status			Status to set
3349
	 *      @param	int		$elementId		Id of element to force (use this->id by default)
3350
	 *      @param	string	$elementType	Type of element to force (use this->table_element by default)
3351
	 *      @param	string	$trigkey		Trigger key to use for trigger
3352
	 *      @return int						<0 if KO, >0 if OK
3353
	 */
3354
	public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '')
3355
	{
3356
		global $user,$langs,$conf;
3357
3358
		$savElementId=$elementId;  // To be used later to know if we were using the method using the id of this or not.
3359
3360
		$elementId = (!empty($elementId)?$elementId:$this->id);
3361
		$elementTable = (!empty($elementType)?$elementType:$this->table_element);
3362
3363
		$this->db->begin();
3364
3365
		$fieldstatus="fk_statut";
3366
		if ($elementTable == 'facture_rec') $fieldstatus="suspended";
3367
		if ($elementTable == 'mailing') $fieldstatus="statut";
3368
		if ($elementTable == 'cronjob') $fieldstatus="status";
3369
		if ($elementTable == 'user') $fieldstatus="statut";
3370
		if ($elementTable == 'expensereport') $fieldstatus="fk_statut";
3371
		if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status";
3372
3373
		$sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable;
3374
		$sql.= " SET ".$fieldstatus." = ".$status;
3375
		// If status = 1 = validated, update also fk_user_valid
3376
		if ($status == 1 && $elementTable == 'expensereport') $sql.=", fk_user_valid = ".$user->id;
3377
		$sql.= " WHERE rowid=".$elementId;
3378
3379
		dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
3380
		if ($this->db->query($sql))
3381
		{
3382
			$error = 0;
3383
3384
			// Try autoset of trigkey
3385
			if (empty($trigkey))
3386
			{
3387
				if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_SIGN';   // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
3388
				if ($this->element == 'supplier_proposal' && $status == 3) $trigkey='SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
3389
				if ($this->element == 'supplier_proposal' && $status == 4) $trigkey='SUPPLIER_PROPOSAL_CLOSE';  // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
3390
				if ($this->element == 'fichinter' && $status == 3) $trigkey='FICHINTER_CLASSIFY_DONE';
3391
				if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED';
3392
				if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED';
3393
			}
3394
3395
			if ($trigkey)
3396
			{
3397
				// Appel des triggers
3398
				include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
3399
				$interface=new Interfaces($this->db);
3400
				$result=$interface->run_triggers($trigkey, $this, $user, $langs, $conf);
3401
				if ($result < 0) {
3402
					$error++; $this->errors=$interface->errors;
3403
				}
3404
				// Fin appel triggers
3405
			}
3406
3407
			if (! $error)
3408
			{
3409
				$this->db->commit();
3410
3411
				if (empty($savElementId))    // If the element we update was $this (so $elementId is null)
3412
				{
3413
					$this->statut = $status;
3414
					$this->status = $status;
3415
				}
3416
3417
				return 1;
3418
			}
3419
			else
3420
			{
3421
				$this->db->rollback();
3422
				dol_syslog(get_class($this)."::setStatus ".$this->error, LOG_ERR);
3423
				return -1;
3424
			}
3425
		}
3426
		else
3427
		{
3428
			$this->error=$this->db->lasterror();
3429
			$this->db->rollback();
3430
			return -1;
3431
		}
3432
	}
3433
3434
3435
	/**
3436
	 *  Load type of canvas of an object if it exists
3437
	 *
3438
	 *  @param      int		$id     Record id
3439
	 *  @param      string	$ref    Record ref
3440
	 *  @return		int				<0 if KO, 0 if nothing done, >0 if OK
3441
	 */
3442
	public function getCanvas($id = 0, $ref = '')
3443
	{
3444
		global $conf;
3445
3446
		if (empty($id) && empty($ref)) return 0;
3447
		if (! empty($conf->global->MAIN_DISABLE_CANVAS)) return 0;    // To increase speed. Not enabled by default.
3448
3449
		// Clean parameters
3450
		$ref = trim($ref);
3451
3452
		$sql = "SELECT rowid, canvas";
3453
		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
3454
		$sql.= " WHERE entity IN (".getEntity($this->element).")";
3455
		if (! empty($id))  $sql.= " AND rowid = ".$id;
3456
		if (! empty($ref)) $sql.= " AND ref = '".$this->db->escape($ref)."'";
3457
3458
		$resql = $this->db->query($sql);
3459
		if ($resql)
3460
		{
3461
			$obj = $this->db->fetch_object($resql);
3462
			if ($obj)
3463
			{
3464
				$this->canvas   = $obj->canvas;
3465
				return 1;
3466
			}
3467
			else return 0;
3468
		}
3469
		else
3470
		{
3471
			dol_print_error($this->db);
3472
			return -1;
3473
		}
3474
	}
3475
3476
3477
	/**
3478
	 * 	Get special code of a line
3479
	 *
3480
	 * 	@param	int		$lineid		Id of line
3481
	 * 	@return	int					Special code
3482
	 */
3483
	public function getSpecialCode($lineid)
3484
	{
3485
		$sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line;
3486
		$sql.= ' WHERE rowid = '.$lineid;
3487
		$resql = $this->db->query($sql);
3488
		if ($resql)
3489
		{
3490
			$row = $this->db->fetch_row($resql);
3491
			return $row[0];
3492
		}
3493
	}
3494
3495
	/**
3496
	 *  Function to check if an object is used by others.
3497
	 *  Check is done into this->childtables. There is no check into llx_element_element.
3498
	 *
3499
	 *  @param	int		$id			Force id of object
3500
	 *  @return	int					<0 if KO, 0 if not used, >0 if already used
3501
	 */
3502
	public function isObjectUsed($id = 0)
3503
	{
3504
		global $langs;
3505
3506
		if (empty($id)) $id=$this->id;
3507
3508
		// Check parameters
3509
		if (! isset($this->childtables) || ! is_array($this->childtables) || count($this->childtables) == 0)
3510
		{
3511
			dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
3512
			return -1;
3513
		}
3514
3515
		$arraytoscan = $this->childtables;
3516
		// For backward compatibility, we check if array is old format array('table1', 'table2', ...)
3517
		$tmparray=array_keys($this->childtables);
3518
		if (is_numeric($tmparray[0]))
3519
		{
3520
			$arraytoscan = array_flip($this->childtables);
3521
		}
3522
3523
		// Test if child exists
3524
		$haschild=0;
3525
		foreach($arraytoscan as $table => $elementname)
3526
		{
3527
			//print $id.'-'.$table.'-'.$elementname.'<br>';
3528
			// Check if third party can be deleted
3529
			$sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table;
3530
			$sql.= " WHERE ".$this->fk_element." = ".$id;
3531
			$resql=$this->db->query($sql);
3532
			if ($resql)
3533
			{
3534
				$obj=$this->db->fetch_object($resql);
3535
				if ($obj->nb > 0)
3536
				{
3537
					$langs->load("errors");
3538
					//print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
3539
					$haschild += $obj->nb;
3540
					if (is_numeric($elementname))	// old usage
3541
					{
3542
						$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $table);
3543
					}
3544
					else	// new usage: $elementname=Translation key
3545
					{
3546
						$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $langs->transnoentitiesnoconv($elementname));
3547
					}
3548
					break;    // We found at least one, we stop here
3549
				}
3550
			}
3551
			else
3552
			{
3553
				$this->errors[]=$this->db->lasterror();
3554
				return -1;
3555
			}
3556
		}
3557
		if ($haschild > 0)
3558
		{
3559
			$this->errors[]="ErrorRecordHasChildren";
3560
			return $haschild;
3561
		}
3562
		else return 0;
3563
	}
3564
3565
	/**
3566
	 *  Function to say how many lines object contains
3567
	 *
3568
	 *	@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
3569
	 *  @return	int						<0 if KO, 0 if no predefined products, nb of lines with predefined products if found
3570
	 */
3571
	public function hasProductsOrServices($predefined = -1)
3572
	{
3573
		$nb=0;
3574
3575
		foreach($this->lines as $key => $val)
3576
		{
3577
			$qualified=0;
3578
			if ($predefined == -1) $qualified=1;
3579
			if ($predefined == 1 && $val->fk_product > 0) $qualified=1;
3580
			if ($predefined == 0 && $val->fk_product <= 0) $qualified=1;
3581
			if ($predefined == 2 && $val->fk_product > 0 && $val->product_type==0) $qualified=1;
3582
			if ($predefined == 3 && $val->fk_product > 0 && $val->product_type==1) $qualified=1;
3583
			if ($qualified) $nb++;
3584
		}
3585
		dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
3586
		return $nb;
3587
	}
3588
3589
	/**
3590
	 * Function that returns the total amount HT of discounts applied for all lines.
3591
	 *
3592
	 * @return 	float
3593
	 */
3594
	public function getTotalDiscount()
3595
	{
3596
		$total_discount=0.00;
3597
3598
		$sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
3599
		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element."det";
3600
		$sql.= " WHERE ".$this->fk_element." = ".$this->id;
3601
3602
		dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
3603
		$resql = $this->db->query($sql);
3604
		if ($resql)
3605
		{
3606
			$num=$this->db->num_rows($resql);
3607
			$i=0;
3608
			while ($i < $num)
3609
			{
3610
				$obj = $this->db->fetch_object($resql);
3611
3612
				$pu_ht = $obj->pu_ht;
3613
				$qty= $obj->qty;
3614
				$total_ht = $obj->total_ht;
3615
3616
				$total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
3617
				$total_discount += $total_discount_line;
3618
3619
				$i++;
3620
			}
3621
		}
3622
3623
		//print $total_discount; exit;
3624
		return price2num($total_discount);
3625
	}
3626
3627
3628
	/**
3629
	 * Return into unit=0, the calculated total of weight and volume of all lines * qty
3630
	 * Calculate by adding weight and volume of each product line, so properties ->volume/volume_units/weight/weight_units must be loaded on line.
3631
	 *
3632
	 * @return  array                           array('weight'=>...,'volume'=>...)
3633
	 */
3634
	public function getTotalWeightVolume()
3635
	{
3636
		$totalWeight = 0;
3637
		$totalVolume = 0;
3638
		// defined for shipment only
3639
		$totalOrdered = '';
3640
		// defined for shipment only
3641
		$totalToShip = '';
3642
3643
		foreach ($this->lines as $line)
3644
		{
3645
			if (isset($line->qty_asked))
3646
			{
3647
				if (empty($totalOrdered)) $totalOrdered=0;  // Avoid warning because $totalOrdered is ''
3648
				$totalOrdered+=$line->qty_asked;    // defined for shipment only
3649
			}
3650
			if (isset($line->qty_shipped))
3651
			{
3652
				if (empty($totalToShip)) $totalToShip=0;    // Avoid warning because $totalToShip is ''
3653
				$totalToShip+=$line->qty_shipped;   // defined for shipment only
3654
			}
3655
			elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty))
3656
			{
3657
				if (empty($totalToShip)) $totalToShip=0;
3658
				$totalToShip+=$line->qty;   // defined for reception only
3659
			}
3660
3661
			// Define qty, weight, volume, weight_units, volume_units
3662
			if ($this->element == 'shipping') {
3663
				// for shipments
3664
				$qty = $line->qty_shipped ? $line->qty_shipped : 0;
3665
			}
3666
			else {
3667
				$qty = $line->qty ? $line->qty : 0;
3668
			}
3669
3670
			$weight = $line->weight ? $line->weight : 0;
3671
            ($weight==0 && !empty($line->product->weight))? $weight=$line->product->weight: 0;
3672
			$volume = $line->volume ? $line->volume : 0;
3673
			($volume==0 && !empty($line->product->volume))? $volume=$line->product->volume: 0;
3674
3675
			$weight_units=$line->weight_units;
3676
			($weight_units==0 && !empty($line->product->weight_units))? $weight_units=$line->product->weight_units: 0;
3677
			$volume_units=$line->volume_units;
3678
			($volume_units==0 && !empty($line->product->volume_units))? $volume_units=$line->product->volume_units: 0;
3679
3680
			$weightUnit=0;
3681
			$volumeUnit=0;
3682
			if (! empty($weight_units)) $weightUnit = $weight_units;
3683
			if (! empty($volume_units)) $volumeUnit = $volume_units;
3684
3685
			if (empty($totalWeight)) $totalWeight=0;  // Avoid warning because $totalWeight is ''
3686
			if (empty($totalVolume)) $totalVolume=0;  // Avoid warning because $totalVolume is ''
3687
3688
			//var_dump($line->volume_units);
3689
			if ($weight_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3690
			{
3691
				$trueWeightUnit=pow(10, $weightUnit);
3692
				$totalWeight += $weight * $qty * $trueWeightUnit;
3693
			}
3694
			else {
3695
		if ($weight_units == 99) {
3696
			// conversion 1 Pound = 0.45359237 KG
3697
			$trueWeightUnit = 0.45359237;
3698
			$totalWeight += $weight * $qty * $trueWeightUnit;
3699
		} elseif ($weight_units == 98) {
3700
			// conversion 1 Ounce = 0.0283495 KG
3701
			$trueWeightUnit = 0.0283495;
3702
			$totalWeight += $weight * $qty * $trueWeightUnit;
3703
		}
3704
		else
3705
					$totalWeight += $weight * $qty;   // This may be wrong if we mix different units
3706
			}
3707
			if ($volume_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3708
			{
3709
				//print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
3710
				$trueVolumeUnit=pow(10, $volumeUnit);
3711
				//print $line->volume;
3712
				$totalVolume += $volume * $qty * $trueVolumeUnit;
3713
			}
3714
			else
3715
			{
3716
				$totalVolume += $volume * $qty;   // This may be wrong if we mix different units
3717
			}
3718
		}
3719
3720
		return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
3721
	}
3722
3723
3724
	/**
3725
	 *	Set extra parameters
3726
	 *
3727
	 *	@return	int      <0 if KO, >0 if OK
3728
	 */
3729
	public function setExtraParameters()
3730
	{
3731
		$this->db->begin();
3732
3733
		$extraparams = (! empty($this->extraparams) ? json_encode($this->extraparams) : null);
3734
3735
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3736
		$sql.= " SET extraparams = ".(! empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
3737
		$sql.= " WHERE rowid = ".$this->id;
3738
3739
		dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
3740
		$resql = $this->db->query($sql);
3741
		if (! $resql)
3742
		{
3743
			$this->error=$this->db->lasterror();
3744
			$this->db->rollback();
3745
			return -1;
3746
		}
3747
		else
3748
		{
3749
			$this->db->commit();
3750
			return 1;
3751
		}
3752
	}
3753
3754
3755
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3756
	/**
3757
	 *    Return incoterms informations
3758
	 *    TODO Use a cache for label get
3759
	 *
3760
	 *    @return	string	incoterms info
3761
	 */
3762
	public function display_incoterms()
3763
	{
3764
        // phpcs:enable
3765
		$out = '';
3766
		$this->label_incoterms = '';
3767
		if (!empty($this->fk_incoterms))
3768
		{
3769
			$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3770
			$result = $this->db->query($sql);
3771
			if ($result)
3772
			{
3773
				$res = $this->db->fetch_object($result);
3774
				$out .= $res->code;
3775
			}
3776
		}
3777
3778
		$out .= (($res->code && $this->location_incoterms)?' - ':'').$this->location_incoterms;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $res does not seem to be defined for all execution paths leading up to this point.
Loading history...
3779
3780
		return $out;
3781
	}
3782
3783
	/**
3784
	 *    Return incoterms informations for pdf display
3785
	 *
3786
	 *    @return	string		incoterms info
3787
	 */
3788
	public function getIncotermsForPDF()
3789
	{
3790
		$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3791
		$resql = $this->db->query($sql);
3792
		if ($resql)
3793
		{
3794
			$num = $this->db->num_rows($resql);
3795
			if ($num > 0)
3796
			{
3797
				$res = $this->db->fetch_object($resql);
3798
				return 'Incoterm : '.$res->code.' - '.$this->location_incoterms;
3799
			}
3800
			else
3801
			{
3802
				return '';
3803
			}
3804
		}
3805
		else
3806
		{
3807
			$this->errors[] = $this->db->lasterror();
3808
			return false;
3809
		}
3810
	}
3811
3812
	/**
3813
	 *    Define incoterms values of current object
3814
	 *
3815
	 *    @param	int		$id_incoterm     Id of incoterm to set or '' to remove
3816
	 * 	  @param 	string  $location		 location of incoterm
3817
	 *    @return	int     		<0 if KO, >0 if OK
3818
	 */
3819
	public function setIncoterms($id_incoterm, $location)
3820
	{
3821
		if ($this->id && $this->table_element)
3822
		{
3823
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3824
			$sql.= " SET fk_incoterms = ".($id_incoterm > 0 ? $id_incoterm : "null");
3825
			$sql.= ", location_incoterms = ".($id_incoterm > 0 ? "'".$this->db->escape($location)."'" : "null");
3826
			$sql.= " WHERE rowid = " . $this->id;
3827
			dol_syslog(get_class($this).'::setIncoterms', LOG_DEBUG);
3828
			$resql=$this->db->query($sql);
3829
			if ($resql)
3830
			{
3831
				$this->fk_incoterms = $id_incoterm;
3832
				$this->location_incoterms = $location;
3833
3834
				$sql = 'SELECT libelle FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3835
				$res = $this->db->query($sql);
3836
				if ($res)
3837
				{
3838
					$obj = $this->db->fetch_object($res);
3839
					$this->label_incoterms = $obj->libelle;
3840
				}
3841
				return 1;
3842
			}
3843
			else
3844
			{
3845
				$this->errors[] = $this->db->lasterror();
3846
				return -1;
3847
			}
3848
		}
3849
		else return -1;
3850
	}
3851
3852
3853
	// --------------------
3854
	// TODO: All functions here must be redesigned and moved as they are not business functions but output functions
3855
	// --------------------
3856
3857
	/* This is to show add lines */
3858
3859
	/**
3860
	 *	Show add free and predefined products/services form
3861
	 *
3862
	 *  @param	int		        $dateSelector       1=Show also date range input fields
3863
	 *  @param	Societe			$seller				Object thirdparty who sell
3864
	 *  @param	Societe			$buyer				Object thirdparty who buy
3865
	 *  @param	string			$defaulttpldir		Directory where to find the template
3866
	 *	@return	void
3867
	 */
3868
	public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
3869
	{
3870
		global $conf,$user,$langs,$object,$hookmanager;
3871
		global $form,$bcnd,$var;
3872
3873
		// Line extrafield
3874
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3875
		$extrafieldsline = new ExtraFields($this->db);
3876
		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3877
3878
		// Output template part (modules that overwrite templates must declare this into descriptor)
3879
		// Use global variables + $dateSelector + $seller and $buyer
3880
		$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
3881
		foreach($dirtpls as $module => $reldir)
3882
		{
3883
			if (!empty($module))
3884
			{
3885
				$tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
3886
			}
3887
			else
3888
			{
3889
				$tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
3890
			}
3891
3892
			if (empty($conf->file->strict_mode)) {
3893
				$res=@include $tpl;
3894
			} else {
3895
				$res=include $tpl; // for debug
3896
			}
3897
			if ($res) break;
3898
		}
3899
	}
3900
3901
3902
3903
	/* This is to show array of line of details */
3904
3905
3906
	/**
3907
	 *	Return HTML table for object lines
3908
	 *	TODO Move this into an output class file (htmlline.class.php)
3909
	 *	If lines are into a template, title must also be into a template
3910
	 *	But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
3911
	 *
3912
	 *	@param	string		$action				Action code
3913
	 *	@param  string		$seller            	Object of seller third party
3914
	 *	@param  string  	$buyer             	Object of buyer third party
3915
	 *	@param	int			$selected		   	Object line selected
3916
	 *	@param  int	    	$dateSelector      	1=Show also date range input fields
3917
	 *  @param	string		$defaulttpldir		Directory where to find the template
3918
	 *	@return	void
3919
	 */
3920
	public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
3921
	{
3922
		global $conf, $hookmanager, $langs, $user, $object, $form;
3923
		// TODO We should not use global var for this
3924
		global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
3925
3926
		// Define usemargins
3927
		$usemargins=0;
3928
		if (! empty($conf->margin->enabled) && ! empty($this->element) && in_array($this->element, array('facture','facturerec','propal','commande'))) $usemargins=1;
3929
3930
		$num = count($this->lines);
3931
3932
		// Line extrafield
3933
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3934
		$extrafieldsline = new ExtraFields($this->db);
3935
		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3936
3937
		$parameters = array('num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $i seems to be never defined.
Loading history...
3938
		$reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3939
		if (empty($reshook))
3940
		{
3941
			// Output template part (modules that overwrite templates must declare this into descriptor)
3942
			// Use global variables + $dateSelector + $seller and $buyer
3943
			$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
3944
			foreach($dirtpls as $module => $reldir)
3945
			{
3946
				if (!empty($module))
3947
				{
3948
					$tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
3949
				}
3950
				else
3951
				{
3952
					$tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
3953
				}
3954
				if (empty($conf->file->strict_mode)) {
3955
					$res=@include $tpl;
3956
				} else {
3957
					$res=include $tpl; // for debug
3958
				}
3959
				if ($res) break;
3960
			}
3961
		}
3962
3963
		$var = true;
3964
		$i	 = 0;
3965
3966
		print "<tbody>\n";
3967
		foreach ($this->lines as $line)
3968
		{
3969
			//Line extrafield
3970
			$line->fetch_optionals();
3971
3972
			//if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
3973
			if (is_object($hookmanager))   // Old code is commented on preceding line.
3974
			{
3975
				if (empty($line->fk_parent_line))
3976
				{
3977
					$parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
3978
					$reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3979
				}
3980
				else
3981
				{
3982
					$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);
3983
					$reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3984
				}
3985
			}
3986
			if (empty($reshook))
3987
			{
3988
				$this->printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafieldsline, $defaulttpldir);
3989
			}
3990
3991
			$i++;
3992
		}
3993
		print "</tbody><!-- end printObjectLines() -->\n";
3994
	}
3995
3996
	/**
3997
	 *	Return HTML content of a detail line
3998
	 *	TODO Move this into an output class file (htmlline.class.php)
3999
	 *
4000
	 *	@param	string      $action				GET/POST action
4001
	 *	@param  CommonObjectLine $line		       	Selected object line to output
4002
	 *	@param  string	    $var               	Is it a an odd line (true)
4003
	 *	@param  int		    $num               	Number of line (0)
4004
	 *	@param  int		    $i					I
4005
	 *	@param  int		    $dateSelector      	1=Show also date range input fields
4006
	 *	@param  string	    $seller            	Object of seller third party
4007
	 *	@param  string	    $buyer             	Object of buyer third party
4008
	 *	@param	int			$selected		   	Object line selected
4009
	 *  @param  int			$extrafieldsline	Object of extrafield line attribute
4010
	 *  @param	string		$defaulttpldir		Directory where to find the template
4011
	 *	@return	void
4012
	 */
4013
	public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafieldsline = 0, $defaulttpldir = '/core/tpl')
4014
	{
4015
		global $conf,$langs,$user,$object,$hookmanager;
4016
		global $form,$bc,$bcdd;
4017
		global $object_rights, $disableedit, $disablemove, $disableremove;   // TODO We should not use global var for this !
4018
4019
		$object_rights = $this->getRights();
4020
4021
		$element=$this->element;
4022
4023
		$text=''; $description=''; $type=0;
4024
4025
		// Show product and description
4026
		$type=(! empty($line->product_type)?$line->product_type:$line->fk_product_type);
4027
		// Try to enhance type detection using date_start and date_end for free lines where type was not saved.
4028
		if (! empty($line->date_start)) $type=1; // deprecated
4029
		if (! empty($line->date_end)) $type=1; // deprecated
4030
4031
		// Ligne en mode visu
4032
		if ($action != 'editline' || $selected != $line->id)
4033
		{
4034
			// Product
4035
			if ($line->fk_product > 0)
4036
			{
4037
				$product_static = new Product($this->db);
4038
				$product_static->fetch($line->fk_product);
4039
4040
				$product_static->ref = $line->ref; //can change ref in hook
4041
				$product_static->label = $line->label; //can change label in hook
4042
				$text=$product_static->getNomUrl(1);
4043
4044
				// Define output language and label
4045
				if (! empty($conf->global->MAIN_MULTILANGS))
4046
				{
4047
					if (property_exists($this, 'socid') && ! is_object($this->thirdparty))
4048
					{
4049
						dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
4050
						return;
4051
					}
4052
4053
					$prod = new Product($this->db);
4054
					$prod->fetch($line->fk_product);
4055
4056
					$outputlangs = $langs;
4057
					$newlang='';
4058
					if (empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang=GETPOST('lang_id', 'aZ09');
4059
					if (! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang) && is_object($this->thirdparty)) $newlang=$this->thirdparty->default_lang;		// To use language of customer
4060
					if (! empty($newlang))
4061
					{
4062
						$outputlangs = new Translate("", $conf);
4063
						$outputlangs->setDefaultLang($newlang);
4064
					}
4065
4066
					$label = (! empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
4067
				}
4068
				else
4069
				{
4070
					$label = $line->product_label;
4071
				}
4072
4073
				$text.= ' - '.(! empty($line->label)?$line->label:$label);
4074
				$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.
4075
			}
4076
4077
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4078
4079
			// Output template part (modules that overwrite templates must declare this into descriptor)
4080
			// Use global variables + $dateSelector + $seller and $buyer
4081
			$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4082
			foreach($dirtpls as $module => $reldir)
4083
			{
4084
				if (!empty($module))
4085
				{
4086
					$tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
4087
				}
4088
				else
4089
				{
4090
					$tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
4091
				}
4092
4093
				if (empty($conf->file->strict_mode)) {
4094
					$res=@include $tpl;
4095
				} else {
4096
					$res=include $tpl; // for debug
4097
				}
4098
				if ($res) break;
4099
			}
4100
		}
4101
4102
		// Line in update mode
4103
		if ($this->statut == 0 && $action == 'editline' && $selected == $line->id)
4104
		{
4105
			$label = (! empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
4106
			$placeholder=' placeholder="'.$langs->trans("Label").'"';
4107
4108
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4109
4110
			// Output template part (modules that overwrite templates must declare this into descriptor)
4111
			// Use global variables + $dateSelector + $seller and $buyer
4112
			$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4113
			foreach($dirtpls as $module => $reldir)
4114
			{
4115
				if (!empty($module))
4116
				{
4117
					$tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
4118
				}
4119
				else
4120
				{
4121
					$tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
4122
				}
4123
4124
				if (empty($conf->file->strict_mode)) {
4125
					$res=@include $tpl;
4126
				} else {
4127
					$res=include $tpl; // for debug
4128
				}
4129
				if ($res) break;
4130
			}
4131
		}
4132
	}
4133
4134
4135
	/* This is to show array of line of details of source object */
4136
4137
4138
	/**
4139
	 * 	Return HTML table table of source object lines
4140
	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
4141
	 *  If lines are into a template, title must also be into a template
4142
	 *  But for the moment we don't know if it's possible, so we keep the method available on overloaded objects.
4143
	 *
4144
	 *	@param	string		$restrictlist		''=All lines, 'services'=Restrict to services only
4145
	 *  @return	void
4146
	 */
4147
	public function printOriginLinesList($restrictlist = '')
4148
	{
4149
		global $langs, $hookmanager, $conf;
4150
4151
		print '<tr class="liste_titre">';
4152
		print '<td>'.$langs->trans('Ref').'</td>';
4153
		print '<td>'.$langs->trans('Description').'</td>';
4154
		print '<td class="right">'.$langs->trans('VATRate').'</td>';
4155
		print '<td class="right">'.$langs->trans('PriceUHT').'</td>';
4156
		if (!empty($conf->multicurrency->enabled)) print '<td class="right">'.$langs->trans('PriceUHTCurrency').'</td>';
4157
		print '<td class="right">'.$langs->trans('Qty').'</td>';
4158
		if($conf->global->PRODUCT_USE_UNITS)
4159
		{
4160
			print '<td class="left">'.$langs->trans('Unit').'</td>';
4161
		}
4162
		print '<td class="right">'.$langs->trans('ReductionShort').'</td></tr>';
4163
4164
		$var = true;
4165
		$i	 = 0;
4166
4167
		if (! empty($this->lines))
4168
		{
4169
			foreach ($this->lines as $line)
4170
			{
4171
				if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
4172
				{
4173
					if (empty($line->fk_parent_line))
4174
					{
4175
						$parameters=array('line'=>$line,'var'=>$var,'i'=>$i);
4176
						$action='';
4177
						$hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
4178
					}
4179
				}
4180
				else
4181
				{
4182
					$this->printOriginLine($line, $var, $restrictlist);
4183
				}
4184
4185
				$i++;
4186
			}
4187
		}
4188
	}
4189
4190
	/**
4191
	 * 	Return HTML with a line of table array of source object lines
4192
	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
4193
	 *  If lines are into a template, title must also be into a template
4194
	 *  But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
4195
	 *
4196
	 * 	@param	CommonObjectLine	$line				Line
4197
	 * 	@param	string				$var				Var
4198
	 *	@param	string				$restrictlist		''=All lines, 'services'=Restrict to services only (strike line if not)
4199
	 *  @param	string				$defaulttpldir		Directory where to find the template
4200
	 * 	@return	void
4201
	 */
4202
	public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl')
4203
	{
4204
		global $langs, $conf;
4205
4206
		//var_dump($line);
4207
		if (!empty($line->date_start))
4208
		{
4209
			$date_start=$line->date_start;
4210
		}
4211
		else
4212
		{
4213
			$date_start=$line->date_debut_prevue;
0 ignored issues
show
Bug introduced by
The property date_debut_prevue does not seem to exist on CommonObjectLine.
Loading history...
4214
			if ($line->date_debut_reel) $date_start=$line->date_debut_reel;
0 ignored issues
show
Bug introduced by
The property date_debut_reel does not seem to exist on CommonObjectLine.
Loading history...
4215
		}
4216
		if (!empty($line->date_end))
4217
		{
4218
			$date_end=$line->date_end;
4219
		}
4220
		else
4221
		{
4222
			$date_end=$line->date_fin_prevue;
0 ignored issues
show
Bug introduced by
The property date_fin_prevue does not seem to exist on CommonObjectLine.
Loading history...
4223
			if ($line->date_fin_reel) $date_end=$line->date_fin_reel;
0 ignored issues
show
Bug introduced by
The property date_fin_reel does not seem to exist on CommonObjectLine.
Loading history...
4224
		}
4225
4226
		$this->tpl['label'] = '';
4227
		if (! empty($line->fk_parent_line)) $this->tpl['label'].= img_picto('', 'rightarrow');
4228
4229
		if (($line->info_bits & 2) == 2)  // TODO Not sure this is used for source object
4230
		{
4231
			$discount=new DiscountAbsolute($this->db);
4232
			$discount->fk_soc = $this->socid;
4233
			$this->tpl['label'].= $discount->getNomUrl(0, 'discount');
4234
		}
4235
		elseif (! empty($line->fk_product))
4236
		{
4237
			$productstatic = new Product($this->db);
4238
			$productstatic->id = $line->fk_product;
4239
			$productstatic->ref = $line->ref;
4240
			$productstatic->type = $line->fk_product_type;
4241
			if (empty($productstatic->ref)) {
4242
				$line->fetch_product();
4243
				$productstatic = $line->product;
4244
			}
4245
4246
			$this->tpl['label'].= $productstatic->getNomUrl(1);
4247
			$this->tpl['label'].= ' - '.(! empty($line->label)?$line->label:$line->product_label);
4248
			// Dates
4249
			if ($line->product_type == 1 && ($date_start || $date_end))
4250
			{
4251
				$this->tpl['label'].= get_date_range($date_start, $date_end);
4252
			}
4253
		}
4254
		else
4255
		{
4256
			$this->tpl['label'].= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
4257
			if (!empty($line->desc)) {
4258
				$this->tpl['label'].=$line->desc;
4259
			}else {
4260
				$this->tpl['label'].= ($line->label ? '&nbsp;'.$line->label : '');
4261
			}
4262
4263
			// Dates
4264
			if ($line->product_type == 1 && ($date_start || $date_end))
4265
			{
4266
				$this->tpl['label'].= get_date_range($date_start, $date_end);
4267
			}
4268
		}
4269
4270
		if (! empty($line->desc))
4271
		{
4272
			if ($line->desc == '(CREDIT_NOTE)')  // TODO Not sure this is used for source object
4273
			{
4274
				$discount=new DiscountAbsolute($this->db);
4275
				$discount->fetch($line->fk_remise_except);
4276
				$this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
4277
			}
4278
			elseif ($line->desc == '(DEPOSIT)')  // TODO Not sure this is used for source object
4279
			{
4280
				$discount=new DiscountAbsolute($this->db);
4281
				$discount->fetch($line->fk_remise_except);
4282
				$this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
4283
			}
4284
			elseif ($line->desc == '(EXCESS RECEIVED)')
4285
			{
4286
				$discount=new DiscountAbsolute($this->db);
4287
				$discount->fetch($line->fk_remise_except);
4288
				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
4289
			}
4290
			elseif ($line->desc == '(EXCESS PAID)')
4291
			{
4292
				$discount=new DiscountAbsolute($this->db);
4293
				$discount->fetch($line->fk_remise_except);
4294
				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
4295
			}
4296
			else
4297
			{
4298
				$this->tpl['description'] = dol_trunc($line->desc, 60);
4299
			}
4300
		}
4301
		else
4302
		{
4303
			$this->tpl['description'] = '&nbsp;';
4304
		}
4305
4306
        // VAT Rate
4307
        $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
4308
        $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
4309
        if (! empty($line->vat_src_code) && ! preg_match('/\(/', $this->tpl['vat_rate'])) $this->tpl['vat_rate'].=' ('.$line->vat_src_code.')';
4310
4311
		$this->tpl['price'] = price($line->subprice);
4312
		$this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
4313
		$this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
4314
		if ($conf->global->PRODUCT_USE_UNITS) $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
4315
		$this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
4316
4317
		// Is the line strike or not
4318
		$this->tpl['strike']=0;
4319
		if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) $this->tpl['strike']=1;
4320
4321
		// Output template part (modules that overwrite templates must declare this into descriptor)
4322
		// Use global variables + $dateSelector + $seller and $buyer
4323
		$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4324
		foreach($dirtpls as $module => $reldir)
4325
		{
4326
			if (!empty($module))
4327
			{
4328
				$tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
4329
			}
4330
			else
4331
			{
4332
				$tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
4333
			}
4334
4335
			if (empty($conf->file->strict_mode)) {
4336
				$res=@include $tpl;
4337
			} else {
4338
				$res=include $tpl; // for debug
4339
			}
4340
			if ($res) break;
4341
		}
4342
	}
4343
4344
4345
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4346
	/**
4347
	 *	Add resources to the current object : add entry into llx_element_resources
4348
	 *	Need $this->element & $this->id
4349
	 *
4350
	 *	@param		int		$resource_id		Resource id
4351
	 *	@param		string	$resource_type		'resource'
4352
	 *	@param		int		$busy				Busy or not
4353
	 *	@param		int		$mandatory			Mandatory or not
4354
	 *	@return		int							<=0 if KO, >0 if OK
4355
	 */
4356
	public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
4357
	{
4358
        // phpcs:enable
4359
		$this->db->begin();
4360
4361
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources (";
4362
		$sql.= "resource_id";
4363
		$sql.= ", resource_type";
4364
		$sql.= ", element_id";
4365
		$sql.= ", element_type";
4366
		$sql.= ", busy";
4367
		$sql.= ", mandatory";
4368
		$sql.= ") VALUES (";
4369
		$sql.= $resource_id;
4370
		$sql.= ", '".$this->db->escape($resource_type)."'";
4371
		$sql.= ", '".$this->db->escape($this->id)."'";
4372
		$sql.= ", '".$this->db->escape($this->element)."'";
4373
		$sql.= ", '".$this->db->escape($busy)."'";
4374
		$sql.= ", '".$this->db->escape($mandatory)."'";
4375
		$sql.= ")";
4376
4377
		dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
4378
		if ($this->db->query($sql))
4379
		{
4380
			$this->db->commit();
4381
			return 1;
4382
		}
4383
		else
4384
		{
4385
			$this->error=$this->db->lasterror();
4386
			$this->db->rollback();
4387
			return  0;
4388
		}
4389
	}
4390
4391
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4392
	/**
4393
	 *    Delete a link to resource line
4394
	 *
4395
	 *    @param	int		$rowid			Id of resource line to delete
4396
	 *    @param	int		$element		element name (for trigger) TODO: use $this->element into commonobject class
4397
	 *    @param	int		$notrigger		Disable all triggers
4398
	 *    @return   int						>0 if OK, <0 if KO
4399
	 */
4400
	public function delete_resource($rowid, $element, $notrigger = 0)
4401
	{
4402
        // phpcs:enable
4403
		global $user;
4404
4405
		$this->db->begin();
4406
4407
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources";
4408
		$sql.= " WHERE rowid=".$rowid;
4409
4410
		dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
4411
4412
		$resql=$this->db->query($sql);
4413
		if (! $resql)
4414
		{
4415
			$this->error=$this->db->lasterror();
4416
			$this->db->rollback();
4417
			return -1;
4418
		}
4419
		else
4420
		{
4421
			if (! $notrigger)
4422
			{
4423
				$result=$this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
4424
				if ($result < 0) { $this->db->rollback(); return -1; }
4425
			}
4426
			$this->db->commit();
4427
			return 1;
4428
		}
4429
	}
4430
4431
4432
	/**
4433
	 * Overwrite magic function to solve problem of cloning object that are kept as references
4434
	 *
4435
	 * @return void
4436
	 */
4437
	public function __clone()
4438
	{
4439
		// Force a copy of this->lines, otherwise it will point to same object.
4440
		if (isset($this->lines) && is_array($this->lines))
4441
		{
4442
			$nboflines=count($this->lines);
4443
			for($i=0; $i < $nboflines; $i++)
4444
			{
4445
				$this->lines[$i] = clone $this->lines[$i];
4446
			}
4447
		}
4448
	}
4449
4450
	/**
4451
	 * Common function for all objects extending CommonObject for generating documents
4452
	 *
4453
	 * @param 	string 		$modelspath 	Relative folder where generators are placed
4454
	 * @param 	string 		$modele 		Generator to use. Caller must set it to obj->modelpdf or GETPOST('modelpdf','alpha') for example.
4455
	 * @param 	Translate 	$outputlangs 	Output language to use
4456
	 * @param 	int 		$hidedetails 	1 to hide details. 0 by default
4457
	 * @param 	int 		$hidedesc 		1 to hide product description. 0 by default
4458
	 * @param 	int 		$hideref 		1 to hide product reference. 0 by default
4459
	 * @param   null|array  $moreparams     Array to provide more information
4460
	 * @return 	int 						>0 if OK, <0 if KO
4461
	 * @see	addFileIntoDatabaseIndex()
4462
	 */
4463
	protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
4464
	{
4465
		global $conf, $langs, $user;
4466
4467
		$srctemplatepath='';
4468
4469
		dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs)?$outputlangs->defaultlang:'null'));
4470
4471
		// Increase limit for PDF build
4472
		$err=error_reporting();
4473
		error_reporting(0);
4474
		@set_time_limit(120);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition for set_time_limit(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

4474
		/** @scrutinizer ignore-unhandled */ @set_time_limit(120);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
4475
		error_reporting($err);
4476
4477
		// If selected model is a filename template (then $modele="modelname" or "modelname:filename")
4478
		$tmp=explode(':', $modele, 2);
4479
		if (! empty($tmp[1]))
4480
		{
4481
			$modele=$tmp[0];
4482
			$srctemplatepath=$tmp[1];
4483
		}
4484
4485
		// Search template files
4486
		$file=''; $classname=''; $filefound=0;
4487
		$dirmodels=array('/');
4488
		if (is_array($conf->modules_parts['models'])) $dirmodels=array_merge($dirmodels, $conf->modules_parts['models']);
4489
		foreach($dirmodels as $reldir)
4490
		{
4491
			foreach(array('doc','pdf') as $prefix)
4492
			{
4493
				if (in_array(get_class($this), array('Adherent'))) $file = $prefix."_".$modele.".class.php";     // Member module use prefix_module.class.php
4494
				else $file = $prefix."_".$modele.".modules.php";
4495
4496
				// On verifie l'emplacement du modele
4497
				$file=dol_buildpath($reldir.$modelspath.$file, 0);
4498
				if (file_exists($file))
4499
				{
4500
					$filefound=1;
4501
					$classname=$prefix.'_'.$modele;
4502
					break;
4503
				}
4504
			}
4505
			if ($filefound) break;
4506
		}
4507
4508
		// If generator was found
4509
		if ($filefound)
4510
		{
4511
			global $db;  // Required to solve a conception default making an include of code using $db instead of $this->db just after.
4512
4513
			require_once $file;
4514
4515
			$obj = new $classname($this->db);
4516
4517
			// If generator is ODT, we must have srctemplatepath defined, if not we set it.
4518
			if ($obj->type == 'odt' && empty($srctemplatepath))
4519
			{
4520
				$varfortemplatedir=$obj->scandir;
4521
				if ($varfortemplatedir && ! empty($conf->global->$varfortemplatedir))
4522
				{
4523
					$dirtoscan=$conf->global->$varfortemplatedir;
4524
4525
					$listoffiles=array();
4526
4527
					// Now we add first model found in directories scanned
4528
					$listofdir=explode(',', $dirtoscan);
4529
					foreach($listofdir as $key => $tmpdir)
4530
					{
4531
						$tmpdir=trim($tmpdir);
4532
						$tmpdir=preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
4533
						if (! $tmpdir) { unset($listofdir[$key]); continue; }
4534
						if (is_dir($tmpdir))
4535
						{
4536
							$tmpfiles=dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
4537
							if (count($tmpfiles)) $listoffiles=array_merge($listoffiles, $tmpfiles);
4538
						}
4539
					}
4540
4541
					if (count($listoffiles))
4542
					{
4543
						foreach($listoffiles as $record)
4544
						{
4545
							$srctemplatepath=$record['fullname'];
4546
							break;
4547
						}
4548
					}
4549
				}
4550
4551
				if (empty($srctemplatepath))
4552
				{
4553
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
4554
					return -1;
4555
				}
4556
			}
4557
4558
			if ($obj->type == 'odt' && ! empty($srctemplatepath))
4559
			{
4560
				if (! dol_is_file($srctemplatepath))
4561
				{
4562
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
4563
					return -1;
4564
				}
4565
			}
4566
4567
			// We save charset_output to restore it because write_file can change it if needed for
4568
			// output format that does not support UTF8.
4569
			$sav_charset_output=$outputlangs->charset_output;
4570
4571
			if (in_array(get_class($this), array('Adherent')))
4572
			{
4573
				$arrayofrecords = array();   // The write_file of templates of adherent class need this var
4574
				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams);
4575
			}
4576
			else
4577
			{
4578
				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
4579
			}
4580
			// After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
4581
4582
			if ($resultwritefile > 0)
4583
			{
4584
				$outputlangs->charset_output=$sav_charset_output;
4585
4586
				// We delete old preview
4587
				require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4588
				dol_delete_preview($this);
4589
4590
				// Index file in database
4591
				if (! empty($obj->result['fullpath']))
4592
				{
4593
					$destfull = $obj->result['fullpath'];
4594
					$upload_dir = dirname($destfull);
4595
					$destfile = basename($destfull);
4596
					$rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
4597
4598
					if (! preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir))     // If not a tmp dir
4599
					{
4600
						$filename = basename($destfile);
4601
						$rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
4602
						$rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
4603
4604
						include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
4605
						$ecmfile=new EcmFiles($this->db);
4606
						$result = $ecmfile->fetch(0, '', ($rel_dir?$rel_dir.'/':'').$filename);
4607
4608
						// Set the public "share" key
4609
						$setsharekey = false;
4610
						if ($this->element == 'propal')
4611
						{
4612
							$useonlinesignature = $conf->global->MAIN_FEATURES_LEVEL;	// Replace this with 1 when feature to make online signature is ok
4613
							if ($useonlinesignature) $setsharekey=true;
4614
							if (! empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4615
						}
4616
						if ($this->element == 'commande' && ! empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) {
4617
							$setsharekey=true;
4618
						}
4619
						if ($this->element == 'facture' && ! empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) {
4620
							$setsharekey=true;
4621
						}
4622
						if ($this->element == 'bank_account' && ! empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) {
4623
							$setsharekey=true;
4624
						}
4625
4626
						if ($setsharekey)
4627
						{
4628
							if (empty($ecmfile->share))	// Because object not found or share not set yet
4629
							{
4630
								require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
4631
								$ecmfile->share = getRandomPassword(true);
4632
							}
4633
						}
4634
4635
						if ($result > 0)
4636
						{
4637
							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4638
							$ecmfile->fullpath_orig = '';
4639
							$ecmfile->gen_or_uploaded = 'generated';
4640
							$ecmfile->description = '';    // indexed content
4641
							$ecmfile->keyword = '';        // keyword content
4642
							$result = $ecmfile->update($user);
4643
							if ($result < 0)
4644
							{
4645
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4646
							}
4647
						}
4648
						else
4649
						{
4650
							$ecmfile->entity = $conf->entity;
4651
							$ecmfile->filepath = $rel_dir;
4652
							$ecmfile->filename = $filename;
4653
							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4654
							$ecmfile->fullpath_orig = '';
4655
							$ecmfile->gen_or_uploaded = 'generated';
4656
							$ecmfile->description = '';    // indexed content
4657
							$ecmfile->keyword = '';        // keyword content
4658
							$ecmfile->src_object_type = $this->table_element;
4659
							$ecmfile->src_object_id   = $this->id;
4660
4661
							$result = $ecmfile->create($user);
4662
							if ($result < 0)
4663
							{
4664
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4665
							}
4666
						}
4667
4668
						/*$this->result['fullname']=$destfull;
4669
						$this->result['filepath']=$ecmfile->filepath;
4670
						$this->result['filename']=$ecmfile->filename;*/
4671
						//var_dump($obj->update_main_doc_field);exit;
4672
4673
						// Update the last_main_doc field into main object (if documenent generator has property ->update_main_doc_field set)
4674
						$update_main_doc_field=0;
4675
						if (! empty($obj->update_main_doc_field)) $update_main_doc_field=1;
4676
						if ($update_main_doc_field && ! empty($this->table_element))
4677
						{
4678
							$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath.'/'.$ecmfile->filename)."'";
4679
							$sql.= ' WHERE rowid = '.$this->id;
4680
4681
							$resql = $this->db->query($sql);
4682
							if (! $resql) dol_print_error($this->db);
4683
							else
4684
							{
4685
							    $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
4686
							}
4687
						}
4688
					}
4689
				}
4690
				else
4691
				{
4692
					dol_syslog('Method ->write_file was called on object '.get_class($obj).' and return a success but the return array ->result["fullpath"] was not set.', LOG_WARNING);
4693
				}
4694
4695
				// Success in building document. We build meta file.
4696
				dol_meta_create($this);
4697
4698
				return 1;
4699
			}
4700
			else
4701
			{
4702
				$outputlangs->charset_output=$sav_charset_output;
4703
				dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors);
4704
				return -1;
4705
			}
4706
		}
4707
		else
4708
		{
4709
			$this->error=$langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists", $file);
4710
			dol_print_error('', $this->error);
4711
			return -1;
4712
		}
4713
	}
4714
4715
	/**
4716
	 *  Build thumb
4717
	 *  @TODO Move this into files.lib.php
4718
	 *
4719
	 *  @param      string	$file           Path file in UTF8 to original file to create thumbs from.
4720
	 *	@return		void
4721
	 */
4722
	public function addThumbs($file)
4723
	{
4724
		global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality;
4725
4726
		require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';		// This define also $maxwidthsmall, $quality, ...
4727
4728
		$file_osencoded=dol_osencode($file);
4729
		if (file_exists($file_osencoded))
4730
		{
4731
			// Create small thumbs for company (Ratio is near 16/9)
4732
			// Used on logon for example
4733
			vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
4734
4735
			// Create mini thumbs for company (Ratio is near 16/9)
4736
			// Used on menu or for setup page for example
4737
			vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
4738
		}
4739
	}
4740
4741
4742
	/* Functions common to commonobject and commonobjectline */
4743
4744
	/* For default values */
4745
4746
	/**
4747
	 * Return the default value to use for a field when showing the create form of object.
4748
	 * Return values in this order:
4749
	 * 1) If parameter is available into POST, we return it first.
4750
	 * 2) If not but an alternate value was provided as parameter of function, we return it.
4751
	 * 3) If not but a constant $conf->global->OBJECTELEMENT_FIELDNAME is set, we return it (It is better to use the dedicated table).
4752
	 * 4) Return value found into database (TODO No yet implemented)
4753
	 *
4754
	 * @param   string              $fieldname          Name of field
4755
	 * @param   string              $alternatevalue     Alternate value to use
4756
	 * @return  string|string[]                         Default value (can be an array if the GETPOST return an array)
4757
	 **/
4758
	public function getDefaultCreateValueFor($fieldname, $alternatevalue = null)
4759
	{
4760
		global $conf, $_POST;
4761
4762
		// If param here has been posted, we use this value first.
4763
		if (isset($_POST[$fieldname])) return GETPOST($fieldname, 2);
4764
4765
		if (isset($alternatevalue)) return $alternatevalue;
4766
4767
		$newelement=$this->element;
4768
		if ($newelement == 'facture') $newelement='invoice';
4769
		if ($newelement == 'commande') $newelement='order';
4770
		if (empty($newelement))
4771
		{
4772
			dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
4773
			return '';
4774
		}
4775
4776
		$keyforfieldname=strtoupper($newelement.'_DEFAULT_'.$fieldname);
4777
		//var_dump($keyforfieldname);
4778
		if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname;
4779
4780
		// TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
4781
	}
4782
4783
4784
	/* For triggers */
4785
4786
4787
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4788
	/**
4789
	 * Call trigger based on this instance.
4790
	 * Some context information may also be provided into array property this->context.
4791
	 * NB:  Error from trigger are stacked in interface->errors
4792
	 * NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction.
4793
	 *
4794
	 * @param   string    $trigger_name   trigger's name to execute
4795
	 * @param   User      $user           Object user
4796
	 * @return  int                       Result of run_triggers
4797
	 */
4798
	public function call_trigger($trigger_name, $user)
4799
	{
4800
		// phpcs:enable
4801
		global $langs,$conf;
4802
4803
		include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
4804
		$interface=new Interfaces($this->db);
4805
		$result=$interface->run_triggers($trigger_name, $this, $user, $langs, $conf);
4806
4807
		if ($result < 0)
4808
		{
4809
			if (!empty($this->errors))
4810
			{
4811
				$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.
4812
			}
4813
			else
4814
			{
4815
				$this->errors=$interface->errors;
4816
			}
4817
		}
4818
		return $result;
4819
	}
4820
4821
4822
	/* Functions for extrafields */
4823
4824
4825
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4826
	/**
4827
	 *  Function to get extra fields of an object into $this->array_options
4828
	 *  This method is in most cases called by method fetch of objects but you can call it separately.
4829
	 *
4830
	 *  @param	int		$rowid			Id of line. Use the id of object if not defined. Deprecated. Function must be called without parameters.
4831
	 *  @param  array	$optionsArray   Array resulting of call of extrafields->fetch_name_optionals_label(). Deprecated. Function must be called without parameters.
4832
	 *  @return	int						<0 if error, 0 if no values of extrafield to find nor found, 1 if an attribute is found and value loaded
4833
	 */
4834
	public function fetch_optionals($rowid = null, $optionsArray = null)
4835
	{
4836
		// phpcs:enable
4837
		if (empty($rowid)) $rowid=$this->id;
4838
4839
		// To avoid SQL errors. Probably not the better solution though
4840
		if (!$this->table_element) {
4841
			return 0;
4842
		}
4843
4844
		$this->array_options=array();
4845
4846
		if (! is_array($optionsArray))
4847
		{
4848
			// If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
4849
			// TODO Use of existing $extrafield is not yet ready (must mutualize code that use extrafields in form first)
4850
			// global $extrafields;
4851
			//if (! is_object($extrafields))
4852
			//{
4853
				require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4854
				$extrafields = new ExtraFields($this->db);
4855
			//}
4856
4857
			// Load array of extrafields for elementype = $this->table_element
4858
			if (empty($extrafields->attributes[$this->table_element]['loaded']))
4859
			{
4860
				$extrafields->fetch_name_optionals_label($this->table_element);
4861
			}
4862
			$optionsArray = (! empty($extrafields->attributes[$this->table_element]['label'])?$extrafields->attributes[$this->table_element]['label']:null);
4863
		}
4864
		else
4865
		{
4866
			global $extrafields;
4867
			dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
4868
		}
4869
4870
		$table_element = $this->table_element;
4871
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4872
4873
		// Request to get complementary values
4874
		if (is_array($optionsArray) && count($optionsArray) > 0)
4875
		{
4876
			$sql = "SELECT rowid";
4877
			foreach ($optionsArray as $name => $label)
4878
			{
4879
				if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate')
4880
				{
4881
					$sql.= ", ".$name;
4882
				}
4883
			}
4884
			$sql.= " FROM ".MAIN_DB_PREFIX.$table_element."_extrafields";
4885
			$sql.= " WHERE fk_object = ".$rowid;
4886
4887
			//dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG);		// Too verbose
4888
			$resql=$this->db->query($sql);
4889
			if ($resql)
4890
			{
4891
				$this->array_options = array();
4892
				$numrows=$this->db->num_rows($resql);
4893
				if ($numrows)
4894
				{
4895
					$tab = $this->db->fetch_array($resql);
4896
4897
					foreach ($tab as $key => $value)
4898
					{
4899
						// 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)
4900
						if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && ! is_int($key))
4901
						{
4902
							// we can add this attribute to object
4903
							if (! empty($extrafields) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime')))
4904
							{
4905
								//var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
4906
								$this->array_options["options_".$key]=$this->db->jdate($value);
4907
							}
4908
							else
4909
							{
4910
								$this->array_options["options_".$key]=$value;
4911
							}
4912
4913
							//var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
4914
						}
4915
					}
4916
4917
					// If field is a computed field, value must become result of compute
4918
					foreach ($tab as $key => $value) {
4919
						if (! empty($extrafields) && !empty($extrafields->attributes[$this->table_element]['computed'][$key]))
4920
						{
4921
							$this->array_options["options_".$key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0);
4922
						}
4923
					}
4924
				}
4925
4926
				$this->db->free($resql);
4927
4928
				if ($numrows) return $numrows;
4929
				else return 0;
4930
			}
4931
			else
4932
			{
4933
				dol_print_error($this->db);
4934
				return -1;
4935
			}
4936
		}
4937
		return 0;
4938
	}
4939
4940
	/**
4941
	 *	Delete all extra fields values for the current object.
4942
	 *
4943
	 *  @return	int		<0 if KO, >0 if OK
4944
	 */
4945
	public function deleteExtraFields()
4946
	{
4947
		$this->db->begin();
4948
4949
		$table_element = $this->table_element;
4950
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4951
4952
		$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
4953
		dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
4954
		$resql=$this->db->query($sql_del);
4955
		if (! $resql)
4956
		{
4957
			$this->error=$this->db->lasterror();
4958
			$this->db->rollback();
4959
			return -1;
4960
		}
4961
		else
4962
		{
4963
			$this->db->commit();
4964
			return 1;
4965
		}
4966
	}
4967
4968
	/**
4969
	 *	Add/Update all extra fields values for the current object.
4970
	 *  Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
4971
	 *  This function delete record with all extrafields and insert them again from the array $this->array_options.
4972
	 *
4973
	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
4974
	 *  @param	User		$userused		Object user
4975
	 *  @return int 						-1=error, O=did nothing, 1=OK
4976
	 *  @see updateExtraField(), setValueFrom()
4977
	 */
4978
	public function insertExtraFields($trigger = '', $userused = null)
4979
	{
4980
		global $conf,$langs,$user;
4981
4982
		if (empty($userused)) $userused=$user;
4983
4984
		$error=0;
4985
4986
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
4987
4988
		if (! empty($this->array_options))
4989
		{
4990
			// Check parameters
4991
			$langs->load('admin');
4992
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4993
			$extrafields = new ExtraFields($this->db);
4994
			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
4995
4996
			//Eliminate copied source object extra_fields that do not exist in target object
4997
			$new_array_options=array();
4998
			foreach ($this->array_options as $key => $value) {
4999
				if (in_array(substr($key, 8), array_keys($target_extrafields)))	// We remove the 'options_' from $key for test
5000
					$new_array_options[$key] = $value;
5001
				elseif (in_array($key, array_keys($target_extrafields)))		// We test on $key that does not contains the 'options_' prefix
5002
					$new_array_options['options_'.$key] = $value;
5003
			}
5004
5005
			foreach($new_array_options as $key => $value)
5006
			{
5007
			   	$attributeKey      = substr($key, 8);   // Remove 'options_' prefix
5008
			   	$attributeType     = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
5009
			   	$attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
5010
			   	$attributeParam    = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
5011
			   	$attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
5012
				$attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
5013
5014
5015
			   	if ($attributeRequired)
5016
			   	{
5017
			   		$mandatorypb=false;
5018
			   		if ($attributeType == 'link' && $this->array_options[$key] == '-1') $mandatorypb=true;
5019
			   		if ($this->array_options[$key] === '') $mandatorypb=true;
5020
			   		if ($mandatorypb)
5021
			   		{
5022
			   		    dol_syslog("Mandatory extra field ".$key." is empty");
5023
			   			$this->errors[]=$langs->trans('ErrorFieldRequired', $attributeLabel);
5024
			   			return -1;
5025
			   		}
5026
			   	}
5027
5028
				//dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5029
				//dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5030
				
5031
				if (!empty($attrfieldcomputed))
5032
				{
5033
					if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS))
5034
					{
5035
						$value = dol_eval($attrfieldcomputed, 1, 0);
5036
					    dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
5037
					    $new_array_options[$key] = $value;
5038
					}
5039
					else
5040
					{
5041
						$new_array_options[$key] = null;
5042
					}
5043
				}
5044
5045
5046
			   	switch ($attributeType)
5047
			   	{
5048
			   		case 'int':
5049
			  			if (!is_numeric($value) && $value!='')
5050
			   			{
5051
			   				$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5052
			   				return -1;
5053
			  			}
5054
			   			elseif ($value=='')
5055
			   			{
5056
			   				$new_array_options[$key] = null;
5057
			   			}
5058
			 			break;
5059
					case 'double':
5060
						$value = price2num($value);
5061
						if (!is_numeric($value) && $value!='')
5062
						{
5063
							dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5064
							$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5065
							return -1;
5066
						}
5067
						elseif ($value=='')
5068
						{
5069
							$new_array_options[$key] = null;
5070
						}
5071
						//dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5072
						$new_array_options[$key] = $value;
5073
						break;
5074
			 		/*case 'select':	// Not required, we chosed value='0' for undefined values
5075
             			if ($value=='-1')
5076
             			{
5077
             				$this->array_options[$key] = null;
5078
             			}
5079
             			break;*/
5080
			   		case 'password':
5081
			   			$algo='';
5082
			   			if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']))
5083
			   			{
5084
			   				// If there is an encryption choice, we use it to crypt data before insert
5085
			   				$tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
5086
			   				$algo=reset($tmparrays);
5087
			   				if ($algo != '')
5088
			   				{
5089
			   					//global $action;		// $action may be 'create', 'update', 'update_extras'...
5090
			   					//var_dump($action);
5091
			   					//var_dump($this->oldcopy);exit;
5092
			   					if (is_object($this->oldcopy))		// If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
5093
			   					{
5094
			   						//var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
5095
				   					if ($this->array_options[$key] == $this->oldcopy->array_options[$key])	// If old value crypted in database is same than submited new value, it means we don't change it, so we don't update.
5096
				   					{
5097
				   						$new_array_options[$key] = $this->array_options[$key];	// Value is kept
5098
				   					}
5099
									else
5100
									{
5101
										// var_dump($algo);
5102
										$newvalue = dol_hash($this->array_options[$key], $algo);
5103
										$new_array_options[$key] = $newvalue;
5104
									}
5105
			   					}
5106
			   					else
5107
			   					{
5108
			   						$new_array_options[$key] = $this->array_options[$key];	// Value is kept
5109
			   					}
5110
			   				}
5111
			   			}
5112
			   			else	// Common usage
5113
			   			{
5114
			   				$new_array_options[$key] = $this->array_options[$key];
5115
			   			}
5116
			   			break;
5117
			   		case 'price':
5118
						$new_array_options[$key] = price2num($this->array_options[$key]);
5119
						break;
5120
					case 'date':
5121
					case 'datetime':
5122
						// If data is a string instead of a timestamp, we convert it
5123
						if (! is_int($this->array_options[$key])) {
5124
							$this->array_options[$key] = strtotime($this->array_options[$key]);
5125
						}
5126
						$new_array_options[$key] = $this->db->idate($this->array_options[$key]);
5127
						break;
5128
		   			case 'link':
5129
						$param_list=array_keys($attributeParam['options']);
5130
						// 0 : ObjectName
5131
						// 1 : classPath
5132
						$InfoFieldList = explode(":", $param_list[0]);
5133
						dol_include_once($InfoFieldList[1]);
5134
						if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
5135
						{
5136
							if ($value == '-1')	// -1 is key for no defined in combo list of objects
5137
							{
5138
								$new_array_options[$key]='';
5139
							}
5140
							elseif ($value)
5141
							{
5142
								$object = new $InfoFieldList[0]($this->db);
5143
								if (is_numeric($value)) $res=$object->fetch($value);
5144
								else $res=$object->fetch('', $value);
5145
5146
								if ($res > 0) $new_array_options[$key]=$object->id;
5147
								else
5148
								{
5149
									$this->error="Id/Ref '".$value."' for object '".$object->element."' not found";
5150
									$this->db->rollback();
5151
									return -1;
5152
								}
5153
							}
5154
						}
5155
						else
5156
						{
5157
							dol_syslog('Error bad setup of extrafield', LOG_WARNING);
5158
						}
5159
						break;
5160
			   	}
5161
			}
5162
5163
			$this->db->begin();
5164
5165
			$table_element = $this->table_element;
5166
			if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
5167
5168
			dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
5169
5170
			$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
5171
			$this->db->query($sql_del);
5172
5173
			$sql = "INSERT INTO ".MAIN_DB_PREFIX.$table_element."_extrafields (fk_object";
5174
			foreach($new_array_options as $key => $value)
5175
			{
5176
				$attributeKey = substr($key, 8);   // Remove 'options_' prefix
5177
				// Add field of attribut
5178
				if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator
5179
					$sql.=",".$attributeKey;
5180
			}
5181
			// We must insert a default value for fields for other entities that are mandatory to avoid not null error
5182
			if (is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']))
5183
			{
5184
    			foreach($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as  $tmpkey => $tmpval)
5185
    			{
5186
    			    if (! isset($extrafields->attributes[$this->table_element]['type'][$tmpkey]))    // If field not already added previously
5187
    			    {
5188
    			        $sql.=",".$tmpkey;
5189
    			    }
5190
    			}
5191
			}
5192
			$sql .= ") VALUES (".$this->id;
5193
5194
			foreach($new_array_options as $key => $value)
5195
			{
5196
				$attributeKey = substr($key, 8);   // Remove 'options_' prefix
5197
				// Add field of attribute
5198
				if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator)
5199
				{
5200
					if ($new_array_options[$key] != '')
5201
					{
5202
						$sql.=",'".$this->db->escape($new_array_options[$key])."'";
5203
					}
5204
					else
5205
					{
5206
						$sql.=",null";
5207
					}
5208
				}
5209
			}
5210
			// We must insert a default value for fields for other entities that are mandatory to avoid not null error
5211
			if (is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']))
5212
			{
5213
			    foreach($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as  $tmpkey => $tmpval)
5214
    			{
5215
    			    if (! isset($extrafields->attributes[$this->table_element]['type'][$tmpkey]))    // If field not already added previously
5216
    			    {
5217
                        if (in_array($tmpval, array('int', 'double'))) $sql.=", 0";
5218
                        else $sql.=", ''";
5219
    			    }
5220
    			}
5221
			}
5222
5223
			$sql.=")";
5224
5225
			$resql = $this->db->query($sql);
5226
5227
			if (! $resql)
5228
			{
5229
				$this->error=$this->db->lasterror();
5230
				$error++;
5231
			}
5232
5233
			if (! $error && $trigger)
5234
			{
5235
				// Call trigger
5236
				$this->context=array('extrafieldaddupdate'=>1);
5237
				$result=$this->call_trigger($trigger, $userused);
5238
				if ($result < 0) $error++;
5239
				// End call trigger
5240
			}
5241
5242
			if ($error)
5243
			{
5244
				$this->db->rollback();
5245
				return -1;
5246
			}
5247
			else
5248
			{
5249
				$this->db->commit();
5250
				return 1;
5251
			}
5252
		}
5253
		else return 0;
5254
	}
5255
5256
	/**
5257
	 *	Update an extra field value for the current object.
5258
	 *  Data to describe values to update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
5259
	 *
5260
	 *  @param  string      $key    		Key of the extrafield (without starting 'options_')
5261
	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
5262
	 *  @param	User		$userused		Object user
5263
	 *  @return int                 		-1=error, O=did nothing, 1=OK
5264
	 *  @see setValueFrom(), insertExtraFields()
5265
	 */
5266
	public function updateExtraField($key, $trigger = null, $userused = null)
5267
	{
5268
		global $conf,$langs,$user;
5269
5270
		if (empty($userused)) $userused=$user;
5271
5272
		$error=0;
5273
5274
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
5275
5276
		if (! empty($this->array_options) && isset($this->array_options["options_".$key]))
5277
		{
5278
			// Check parameters
5279
			$langs->load('admin');
5280
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5281
			$extrafields = new ExtraFields($this->db);
5282
			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
5283
5284
			$value=$this->array_options["options_".$key];
5285
5286
			$attributeType     = $extrafields->attributes[$this->table_element]['type'][$key];
5287
			$attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$key];
5288
			$attributeParam    = $extrafields->attributes[$this->table_element]['param'][$key];
5289
			$attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
5290
5291
			//dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5292
			//dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5293
5294
			switch ($attributeType)
5295
			{
5296
				case 'int':
5297
					if (!is_numeric($value) && $value!='')
5298
					{
5299
						$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5300
						return -1;
5301
					}
5302
					elseif ($value=='')
5303
					{
5304
						$this->array_options["options_".$key] = null;
5305
					}
5306
					break;
5307
				case 'double':
5308
					$value = price2num($value);
5309
					if (!is_numeric($value) && $value!='')
5310
					{
5311
						dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5312
						$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5313
						return -1;
5314
					}
5315
					elseif ($value=='')
5316
					{
5317
						$this->array_options["options_".$key] = null;
5318
					}
5319
					//dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5320
					$this->array_options["options_".$key] = $value;
5321
					break;
5322
			 	/*case 'select':	// Not required, we chosed value='0' for undefined values
5323
             		if ($value=='-1')
5324
             		{
5325
             			$this->array_options[$key] = null;
5326
             		}
5327
             		break;*/
5328
				case 'price':
5329
					$this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
5330
					break;
5331
				case 'date':
5332
					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5333
					break;
5334
				case 'datetime':
5335
					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5336
					break;
5337
				case 'link':
5338
					$param_list=array_keys($attributeParam['options']);
5339
					// 0 : ObjectName
5340
					// 1 : classPath
5341
					$InfoFieldList = explode(":", $param_list[0]);
5342
					dol_include_once($InfoFieldList[1]);
5343
					if ($value)
5344
					{
5345
						$object = new $InfoFieldList[0]($this->db);
5346
						$object->fetch(0, $value);
5347
						$this->array_options["options_".$key]=$object->id;
5348
					}
5349
					break;
5350
			}
5351
5352
			$this->db->begin();
5353
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key."='".$this->db->escape($this->array_options["options_".$key])."'";
5354
			$sql .= " WHERE fk_object = ".$this->id;
5355
			$resql = $this->db->query($sql);
5356
			if (! $resql)
5357
			{
5358
				$error++;
5359
				$this->error=$this->db->lasterror();
5360
			}
5361
5362
			if (! $error && $trigger)
5363
			{
5364
				// Call trigger
5365
				$this->context=array('extrafieldupdate'=>1);
5366
				$result=$this->call_trigger($trigger, $userused);
5367
				if ($result < 0) $error++;
5368
				// End call trigger
5369
			}
5370
5371
			if ($error)
5372
			{
5373
				dol_syslog(get_class($this) . "::".__METHOD__ . $this->error, LOG_ERR);
5374
				$this->db->rollback();
5375
				return -1;
5376
			}
5377
			else
5378
			{
5379
				$this->db->commit();
5380
				return 1;
5381
			}
5382
		}
5383
		else return 0;
5384
	}
5385
5386
5387
	/**
5388
	 * Return HTML string to put an input field into a page
5389
	 * Code very similar with showInputField of extra fields
5390
	 *
5391
	 * @param  array   		$val	       Array of properties for field to show
5392
	 * @param  string  		$key           Key of attribute
5393
	 * @param  string  		$value         Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value)
5394
	 * @param  string  		$moreparam     To add more parameters on html input tag
5395
	 * @param  string  		$keysuffix     Prefix string to add into name and id of field (can be used to avoid duplicate names)
5396
	 * @param  string  		$keyprefix     Suffix string to add into name and id of field (can be used to avoid duplicate names)
5397
	 * @param  string|int	$morecss       Value for css to define style/length of field. May also be a numeric.
5398
	 * @return string
5399
	 */
5400
	public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0)
5401
	{
5402
		global $conf,$langs,$form;
5403
5404
		if (! is_object($form))
5405
		{
5406
			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5407
			$form=new Form($this->db);
5408
		}
5409
5410
		$val=$this->fields[$key];
5411
5412
		$out='';
5413
        $type='';
5414
        $param = array();
5415
        $param['options']=array();
5416
        $size =$this->fields[$key]['size'];
5417
        // Because we work on extrafields
5418
        if(preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)){
5419
            $param['options']=array($reg[1].':'.$reg[2]=>'N');
5420
            $type ='link';
5421
        } elseif(preg_match('/^link:(.*):(.*)/i', $val['type'], $reg)) {
5422
            $param['options']=array($reg[1].':'.$reg[2]=>'N');
5423
            $type ='link';
5424
        } elseif(preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
5425
            $param['options']=array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4]=>'N');
5426
            $type ='sellist';
5427
        } elseif(preg_match('/varchar\((\d+)\)/', $val['type'], $reg)) {
5428
            $param['options']=array();
5429
            $type ='varchar';
5430
            $size=$reg[1];
5431
        } elseif(preg_match('/varchar/', $val['type'])) {
5432
            $param['options']=array();
5433
            $type ='varchar';
5434
        } elseif(is_array($this->fields[$key]['arrayofkeyval'])) {
5435
            $param['options']=$this->fields[$key]['arrayofkeyval'];
5436
            $type ='select';
5437
        } else {
5438
            $param['options']=array();
5439
            $type =$this->fields[$key]['type'];
5440
        }
5441
5442
		$label=$this->fields[$key]['label'];
5443
		//$elementtype=$this->fields[$key]['elementtype'];	// Seems not used
5444
		$default=$this->fields[$key]['default'];
5445
		$computed=$this->fields[$key]['computed'];
5446
		$unique=$this->fields[$key]['unique'];
5447
		$required=$this->fields[$key]['required'];
5448
5449
		$langfile=$this->fields[$key]['langfile'];
5450
		$list=$this->fields[$key]['list'];
5451
		$hidden=(in_array(abs($this->fields[$key]['visible']), array(0,2)) ? 1 : 0);
5452
5453
		$objectid = $this->id;
5454
5455
5456
		if ($computed)
5457
		{
5458
			if (! preg_match('/^search_/', $keyprefix)) return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
5459
			else return '';
5460
		}
5461
5462
5463
		// Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
5464
		if (empty($morecss) && ! empty($val['css']))
5465
		{
5466
		    $morecss = $val['css'];
5467
		}
5468
		elseif (empty($morecss))
5469
		{
5470
			if ($type == 'date')
5471
			{
5472
				$morecss = 'minwidth100imp';
5473
			}
5474
			elseif ($type == 'datetime')
5475
			{
5476
				$morecss = 'minwidth200imp';
5477
			}
5478
			elseif (in_array($type, array('int','integer','price')) || preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type))
5479
			{
5480
				$morecss = 'maxwidth75';
5481
			} elseif ($type == 'url') {
5482
				$morecss='minwidth400';
5483
			}
5484
			elseif ($type == 'boolean')
5485
			{
5486
				$morecss='';
5487
			}
5488
			else
5489
			{
5490
				if (round($size) < 12)
5491
				{
5492
					$morecss = 'minwidth100';
5493
				}
5494
				elseif (round($size) <= 48)
5495
				{
5496
					$morecss = 'minwidth200';
5497
				}
5498
				else
5499
				{
5500
					$morecss = 'minwidth400';
5501
				}
5502
			}
5503
		}
5504
5505
		if (in_array($type, array('date','datetime')))
5506
		{
5507
			$tmp=explode(',', $size);
5508
			$newsize=$tmp[0];
5509
5510
			$showtime = in_array($type, array('datetime')) ? 1 : 0;
5511
5512
			// Do not show current date when field not required (see selectDate() method)
5513
			if (!$required && $value == '') $value = '-1';
5514
5515
			// TODO Must also support $moreparam
5516
			$out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
5517
		}
5518
		elseif (in_array($type, array('int','integer')))
5519
		{
5520
			$tmp=explode(',', $size);
5521
			$newsize=$tmp[0];
5522
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$newsize.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5523
		}
5524
		elseif (in_array($type, array('real')))
5525
		{
5526
		    $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5527
		}
5528
		elseif (preg_match('/varchar/', $type))
5529
		{
5530
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$size.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5531
		}
5532
		elseif (in_array($type, array('mail', 'phone', 'url')))
5533
		{
5534
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5535
		}
5536
		elseif ($type == 'text')
5537
		{
5538
			if (! preg_match('/search_/', $keyprefix))		// If keyprefix is search_ or search_options_, we must just use a simple text field
5539
			{
5540
				require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5541
				$doleditor=new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
5542
				$out=$doleditor->Create(1);
5543
			}
5544
			else
5545
			{
5546
				$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5547
			}
5548
		}
5549
		elseif ($type == 'html')
5550
		{
5551
			if (! preg_match('/search_/', $keyprefix))		// If keyprefix is search_ or search_options_, we must just use a simple text field
5552
			{
5553
				require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5554
				$doleditor=new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, ! empty($conf->fckeditor->enabled) && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
5555
				$out=$doleditor->Create(1);
5556
			}
5557
			else
5558
			{
5559
				$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5560
			}
5561
		}
5562
		elseif ($type == 'boolean')
5563
		{
5564
			$checked='';
5565
			if (!empty($value)) {
5566
				$checked=' checked value="1" ';
5567
			} else {
5568
				$checked=' value="1" ';
5569
			}
5570
			$out='<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam?$moreparam:'').'>';
5571
		}
5572
		elseif ($type == 'price')
5573
		{
5574
			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5575
				$value=price($value);
5576
			}
5577
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> '.$langs->getCurrencySymbol($conf->currency);
5578
		}
5579
		elseif (preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type))
5580
		{
5581
			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5582
				$value=price($value);
5583
			}
5584
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> ';
5585
		}
5586
		elseif ($type == 'select')
5587
		{
5588
			$out = '';
5589
			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5590
			{
5591
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5592
				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5593
			}
5594
5595
			$out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5596
                if((! isset($this->fields[$key]['default'])) ||($this->fields[$key]['notnull']!=1))$out.='<option value="0">&nbsp;</option>';
5597
			foreach ($param['options'] as $key => $val)
0 ignored issues
show
introduced by
$key is overwriting one of the parameters of this function.
Loading history...
introduced by
$val is overwriting one of the parameters of this function.
Loading history...
5598
			{
5599
				if ((string) $key == '') continue;
5600
				list($val, $parent) = explode('|', $val);
5601
				$out.='<option value="'.$key.'"';
5602
				$out.= (((string) $value == (string) $key)?' selected':'');
5603
				$out.= (!empty($parent)?' parent="'.$parent.'"':'');
5604
				$out.='>'.$val.'</option>';
5605
			}
5606
			$out.='</select>';
5607
		}
5608
		elseif ($type == 'sellist')
5609
		{
5610
			$out = '';
5611
			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5612
			{
5613
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5614
				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5615
			}
5616
5617
			$out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5618
			if (is_array($param['options']))
5619
			{
5620
				$param_list=array_keys($param['options']);
5621
				$InfoFieldList = explode(":", $param_list[0]);
5622
				$parentName='';
5623
				$parentField='';
5624
				// 0 : tableName
5625
				// 1 : label field name
5626
				// 2 : key fields name (if differ of rowid)
5627
				// 3 : key field parent (for dependent lists)
5628
				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5629
				$keyList=(empty($InfoFieldList[2])?'rowid':$InfoFieldList[2].' as rowid');
5630
5631
5632
				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4]))
5633
				{
5634
					if (strpos($InfoFieldList[4], 'extra.') !== false)
5635
					{
5636
						$keyList='main.'.$InfoFieldList[2].' as rowid';
5637
					} else {
5638
						$keyList=$InfoFieldList[2].' as rowid';
5639
					}
5640
				}
5641
				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3]))
5642
				{
5643
					list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
5644
					$keyList.= ', '.$parentField;
5645
				}
5646
5647
				$fields_label = explode('|', $InfoFieldList[1]);
5648
				if (is_array($fields_label))
1 ignored issue
show
introduced by
The condition is_array($fields_label) is always true.
Loading history...
5649
				{
5650
					$keyList .=', ';
5651
					$keyList .= implode(', ', $fields_label);
5652
				}
5653
5654
				$sqlwhere='';
5655
				$sql = 'SELECT '.$keyList;
5656
				$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
5657
				if (!empty($InfoFieldList[4]))
5658
				{
5659
					// can use SELECT request
5660
					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5661
						$InfoFieldList[4]=str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
5662
					}
5663
5664
					// current object id can be use into filter
5665
					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5666
						$InfoFieldList[4]=str_replace('$ID$', $objectid, $InfoFieldList[4]);
5667
					} else {
5668
						$InfoFieldList[4]=str_replace('$ID$', '0', $InfoFieldList[4]);
5669
					}
5670
					//We have to join on extrafield table
5671
					if (strpos($InfoFieldList[4], 'extra')!==false)
5672
					{
5673
						$sql.= ' as main, '.MAIN_DB_PREFIX .$InfoFieldList[0].'_extrafields as extra';
5674
						$sqlwhere.= ' WHERE extra.fk_object=main.'.$InfoFieldList[2]. ' AND '.$InfoFieldList[4];
5675
					}
5676
					else
5677
					{
5678
						$sqlwhere.= ' WHERE '.$InfoFieldList[4];
5679
					}
5680
				}
5681
				else
5682
				{
5683
					$sqlwhere.= ' WHERE 1=1';
5684
				}
5685
				// Some tables may have field, some other not. For the moment we disable it.
5686
				if (in_array($InfoFieldList[0], array('tablewithentity')))
5687
				{
5688
					$sqlwhere.= ' AND entity = '.$conf->entity;
5689
				}
5690
				$sql.=$sqlwhere;
5691
				//print $sql;
5692
5693
				$sql .= ' ORDER BY ' . implode(', ', $fields_label);
5694
5695
				dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
5696
				$resql = $this->db->query($sql);
5697
				if ($resql)
5698
				{
5699
					$out.='<option value="0">&nbsp;</option>';
5700
					$num = $this->db->num_rows($resql);
5701
					$i = 0;
5702
					while ($i < $num)
5703
					{
5704
						$labeltoshow='';
5705
						$obj = $this->db->fetch_object($resql);
5706
5707
						// Several field into label (eq table:code|libelle:rowid)
5708
						$notrans = false;
5709
						$fields_label = explode('|', $InfoFieldList[1]);
5710
						if (is_array($fields_label))
5711
						{
5712
							$notrans = true;
5713
							foreach ($fields_label as $field_toshow)
5714
							{
5715
								$labeltoshow.= $obj->$field_toshow.' ';
5716
							}
5717
						}
5718
						else
5719
						{
5720
							$labeltoshow=$obj->{$InfoFieldList[1]};
5721
						}
5722
						$labeltoshow=dol_trunc($labeltoshow, 45);
5723
5724
						if ($value == $obj->rowid)
5725
						{
5726
							foreach ($fields_label as $field_toshow)
5727
							{
5728
								$translabel=$langs->trans($obj->$field_toshow);
5729
								if ($translabel!=$obj->$field_toshow) {
5730
									$labeltoshow=dol_trunc($translabel, 18).' ';
5731
								}else {
5732
									$labeltoshow=dol_trunc($obj->$field_toshow, 18).' ';
5733
								}
5734
							}
5735
							$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5736
						}
5737
						else
5738
						{
5739
							if (! $notrans)
5740
							{
5741
								$translabel=$langs->trans($obj->{$InfoFieldList[1]});
5742
								if ($translabel!=$obj->{$InfoFieldList[1]}) {
5743
									$labeltoshow=dol_trunc($translabel, 18);
5744
								}
5745
								else {
5746
									$labeltoshow=dol_trunc($obj->{$InfoFieldList[1]}, 18);
5747
								}
5748
							}
5749
							if (empty($labeltoshow)) $labeltoshow='(not defined)';
5750
							if ($value==$obj->rowid)
5751
							{
5752
								$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5753
							}
5754
5755
							if (!empty($InfoFieldList[3]) && $parentField)
5756
							{
5757
								$parent = $parentName.':'.$obj->{$parentField};
5758
							}
5759
5760
							$out.='<option value="'.$obj->rowid.'"';
5761
							$out.= ($value==$obj->rowid?' selected':'');
5762
							$out.= (!empty($parent)?' parent="'.$parent.'"':'');
5763
							$out.='>'.$labeltoshow.'</option>';
5764
						}
5765
5766
						$i++;
5767
					}
5768
					$this->db->free($resql);
5769
				}
5770
				else {
5771
					print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
5772
				}
5773
			}
5774
			$out.='</select>';
5775
		}
5776
		elseif ($type == 'checkbox')
5777
		{
5778
			$value_arr=explode(',', $value);
5779
			$out=$form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options'])?null:$param['options']), $value_arr, '', 0, '', 0, '100%');
5780
		}
5781
		elseif ($type == 'radio')
5782
		{
5783
			$out='';
5784
			foreach ($param['options'] as $keyopt => $val)
0 ignored issues
show
introduced by
$val is overwriting one of the parameters of this function.
Loading history...
5785
			{
5786
				$out.='<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'');
5787
				$out.=' value="'.$keyopt.'"';
5788
				$out.=' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
5789
				$out.= ($value==$keyopt?'checked':'');
5790
				$out.='/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$val.'</label><br>';
5791
			}
5792
		}
5793
		elseif ($type == 'chkbxlst')
5794
		{
5795
			if (is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
5796
				$value_arr = $value;
5797
			}
5798
			else {
5799
				$value_arr = explode(',', $value);
5800
			}
5801
5802
			if (is_array($param['options'])) {
5803
				$param_list = array_keys($param['options']);
5804
				$InfoFieldList = explode(":", $param_list[0]);
5805
				$parentName='';
5806
				$parentField='';
5807
				// 0 : tableName
5808
				// 1 : label field name
5809
				// 2 : key fields name (if differ of rowid)
5810
				// 3 : key field parent (for dependent lists)
5811
				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5812
				$keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
5813
5814
				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3])) {
5815
					list ( $parentName, $parentField ) = explode('|', $InfoFieldList[3]);
5816
					$keyList .= ', ' . $parentField;
5817
				}
5818
				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4])) {
5819
					if (strpos($InfoFieldList[4], 'extra.') !== false) {
5820
						$keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
5821
					} else {
5822
						$keyList = $InfoFieldList[2] . ' as rowid';
5823
					}
5824
				}
5825
5826
				$fields_label = explode('|', $InfoFieldList[1]);
5827
				if (is_array($fields_label)) {
1 ignored issue
show
introduced by
The condition is_array($fields_label) is always true.
Loading history...
5828
					$keyList .= ', ';
5829
					$keyList .= implode(', ', $fields_label);
5830
				}
5831
5832
				$sqlwhere = '';
5833
				$sql = 'SELECT ' . $keyList;
5834
				$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
5835
				if (! empty($InfoFieldList[4])) {
5836
5837
					// can use SELECT request
5838
					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5839
						$InfoFieldList[4]=str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
5840
					}
5841
5842
					// current object id can be use into filter
5843
					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5844
						$InfoFieldList[4]=str_replace('$ID$', $objectid, $InfoFieldList[4]);
5845
					} else {
5846
						$InfoFieldList[4]=str_replace('$ID$', '0', $InfoFieldList[4]);
5847
					}
5848
5849
					// We have to join on extrafield table
5850
					if (strpos($InfoFieldList[4], 'extra') !== false) {
5851
						$sql .= ' as main, ' . MAIN_DB_PREFIX . $InfoFieldList[0] . '_extrafields as extra';
5852
						$sqlwhere .= ' WHERE extra.fk_object=main.' . $InfoFieldList[2] . ' AND ' . $InfoFieldList[4];
5853
					} else {
5854
						$sqlwhere .= ' WHERE ' . $InfoFieldList[4];
5855
					}
5856
				} else {
5857
					$sqlwhere .= ' WHERE 1=1';
5858
				}
5859
				// Some tables may have field, some other not. For the moment we disable it.
5860
				if (in_array($InfoFieldList[0], array ('tablewithentity')))
5861
				{
5862
					$sqlwhere .= ' AND entity = ' . $conf->entity;
5863
				}
5864
				// $sql.=preg_replace('/^ AND /','',$sqlwhere);
5865
				// print $sql;
5866
5867
				$sql .= $sqlwhere;
5868
				dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
5869
				$resql = $this->db->query($sql);
5870
				if ($resql) {
5871
					$num = $this->db->num_rows($resql);
5872
					$i = 0;
5873
5874
					$data=array();
5875
5876
					while ( $i < $num ) {
5877
						$labeltoshow = '';
5878
						$obj = $this->db->fetch_object($resql);
5879
5880
						$notrans = false;
5881
						// Several field into label (eq table:code|libelle:rowid)
5882
						$fields_label = explode('|', $InfoFieldList[1]);
5883
						if (is_array($fields_label)) {
5884
							$notrans = true;
5885
							foreach ($fields_label as $field_toshow) {
5886
								$labeltoshow .= $obj->$field_toshow . ' ';
5887
							}
5888
						} else {
5889
							$labeltoshow = $obj->{$InfoFieldList[1]};
5890
						}
5891
						$labeltoshow = dol_trunc($labeltoshow, 45);
5892
5893
						if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5894
							foreach ($fields_label as $field_toshow) {
5895
								$translabel = $langs->trans($obj->$field_toshow);
5896
								if ($translabel != $obj->$field_toshow) {
5897
									$labeltoshow = dol_trunc($translabel, 18) . ' ';
5898
								} else {
5899
									$labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
5900
								}
5901
							}
5902
5903
							$data[$obj->rowid]=$labeltoshow;
5904
						} else {
5905
							if (! $notrans) {
5906
								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
5907
								if ($translabel != $obj->{$InfoFieldList[1]}) {
5908
									$labeltoshow = dol_trunc($translabel, 18);
5909
								} else {
5910
									$labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
5911
								}
5912
							}
5913
							if (empty($labeltoshow))
5914
								$labeltoshow = '(not defined)';
5915
5916
								if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5917
									$data[$obj->rowid]=$labeltoshow;
5918
								}
5919
5920
								if (! empty($InfoFieldList[3]) && $parentField) {
5921
									$parent = $parentName . ':' . $obj->{$parentField};
5922
								}
5923
5924
								$data[$obj->rowid]=$labeltoshow;
5925
						}
5926
5927
						$i ++;
5928
					}
5929
					$this->db->free($resql);
5930
5931
					$out=$form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
5932
				} else {
5933
					print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
5934
				}
5935
			}
5936
		}
5937
		elseif ($type == 'link')
5938
		{
5939
			$param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
5940
			$showempty=(($required && $default != '')?0:1);
5941
			$out=$form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty);
5942
			if ($conf->global->MAIN_FEATURES_LEVEL >= 2)
5943
			{
5944
            			list($class,$classfile)=explode(':', $param_list[0]);
5945
            			if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) $url_path=dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
5946
            			else $url_path=dol_buildpath(dirname(dirname($classfile)).'/'.$class.'_card.php', 1);
5947
            			$out.='<a class="butActionNew" href="'.$url_path.'?action=create&backtopage='.$_SERVER['PHP_SELF'].'"><span class="fa fa-plus-circle valignmiddle"></span></a>';
5948
            			// TODO Add Javascript code to add input fields contents to new elements urls
5949
			}
5950
		}
5951
		elseif ($type == 'password')
5952
		{
5953
			// If prefix is 'search_', field is used as a filter, we use a common text field.
5954
			$out='<input type="'.($keyprefix=='search_'?'text':'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'>';
5955
		}
5956
		elseif ($type == 'array')
5957
		{
5958
			$newval = $val;
5959
			$newval['type'] = 'varchar(256)';
5960
5961
			$out='';
5962
5963
			$inputs = array();
5964
			if(! empty($value)) {
5965
				foreach($value as $option) {
0 ignored issues
show
Bug introduced by
The expression $value of type string is not traversable.
Loading history...
5966
					$out.= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5967
					$out.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
5968
				}
5969
			}
5970
5971
			$out.= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
5972
5973
			$newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5974
			$newInput.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
5975
5976
			if(! empty($conf->use_javascript_ajax)) {
5977
				$out.= '
5978
					<script>
5979
					$(document).ready(function() {
5980
						$("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
5981
							$("'.dol_escape_js($newInput).'").insertBefore(this);
5982
						});
5983
5984
						$(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
5985
							$(this).parent().remove();
5986
						});
5987
					});
5988
					</script>';
5989
			}
5990
		}
5991
		if (!empty($hidden)) {
5992
			$out='<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
5993
		}
5994
		/* Add comments
5995
		 if ($type == 'date') $out.=' (YYYY-MM-DD)';
5996
		 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
5997
		 */
5998
		return $out;
5999
	}
6000
6001
	/**
6002
	 * Return HTML string to show a field into a page
6003
	 * Code very similar with showOutputField of extra fields
6004
	 *
6005
	 * @param  array   $val		       Array of properties of field to show
6006
	 * @param  string  $key            Key of attribute
6007
	 * @param  string  $value          Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value)
6008
	 * @param  string  $moreparam      To add more parametes on html input tag
6009
	 * @param  string  $keysuffix      Prefix string to add into name and id of field (can be used to avoid duplicate names)
6010
	 * @param  string  $keyprefix      Suffix string to add into name and id of field (can be used to avoid duplicate names)
6011
	 * @param  mixed   $morecss        Value for css to define size. May also be a numeric.
6012
	 * @return string
6013
	 */
6014
	public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
6015
	{
6016
		global $conf,$langs,$form;
6017
6018
		if (! is_object($form))
6019
		{
6020
			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
6021
			$form=new Form($this->db);
6022
		}
6023
6024
		$objectid = $this->id;
6025
		$label = $val['label'];
6026
		$type  = $val['type'];
6027
		$size  = $val['css'];
6028
6029
		// Convert var to be able to share same code than showOutputField of extrafields
6030
		if (preg_match('/varchar\((\d+)\)/', $type, $reg))
6031
		{
6032
			$type = 'varchar';		// convert varchar(xx) int varchar
6033
			$size = $reg[1];
6034
		}
6035
		elseif (preg_match('/varchar/', $type)) $type = 'varchar';		// convert varchar(xx) int varchar
6036
		if (is_array($val['arrayofkeyval'])) $type='select';
6037
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) $type='link';
6038
6039
		$default=$val['default'];
6040
		$computed=$val['computed'];
6041
		$unique=$val['unique'];
6042
		$required=$val['required'];
6043
		$param=$val['param'];
6044
		if (is_array($val['arrayofkeyval'])) $param['options'] = $val['arrayofkeyval'];
6045
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg))
6046
		{
6047
			$type='link';
6048
			$param['options']=array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
6049
		}
6050
		$langfile=$val['langfile'];
6051
		$list=$val['list'];
6052
		$help=$val['help'];
6053
		$hidden=(($val['visible'] == 0) ? 1 : 0);			// If zero, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller)
6054
6055
		if ($hidden) return '';
6056
6057
		// If field is a computed field, value must become result of compute
6058
		if ($computed)
6059
		{
6060
			// Make the eval of compute string
6061
			//var_dump($computed);
6062
			$value = dol_eval($computed, 1, 0);
6063
		}
6064
6065
		if (empty($morecss))
6066
		{
6067
			if ($type == 'date')
6068
			{
6069
				$morecss = 'minwidth100imp';
6070
			}
6071
			elseif ($type == 'datetime')
6072
			{
6073
				$morecss = 'minwidth200imp';
6074
			}
6075
			elseif (in_array($type, array('int','double','price')))
6076
			{
6077
				$morecss = 'maxwidth75';
6078
			}
6079
			elseif ($type == 'url')
6080
			{
6081
				$morecss='minwidth400';
6082
			}
6083
			elseif ($type == 'boolean')
6084
			{
6085
				$morecss='';
6086
			}
6087
			else
6088
			{
6089
				if (round($size) < 12)
6090
				{
6091
					$morecss = 'minwidth100';
6092
				}
6093
				elseif (round($size) <= 48)
6094
				{
6095
					$morecss = 'minwidth200';
6096
				}
6097
				else
6098
				{
6099
					$morecss = 'minwidth400';
6100
				}
6101
			}
6102
		}
6103
6104
		// Format output value differently according to properties of field
6105
		if ($key == 'ref' && method_exists($this, 'getNomUrl')) $value=$this->getNomUrl(1, '', 0, '', 1);
6106
		elseif ($key == 'status' && method_exists($this, 'getLibStatut')) $value=$this->getLibStatut(3);
6107
		elseif ($type == 'date')
6108
		{
6109
			if(! empty($value)) {
6110
				$value=dol_print_date($value, 'day');
6111
			} else {
6112
				$value='';
6113
			}
6114
		}
6115
		elseif ($type == 'datetime')
6116
		{
6117
			if(! empty($value)) {
6118
				$value=dol_print_date($value, 'dayhour');
6119
			} else {
6120
				$value='';
6121
			}
6122
		}
6123
		elseif ($type == 'double')
6124
		{
6125
			if (!empty($value)) {
6126
				$value=price($value);
6127
			}
6128
		}
6129
		elseif ($type == 'real')
6130
		{
6131
		    if (!empty($value)) {
6132
		        $value=price($value);
6133
		    }
6134
		}
6135
		elseif ($type == 'boolean')
6136
		{
6137
			$checked='';
6138
			if (!empty($value)) {
6139
				$checked=' checked ';
6140
			}
6141
			$value='<input type="checkbox" '.$checked.' '.($moreparam?$moreparam:'').' readonly disabled>';
6142
		}
6143
		elseif ($type == 'mail')
6144
		{
6145
			$value=dol_print_email($value, 0, 0, 0, 64, 1, 1);
6146
		}
6147
		elseif ($type == 'url')
6148
		{
6149
			$value=dol_print_url($value, '_blank', 32, 1);
6150
		}
6151
		elseif ($type == 'phone')
6152
		{
6153
			$value=dol_print_phone($value, '', 0, 0, '', '&nbsp;', 1);
6154
		}
6155
		elseif ($type == 'price')
6156
		{
6157
			$value=price($value, 0, $langs, 0, 0, -1, $conf->currency);
6158
		}
6159
		elseif ($type == 'select')
6160
		{
6161
			$value=$param['options'][$value];
6162
		}
6163
		elseif ($type == 'sellist')
6164
		{
6165
			$param_list=array_keys($param['options']);
6166
			$InfoFieldList = explode(":", $param_list[0]);
6167
6168
			$selectkey="rowid";
6169
			$keyList='rowid';
6170
6171
			if (count($InfoFieldList)>=3)
6172
			{
6173
				$selectkey = $InfoFieldList[2];
6174
				$keyList=$InfoFieldList[2].' as rowid';
6175
			}
6176
6177
			$fields_label = explode('|', $InfoFieldList[1]);
6178
			if(is_array($fields_label)) {
1 ignored issue
show
introduced by
The condition is_array($fields_label) is always true.
Loading history...
6179
				$keyList .=', ';
6180
				$keyList .= implode(', ', $fields_label);
6181
			}
6182
6183
			$sql = 'SELECT '.$keyList;
6184
			$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
6185
			if (strpos($InfoFieldList[4], 'extra')!==false)
6186
			{
6187
				$sql.= ' as main';
6188
			}
6189
			if ($selectkey=='rowid' && empty($value)) {
6190
				$sql.= " WHERE ".$selectkey."=0";
6191
			} elseif ($selectkey=='rowid') {
6192
				$sql.= " WHERE ".$selectkey."=".$this->db->escape($value);
6193
			}else {
6194
				$sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6195
			}
6196
6197
			//$sql.= ' AND entity = '.$conf->entity;
6198
6199
			dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
6200
			$resql = $this->db->query($sql);
6201
			if ($resql)
6202
			{
6203
				$value='';	// value was used, so now we reste it to use it to build final output
6204
6205
				$obj = $this->db->fetch_object($resql);
6206
6207
				// Several field into label (eq table:code|libelle:rowid)
6208
				$fields_label = explode('|', $InfoFieldList[1]);
6209
6210
				if(is_array($fields_label) && count($fields_label)>1)
6211
				{
6212
					foreach ($fields_label as $field_toshow)
6213
					{
6214
						$translabel='';
6215
						if (!empty($obj->$field_toshow)) {
6216
							$translabel=$langs->trans($obj->$field_toshow);
6217
						}
6218
						if ($translabel!=$field_toshow) {
6219
							$value.=dol_trunc($translabel, 18).' ';
6220
						}else {
6221
							$value.=$obj->$field_toshow.' ';
6222
						}
6223
					}
6224
				}
6225
				else
6226
				{
6227
					$translabel='';
6228
					if (!empty($obj->{$InfoFieldList[1]})) {
6229
						$translabel=$langs->trans($obj->{$InfoFieldList[1]});
6230
					}
6231
					if ($translabel!=$obj->{$InfoFieldList[1]}) {
6232
						$value=dol_trunc($translabel, 18);
6233
					}else {
6234
						$value=$obj->{$InfoFieldList[1]};
6235
					}
6236
				}
6237
			}
6238
			else dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
6239
		}
6240
		elseif ($type == 'radio')
6241
		{
6242
			$value=$param['options'][$value];
6243
		}
6244
		elseif ($type == 'checkbox')
6245
		{
6246
			$value_arr=explode(',', $value);
6247
			$value='';
6248
			if (is_array($value_arr) && count($value_arr)>0)
6249
			{
6250
				$toprint=array();
6251
				foreach ($value_arr as $keyval=>$valueval) {
6252
					$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$param['options'][$valueval].'</li>';
6253
				}
6254
				$value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6255
			}
6256
		}
6257
		elseif ($type == 'chkbxlst')
6258
		{
6259
			$value_arr = explode(',', $value);
6260
6261
			$param_list = array_keys($param['options']);
6262
			$InfoFieldList = explode(":", $param_list[0]);
6263
6264
			$selectkey = "rowid";
6265
			$keyList = 'rowid';
6266
6267
			if (count($InfoFieldList) >= 3) {
6268
				$selectkey = $InfoFieldList[2];
6269
				$keyList = $InfoFieldList[2] . ' as rowid';
6270
			}
6271
6272
			$fields_label = explode('|', $InfoFieldList[1]);
6273
			if (is_array($fields_label)) {
1 ignored issue
show
introduced by
The condition is_array($fields_label) is always true.
Loading history...
6274
				$keyList .= ', ';
6275
				$keyList .= implode(', ', $fields_label);
6276
			}
6277
6278
			$sql = 'SELECT ' . $keyList;
6279
			$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
6280
			if (strpos($InfoFieldList[4], 'extra') !== false) {
6281
				$sql .= ' as main';
6282
			}
6283
			// $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6284
			// $sql.= ' AND entity = '.$conf->entity;
6285
6286
			dol_syslog(get_class($this) . ':showOutputField:$type=chkbxlst', LOG_DEBUG);
6287
			$resql = $this->db->query($sql);
6288
			if ($resql) {
6289
				$value = ''; // value was used, so now we reste it to use it to build final output
6290
				$toprint=array();
6291
				while ( $obj = $this->db->fetch_object($resql) ) {
6292
6293
					// Several field into label (eq table:code|libelle:rowid)
6294
					$fields_label = explode('|', $InfoFieldList[1]);
6295
					if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
6296
						if (is_array($fields_label) && count($fields_label) > 1) {
6297
							foreach ($fields_label as $field_toshow) {
6298
								$translabel = '';
6299
								if (! empty($obj->$field_toshow)) {
6300
									$translabel = $langs->trans($obj->$field_toshow);
6301
								}
6302
								if ($translabel != $field_toshow) {
6303
									$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6304
								} else {
6305
									$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->$field_toshow.'</li>';
6306
								}
6307
							}
6308
						} else {
6309
							$translabel = '';
6310
							if (! empty($obj->{$InfoFieldList[1]})) {
6311
								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
6312
							}
6313
							if ($translabel != $obj->{$InfoFieldList[1]}) {
6314
								$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6315
							} else {
6316
								$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->{$InfoFieldList[1]}.'</li>';
6317
							}
6318
						}
6319
					}
6320
				}
6321
				$value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6322
			} else {
6323
				dol_syslog(get_class($this) . '::showOutputField error ' . $this->db->lasterror(), LOG_WARNING);
6324
			}
6325
		}
6326
		elseif ($type == 'link')
6327
		{
6328
			$out='';
6329
6330
			// only if something to display (perf)
6331
			if ($value)
6332
			{
6333
				$param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
6334
6335
				$InfoFieldList = explode(":", $param_list[0]);
6336
				$classname=$InfoFieldList[0];
6337
				$classpath=$InfoFieldList[1];
6338
				$getnomurlparam=(empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]);
6339
				if (! empty($classpath))
6340
				{
6341
					dol_include_once($InfoFieldList[1]);
6342
					if ($classname && class_exists($classname))
6343
					{
6344
						$object = new $classname($this->db);
6345
						$object->fetch($value);
6346
						$value=$object->getNomUrl($getnomurlparam);
6347
					}
6348
				}
6349
				else
6350
				{
6351
					dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6352
					return 'Error bad setup of extrafield';
6353
				}
6354
			}
6355
			else $value='';
6356
		}
6357
		elseif ($type == 'text' || $type == 'html')
6358
		{
6359
			$value=dol_htmlentitiesbr($value);
6360
		}
6361
		elseif ($type == 'password')
6362
		{
6363
			$value=preg_replace('/./i', '*', $value);
6364
		}
6365
		elseif ($type == 'array')
6366
		{
6367
			$value = implode('<br>', $value);
6368
		}
6369
6370
		//print $type.'-'.$size;
6371
		$out=$value;
6372
6373
		return $out;
6374
	}
6375
6376
6377
	/**
6378
	 * Function to show lines of extrafields with output datas
6379
	 *
6380
	 * @param 	Extrafields $extrafields    Extrafield Object
6381
	 * @param 	string      $mode           Show output (view) or input (edit) for extrafield
6382
	 * @param 	array       $params         Optional parameters. Example: array('style'=>'class="oddeven"', 'colspan'=>$colspan)
6383
	 * @param 	string      $keysuffix      Suffix string to add after name and id of field (can be used to avoid duplicate names)
6384
	 * @param 	string      $keyprefix      Prefix string to add before name and id of field (can be used to avoid duplicate names)
6385
	 * @param	string		$onetrtd		All fields in same tr td (TODO field not used ?)
6386
	 * @return 	string
6387
	 */
6388
	public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = 0)
6389
	{
6390
		global $db, $conf, $langs, $action, $form;
6391
6392
		if (! is_object($form)) $form=new Form($db);
6393
6394
		$out = '';
6395
6396
		if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0)
6397
		{
6398
			$out .= "\n";
6399
			$out .= '<!-- showOptionalsInput --> ';
6400
			$out .= "\n";
6401
6402
            $extrafields_collapse_num = '';
6403
			$e = 0;
6404
			foreach($extrafields->attributes[$this->table_element]['label'] as $key=>$label)
6405
			{
6406
				// Show only the key field in params
6407
				if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) continue;
6408
6409
				// @TODO Add test also on 'enabled' (different than 'list' that is 'visibility')
6410
				$enabled = 1;
6411
6412
				$visibility = 1;
6413
				if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key]))
6414
				{
6415
				    $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1);
6416
				}
6417
6418
				$perms = 1;
6419
				if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key]))
6420
				{
6421
					$perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1);
6422
				}
6423
6424
				if (($mode == 'create' || $mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3) continue;	// <> -1 and <> 1 and <> 3 = not visible on forms, only on list
6425
				elseif($mode == 'view' && empty($visibility)) continue;
6426
				if (empty($perms)) continue;
6427
6428
				// Load language if required
6429
				if (! empty($extrafields->attributes[$this->table_element]['langfile'][$key])) $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
6430
6431
				$colspan='3';
6432
				if (is_array($params) && count($params)>0) {
6433
					if (array_key_exists('colspan', $params)) {
6434
						$colspan=$params['colspan'];
6435
					}
6436
				}
6437
6438
				switch($mode) {
6439
					case "view":
6440
						$value=$this->array_options["options_".$key.$keysuffix];
6441
						break;
6442
					case "edit":
6443
						$getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, 'none');				// GETPOST can get value from GET, POST or setup of default values.
6444
						// GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
6445
						if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix))
6446
						{
6447
							if (is_array($getposttemp)) {
6448
								// $getposttemp is an array but following code expects a comma separated string
6449
								$value = implode(",", $getposttemp);
6450
							} else {
6451
								$value = $getposttemp;
6452
							}
6453
						} else {
6454
							$value = $this->array_options["options_" . $key];			// No GET, no POST, no default value, so we take value of object.
6455
						}
6456
						//var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
6457
						break;
6458
				}
6459
6460
				if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate')
6461
				{
6462
                    $extrafields_collapse_num = '';
6463
                    $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
6464
                    if (!empty($extrafield_param) && is_array($extrafield_param)) {
6465
                        $extrafield_param_list = array_keys($extrafield_param['options']);
6466
6467
                        if (count($extrafield_param_list)>0) {
6468
                            $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
6469
6470
                            if ($extrafield_collapse_display_value==1 || $extrafield_collapse_display_value==2) {
6471
                                $extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
6472
                            }
6473
                        }
6474
                    }
6475
6476
					$out .= $extrafields->showSeparator($key, $this);
6477
				}
6478
				else
6479
				{
6480
					$csstyle='';
6481
					$class=(!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
6482
					if (is_array($params) && count($params)>0) {
6483
						if (array_key_exists('style', $params)) {
6484
							$csstyle=$params['style'];
6485
						}
6486
					}
6487
6488
					// add html5 elements
6489
					$domData  = ' data-element="extrafield"';
6490
					$domData .= ' data-targetelement="'.$this->element.'"';
6491
					$domData .= ' data-targetid="'.$this->id.'"';
6492
6493
					$html_id = !empty($this->id) ? 'extrarow-'.$this->element.'_'.$key.'_'.$this->id : '';
6494
6495
					$out .= '<tr id="'.$html_id.'" '.$csstyle.' class="'.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$extrafields_collapse_num.'" '.$domData.' >';
6496
6497
					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) { $colspan='0'; }
6498
6499
					if ($action == 'selectlines') { $colspan++; }
6500
6501
					// Convert date into timestamp format (value in memory must be a timestamp)
6502
					if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime')))
6503
					{
6504
						$datenotinstring = $this->array_options['options_' . $key];
6505
						if (! is_numeric($this->array_options['options_' . $key]))	// For backward compatibility
6506
						{
6507
							$datenotinstring = $this->db->jdate($datenotinstring);
6508
						}
6509
						$value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?dol_mktime(GETPOST($keyprefix.'options_'.$key.$keysuffix."hour", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."min", 'int', 3), 0, GETPOST($keyprefix.'options_'.$key.$keysuffix."month", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."day", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."year", 'int', 3)):$datenotinstring;
6510
					}
6511
					// Convert float submited string into real php numeric (value in memory must be a php numeric)
6512
					if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price','double')))
6513
					{
6514
						$value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?price2num(GETPOST($keyprefix.'options_'.$key.$keysuffix, 'alpha', 3)):$this->array_options['options_'.$key];
6515
					}
6516
6517
					$labeltoshow = $langs->trans($label);
6518
6519
					$out .= '<td class="titlefield';
6520
					if (GETPOST('action', 'none') == 'create') $out.='create';
6521
					if ($mode != 'view' && ! empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= ' fieldrequired';
6522
					$out .= '">';
6523
					if (! empty($extrafields->attributes[$object->table_element]['help'][$key])) $out .= $form->textwithpicto($labeltoshow, $extrafields->attributes[$object->table_element]['help'][$key]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $object seems to be never defined.
Loading history...
6524
					else $out .= $labeltoshow;
6525
					$out .= '</td>';
6526
6527
					$html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
6528
					$out .='<td id="'.$html_id.'" class="'.$this->element.'_extras_'.$key.'" '.($colspan?' colspan="'.$colspan.'"':'').'>';
6529
6530
					switch($mode) {
6531
						case "view":
6532
							$out .= $extrafields->showOutputField($key, $value);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $value does not seem to be defined for all execution paths leading up to this point.
Loading history...
6533
							break;
6534
						case "edit":
6535
							$out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
6536
							break;
6537
					}
6538
6539
					$out .= '</td>';
6540
6541
					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>';
6542
					else $out .= '</tr>';
6543
					$e++;
6544
				}
6545
			}
6546
			$out .= "\n";
6547
			// Add code to manage list depending on others
6548
			if (! empty($conf->use_javascript_ajax)) {
6549
				$out .= '
6550
				<script>
6551
				    jQuery(document).ready(function() {
6552
				    	function showOptions(child_list, parent_list)
6553
				    	{
6554
				    		var val = $("select[name=\""+parent_list+"\"]").val();
6555
				    		var parentVal = parent_list + ":" + val;
6556
							if(val > 0) {
6557
					    		$("select[name=\""+child_list+"\"] option[parent]").hide();
6558
					    		$("select[name=\""+child_list+"\"] option[parent=\""+parentVal+"\"]").show();
6559
							} else {
6560
								$("select[name=\""+child_list+"\"] option").show();
6561
							}
6562
				    	}
6563
						function setListDependencies() {
6564
					    	jQuery("select option[parent]").parent().each(function() {
6565
					    		var child_list = $(this).attr("name");
6566
								var parent = $(this).find("option[parent]:first").attr("parent");
6567
								var infos = parent.split(":");
6568
								var parent_list = infos[0];
6569
								$("select[name=\""+parent_list+"\"]").change(function() {
6570
									showOptions(child_list, parent_list);
6571
								});
6572
					    	});
6573
						}
6574
6575
						setListDependencies();
6576
				    });
6577
				</script>'."\n";
6578
				$out .= '<!-- /showOptionalsInput --> '."\n";
6579
			}
6580
		}
6581
		return $out;
6582
	}
6583
6584
6585
	/**
6586
	 * Returns the rights used for this class
6587
	 * @return stdClass
6588
	 */
6589
	public function getRights()
6590
	{
6591
		global $user;
6592
6593
		$element = $this->element;
6594
		if ($element == 'facturerec') $element='facture';
6595
6596
		return $user->rights->{$element};
6597
	}
6598
6599
	/**
6600
	 * Function used to replace a thirdparty id with another one.
6601
	 * This function is meant to be called from replaceThirdparty with the appropiate tables
6602
	 * Column name fk_soc MUST be used to identify thirdparties
6603
	 *
6604
	 * @param  DoliDB 	   $db 			  Database handler
6605
	 * @param  int 		   $origin_id     Old thirdparty id (the thirdparty to delete)
6606
	 * @param  int 		   $dest_id       New thirdparty id (the thirdparty that will received element of the other)
6607
	 * @param  string[]    $tables        Tables that need to be changed
6608
	 * @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)
6609
	 * @return bool						  True if success, False if error
6610
	 */
6611
	public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
6612
	{
6613
		foreach ($tables as $table)
6614
		{
6615
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id;
6616
6617
			if (! $db->query($sql))
6618
			{
6619
				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.
6620
				//$this->errors = $db->lasterror();
6621
				return false;
6622
			}
6623
		}
6624
6625
		return true;
6626
	}
6627
6628
	/**
6629
	 * Get buy price to use for margin calculation. This function is called when buy price is unknown.
6630
	 *	 Set buy price = sell price if ForceBuyingPriceIfNull configured,
6631
	 *   else if calculation MARGIN_TYPE = 'costprice' and costprice is defined, use costprice as buyprice
6632
	 *	 else if calculation MARGIN_TYPE = 'pmp' and pmp is calculated, use pmp as buyprice
6633
	 *	 else set min buy price as buy price
6634
	 *
6635
	 * @param float		$unitPrice		 Product unit price
6636
	 * @param float		$discountPercent Line discount percent
6637
	 * @param int		$fk_product		 Product id
6638
	 * @return	float                    <0 if KO, buyprice if OK
6639
	 */
6640
	public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
6641
	{
6642
		global $conf;
6643
6644
		$buyPrice = 0;
6645
6646
		if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
6647
		{
6648
			$buyPrice = $unitPrice * (1 - $discountPercent / 100);
6649
		}
6650
		else
6651
		{
6652
			// Get cost price for margin calculation
6653
			if (! empty($fk_product))
6654
			{
6655
				if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice')
6656
				{
6657
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6658
					$product = new Product($this->db);
6659
					$result = $product->fetch($fk_product);
6660
					if ($result <= 0)
6661
					{
6662
						$this->errors[] = 'ErrorProductIdDoesNotExists';
6663
						return -1;
6664
					}
6665
					if ($product->cost_price > 0)
6666
					{
6667
						$buyPrice = $product->cost_price;
6668
					}
6669
					elseif ($product->pmp > 0)
6670
					{
6671
						$buyPrice = $product->pmp;
6672
					}
6673
				}
6674
				elseif (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')
6675
				{
6676
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6677
					$product = new Product($this->db);
6678
					$result = $product->fetch($fk_product);
6679
					if ($result <= 0)
6680
					{
6681
						$this->errors[] = 'ErrorProductIdDoesNotExists';
6682
						return -1;
6683
					}
6684
					if ($product->pmp > 0)
6685
					{
6686
						$buyPrice = $product->pmp;
6687
					}
6688
				}
6689
6690
				if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1','pmp','costprice')))
6691
				{
6692
					require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6693
					$productFournisseur = new ProductFournisseur($this->db);
6694
					if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0)
6695
					{
6696
						$buyPrice = $productFournisseur->fourn_unitprice;
6697
					}
6698
					elseif ($result < 0)
6699
					{
6700
						$this->errors[] = $productFournisseur->error;
6701
						return -2;
6702
					}
6703
				}
6704
			}
6705
		}
6706
		return $buyPrice;
6707
	}
6708
6709
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6710
	/**
6711
	 *  Show photos of an object (nbmax maximum), into several columns
6712
	 *
6713
	 *  @param		string	$modulepart		'product', 'ticket', ...
6714
	 *  @param      string	$sdir        	Directory to scan (full absolute path)
6715
	 *  @param      int		$size        	0=original size, 1='small' use thumbnail if possible
6716
	 *  @param      int		$nbmax       	Nombre maximum de photos (0=pas de max)
6717
	 *  @param      int		$nbbyrow     	Number of image per line or -1 to use div. Used only if size=1.
6718
	 * 	@param		int		$showfilename	1=Show filename
6719
	 * 	@param		int		$showaction		1=Show icon with action links (resize, delete)
6720
	 * 	@param		int		$maxHeight		Max height of original image when size='small' (so we can use original even if small requested). If 0, always use 'small' thumb image.
6721
	 * 	@param		int		$maxWidth		Max width of original image when size='small'
6722
	 *  @param      int     $nolink         Do not add a href link to view enlarged imaged into a new tab
6723
	 *  @param      int     $notitle        Do not add title tag on image
6724
	 *  @param		int		$usesharelink	Use the public shared link of image (if not available, the 'nophoto' image will be shown instead)
6725
	 *  @return     string					Html code to show photo. Number of photos shown is saved in this->nbphoto
6726
	 */
6727
	public function show_photos($modulepart, $sdir, $size = 0, $nbmax = 0, $nbbyrow = 5, $showfilename = 0, $showaction = 0, $maxHeight = 120, $maxWidth = 160, $nolink = 0, $notitle = 0, $usesharelink = 0)
6728
	{
6729
        // phpcs:enable
6730
		global $conf,$user,$langs;
6731
6732
		include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php';
6733
		include_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';
6734
6735
		$sortfield='position_name';
6736
		$sortorder='asc';
6737
6738
		$dir = $sdir . '/';
6739
		$pdir = '/';
6740
		if ($modulepart == 'ticket')
6741
		{
6742
			$dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6743
			$pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6744
		}
6745
		else
6746
		{
6747
			$dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6748
			$pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6749
		}
6750
6751
		// For backward compatibility
6752
		if ($modulepart == 'product' && ! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))
6753
		{
6754
			$dir = $sdir . '/'. get_exdir($this->id, 2, 0, 0, $this, $modulepart) . $this->id ."/photos/";
6755
			$pdir = '/' . get_exdir($this->id, 2, 0, 0, $this, $modulepart) . $this->id ."/photos/";
6756
		}
6757
6758
		// Defined relative dir to DOL_DATA_ROOT
6759
		$relativedir = '';
6760
		if ($dir)
6761
		{
6762
			$relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
6763
			$relativedir = preg_replace('/^[\\/]/', '', $relativedir);
6764
			$relativedir = preg_replace('/[\\/]$/', '', $relativedir);
6765
		}
6766
6767
		$dirthumb = $dir.'thumbs/';
6768
		$pdirthumb = $pdir.'thumbs/';
6769
6770
		$return ='<!-- Photo -->'."\n";
6771
		$nbphoto=0;
6772
6773
		$filearray=dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC), 1);
0 ignored issues
show
introduced by
The condition strtolower($sortorder) == 'desc' is always false.
Loading history...
6774
6775
		/*if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))    // For backward compatiblity, we scan also old dirs
6776
		 {
6777
		 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
6778
		 $filearray=array_merge($filearray, $filearrayold);
6779
		 }*/
6780
6781
		completeFileArrayWithDatabaseInfo($filearray, $relativedir);
6782
6783
		if (count($filearray))
6784
		{
6785
			if ($sortfield && $sortorder)
6786
			{
6787
				$filearray=dol_sort_array($filearray, $sortfield, $sortorder);
6788
			}
6789
6790
			foreach($filearray as $key => $val)
6791
			{
6792
				$photo='';
6793
				$file = $val['name'];
6794
6795
				//if (! utf8_check($file)) $file=utf8_encode($file);	// To be sure file is stored in UTF8 in memory
6796
6797
				//if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
6798
				if (image_format_supported($file) >= 0)
6799
				{
6800
					$nbphoto++;
6801
					$photo = $file;
6802
					$viewfilename = $file;
6803
6804
					if ($size == 1 || $size == 'small') {   // Format vignette
6805
6806
						// Find name of thumb file
6807
						$photo_vignette=basename(getImageFileNameForSize($dir.$file, '_small'));
6808
						if (! dol_is_file($dirthumb.$photo_vignette)) $photo_vignette='';
6809
6810
						// Get filesize of original file
6811
						$imgarray=dol_getImageSize($dir.$photo);
6812
6813
						if ($nbbyrow > 0)
6814
						{
6815
							if ($nbphoto == 1) $return.= '<table class="valigntop center centpercent" style="border: 0; padding: 2; border-spacing: 2px; border-collapse: separate;">';
6816
6817
							if ($nbphoto % $nbbyrow == 1) $return.= '<tr class="center valignmiddle" style="border: 1px">';
6818
							$return.= '<td style="width: '.ceil(100/$nbbyrow).'%" class="photo">';
6819
						}
6820
						elseif ($nbbyrow < 0) $return .= '<div class="inline-block">';
6821
6822
						$return.= "\n";
6823
6824
						$relativefile=preg_replace('/^\//', '', $pdir.$photo);
6825
						if (empty($nolink))
6826
						{
6827
							$urladvanced=getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
6828
							if ($urladvanced) $return.='<a href="'.$urladvanced.'">';
6829
							else $return.= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank">';
6830
						}
6831
6832
						// Show image (width height=$maxHeight)
6833
						// Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
6834
						$alt=$langs->transnoentitiesnoconv('File').': '.$relativefile;
6835
						$alt.=' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
6836
						if ($notitle) $alt='';
6837
6838
						if ($usesharelink)
6839
						{
6840
							if ($val['share'])
6841
							{
6842
								if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6843
								{
6844
									$return.= '<!-- Show original file (thumb not yet available with shared links) -->';
6845
									$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6846
								}
6847
								else {
6848
									$return.= '<!-- Show original file -->';
6849
									$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6850
								}
6851
							}
6852
							else
6853
							{
6854
								$return.= '<!-- Show nophoto file (because file is not shared) -->';
6855
								$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
6856
							}
6857
						}
6858
						else
6859
						{
6860
							if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6861
							{
6862
								$return.= '<!-- Show thumb -->';
6863
								$return.= '<img class="photo photowithmargin"  height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdirthumb.$photo_vignette).'" title="'.dol_escape_htmltag($alt).'">';
6864
							}
6865
							else {
6866
								$return.= '<!-- Show original file -->';
6867
								$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" title="'.dol_escape_htmltag($alt).'">';
6868
							}
6869
						}
6870
6871
						if (empty($nolink)) $return.= '</a>';
6872
						$return.="\n";
6873
6874
						if ($showfilename) $return.= '<br>'.$viewfilename;
6875
						if ($showaction)
6876
						{
6877
							$return.= '<br>';
6878
							// On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
6879
							if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight))
6880
							{
6881
								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=addthumb&amp;file='.urlencode($pdir.$viewfilename).'">'.img_picto($langs->trans('GenerateThumb'), 'refresh').'&nbsp;&nbsp;</a>';
6882
							}
6883
							// Special cas for product
6884
							if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6885
							{
6886
								// Link to resize
6887
								$return.= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&amp;file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
6888
6889
								// Link to delete
6890
								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6891
								$return.= img_delete().'</a>';
6892
							}
6893
						}
6894
						$return.= "\n";
6895
6896
						if ($nbbyrow > 0)
6897
						{
6898
							$return.= '</td>';
6899
							if (($nbphoto % $nbbyrow) == 0) $return.= '</tr>';
6900
						}
6901
						elseif ($nbbyrow < 0) $return.='</div>';
6902
					}
6903
6904
					if (empty($size)) {     // Format origine
6905
						$return.= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
6906
6907
						if ($showfilename) $return.= '<br>'.$viewfilename;
6908
						if ($showaction)
6909
						{
6910
							// Special case for product
6911
							if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6912
							{
6913
								// Link to resize
6914
								$return.= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&amp;file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
6915
6916
								// Link to delete
6917
								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6918
								$return.= img_delete().'</a>';
6919
							}
6920
						}
6921
					}
6922
6923
					// On continue ou on arrete de boucler ?
6924
					if ($nbmax && $nbphoto >= $nbmax) break;
6925
				}
6926
			}
6927
6928
			if ($size==1 || $size=='small')
6929
			{
6930
				if ($nbbyrow > 0)
6931
				{
6932
					// Ferme tableau
6933
					while ($nbphoto % $nbbyrow)
6934
					{
6935
						$return.= '<td style="width: '.ceil(100/$nbbyrow).'%">&nbsp;</td>';
6936
						$nbphoto++;
6937
					}
6938
6939
					if ($nbphoto) $return.= '</table>';
6940
				}
6941
			}
6942
		}
6943
6944
		$this->nbphoto = $nbphoto;
6945
6946
		return $return;
6947
	}
6948
6949
6950
	/**
6951
	 * Function test if type is array
6952
	 *
6953
	 * @param   array   $info   content informations of field
6954
	 * @return                  bool
6955
	 */
6956
	protected function isArray($info)
6957
	{
6958
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
6959
		{
6960
			if(isset($info['type']) && $info['type']=='array') return true;
6961
			else return false;
6962
		}
6963
		else return false;
6964
	}
6965
6966
	/**
6967
	 * Function test if type is null
6968
	 *
6969
	 * @param   array   $info   content informations of field
6970
	 * @return                  bool
6971
	 */
6972
	protected function isNull($info)
6973
	{
6974
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
6975
		{
6976
			if(isset($info['type']) && $info['type']=='null') return true;
6977
			else return false;
6978
		}
6979
		else return false;
6980
	}
6981
6982
	/**
6983
	 * Function test if type is date
6984
	 *
6985
	 * @param   array   $info   content informations of field
6986
	 * @return                  bool
6987
	 */
6988
	public function isDate($info)
6989
	{
6990
		if(isset($info['type']) && ($info['type']=='date' || $info['type']=='datetime' || $info['type']=='timestamp')) return true;
6991
		else return false;
6992
	}
6993
6994
	/**
6995
	 * Function test if type is integer
6996
	 *
6997
	 * @param   array   $info   content informations of field
6998
	 * @return                  bool
6999
	 */
7000
	public function isInt($info)
7001
	{
7002
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
7003
		{
7004
			if(isset($info['type']) && ($info['type']=='int' || preg_match('/^integer/i', $info['type']) ) ) return true;
7005
			else return false;
7006
		}
7007
		else return false;
7008
	}
7009
7010
	/**
7011
	 * Function test if type is float
7012
	 *
7013
	 * @param   array   $info   content informations of field
7014
	 * @return                  bool
7015
	 */
7016
	public function isFloat($info)
7017
	{
7018
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
7019
		{
7020
			if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) return true;
7021
			else return false;
7022
		}
7023
		else return false;
7024
	}
7025
7026
	/**
7027
	 * Function test if type is text
7028
	 *
7029
	 * @param   array   $info   content informations of field
7030
	 * @return                  bool
7031
	 */
7032
	public function isText($info)
7033
	{
7034
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
7035
		{
7036
			if(isset($info['type']) && $info['type']=='text') return true;
7037
			else return false;
7038
		}
7039
		else return false;
7040
	}
7041
7042
	/**
7043
	 * Function test if is indexed
7044
	 *
7045
	 * @param   array   $info   content informations of field
7046
	 * @return                  bool
7047
	 */
7048
	protected function isIndex($info)
7049
	{
7050
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
7051
		{
7052
			if(isset($info['index']) && $info['index']==true) return true;
7053
			else return false;
7054
		}
7055
		else return false;
7056
	}
7057
7058
	/**
7059
	 * Function to prepare the values to insert.
7060
	 * Note $this->${field} are set by the page that make the createCommon or the updateCommon.
7061
	 *
7062
	 * @return array
7063
	 */
7064
	protected function setSaveQuery()
7065
	{
7066
		global $conf;
7067
7068
		$queryarray=array();
7069
		foreach ($this->fields as $field=>$info)	// Loop on definition of fields
7070
		{
7071
			// Depending on field type ('datetime', ...)
7072
			if($this->isDate($info))
7073
			{
7074
				if(empty($this->{$field}))
7075
				{
7076
					$queryarray[$field] = null;
7077
				}
7078
				else
7079
				{
7080
					$queryarray[$field] = $this->db->idate($this->{$field});
7081
				}
7082
			}
7083
			elseif($this->isArray($info))
7084
			{
7085
				if(! empty($this->{$field})) {
7086
					if(! is_array($this->{$field})) {
7087
						$this->{$field} = array($this->{$field});
7088
					}
7089
					$queryarray[$field] = serialize($this->{$field});
7090
				} else {
7091
					$queryarray[$field] = null;
7092
				}
7093
			}
7094
			elseif($this->isInt($info))
7095
			{
7096
				if ($field == 'entity' && is_null($this->{$field})) $queryarray[$field]=$conf->entity;
7097
				else
7098
				{
7099
					$queryarray[$field] = (int) price2num($this->{$field});
7100
					if (empty($queryarray[$field])) $queryarray[$field]=0;		// May be reset to null later if property 'notnull' is -1 for this field.
7101
				}
7102
			}
7103
			elseif($this->isFloat($info))
7104
			{
7105
				$queryarray[$field] = (double) price2num($this->{$field});
7106
				if (empty($queryarray[$field])) $queryarray[$field]=0;
7107
			}
7108
			else
7109
			{
7110
				$queryarray[$field] = $this->{$field};
7111
			}
7112
7113
			if ($info['type'] == 'timestamp' && empty($queryarray[$field])) unset($queryarray[$field]);
7114
			if (! empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) $queryarray[$field] = null;
7115
		}
7116
7117
		return $queryarray;
7118
	}
7119
7120
	/**
7121
	 * Function to load data from a SQL pointer into properties of current object $this
7122
	 *
7123
	 * @param   stdClass    $obj    Contain data of object from database
7124
     * @return void
7125
	 */
7126
	protected function setVarsFromFetchObj(&$obj)
7127
	{
7128
		foreach ($this->fields as $field => $info)
7129
		{
7130
			if($this->isDate($info))
7131
			{
7132
				if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
7133
				else $this->{$field} = strtotime($obj->{$field});
7134
			}
7135
			elseif($this->isArray($info))
7136
			{
7137
				if(! empty($obj->{$field})) {
7138
					$this->{$field} = @unserialize($obj->{$field});
7139
					// Hack for data not in UTF8
7140
					if($this->{$field } === false) @unserialize(utf8_decode($obj->{$field}));
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unserialize(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

7140
					if($this->{$field } === false) /** @scrutinizer ignore-unhandled */ @unserialize(utf8_decode($obj->{$field}));

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
7141
				} else {
7142
					$this->{$field} = array();
7143
				}
7144
			}
7145
			elseif($this->isInt($info))
7146
			{
7147
				if ($field == 'rowid') $this->id = (int) $obj->{$field};
7148
				else $this->{$field} = (int) $obj->{$field};
7149
			}
7150
			elseif($this->isFloat($info))
7151
			{
7152
				$this->{$field} = (double) $obj->{$field};
7153
			}
7154
			elseif($this->isNull($info))
7155
			{
7156
				$val = $obj->{$field};
7157
				// zero is not null
7158
				$this->{$field} = (is_null($val) || (empty($val) && $val!==0 && $val!=='0') ? null : $val);
7159
			}
7160
			else
7161
			{
7162
				$this->{$field} = $obj->{$field};
7163
			}
7164
		}
7165
7166
		// If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
7167
		if (! isset($this->fields['ref']) && isset($this->id)) $this->ref = $this->id;
7168
	}
7169
7170
	/**
7171
	 * Function to concat keys of fields
7172
	 *
7173
	 * @return string
7174
	 */
7175
	protected function getFieldList()
7176
	{
7177
		$keys = array_keys($this->fields);
7178
		return implode(',', $keys);
7179
	}
7180
7181
	/**
7182
	 * Add quote to field value if necessary
7183
	 *
7184
	 * @param 	string|int	$value			Value to protect
7185
	 * @param	array		$fieldsentry	Properties of field
7186
	 * @return 	string
7187
	 */
7188
	protected function quote($value, $fieldsentry)
7189
	{
7190
		if (is_null($value)) return 'NULL';
1 ignored issue
show
introduced by
The condition is_null($value) is always false.
Loading history...
7191
		elseif (preg_match('/^(int|double|real)/i', $fieldsentry['type'])) return $this->db->escape("$value");
7192
		else return "'".$this->db->escape($value)."'";
7193
	}
7194
7195
7196
	/**
7197
	 * Create object into database
7198
	 *
7199
	 * @param  User $user      User that creates
7200
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
7201
	 * @return int             <0 if KO, Id of created object if OK
7202
	 */
7203
	public function createCommon(User $user, $notrigger = false)
7204
	{
7205
		global $langs;
7206
7207
		$error = 0;
7208
7209
		$now=dol_now();
7210
7211
		$fieldvalues = $this->setSaveQuery();
7212
		if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) $fieldvalues['date_creation']=$this->db->idate($now);
7213
		if (array_key_exists('fk_user_creat', $fieldvalues) && ! ($fieldvalues['fk_user_creat'] > 0)) $fieldvalues['fk_user_creat']=$user->id;
7214
		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
7215
7216
		$keys=array();
7217
		$values = array();
7218
		foreach ($fieldvalues as $k => $v) {
7219
			$keys[$k] = $k;
7220
			$value = $this->fields[$k];
7221
			$values[$k] = $this->quote($v, $value);
7222
		}
7223
7224
		// Clean and check mandatory
7225
		foreach($keys as $key)
7226
		{
7227
			// If field is an implicit foreign key field
7228
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';
7229
			if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';
7230
7231
			//var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7232
			if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && ! isset($values[$key]) && is_null($val['default']))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $val does not exist. Did you maybe mean $value?
Loading history...
7233
			{
7234
				$error++;
7235
				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7236
			}
7237
7238
			// If field is an implicit foreign key field
7239
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) $values[$key]='null';
7240
			if (! empty($this->fields[$key]['foreignkey']) && empty($values[$key])) $values[$key]='null';
7241
		}
7242
7243
		if ($error) return -1;
7244
7245
		$this->db->begin();
7246
7247
		if (! $error)
7248
		{
7249
			$sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
7250
			$sql.= ' ('.implode(", ", $keys).')';
7251
			$sql.= ' VALUES ('.implode(", ", $values).')';
7252
7253
			$res = $this->db->query($sql);
7254
			if ($res===false) {
0 ignored issues
show
introduced by
The condition $res === false is always false.
Loading history...
7255
				$error++;
7256
				$this->errors[] = $this->db->lasterror();
7257
			}
7258
		}
7259
7260
		if (! $error)
7261
		{
7262
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
7263
		}
7264
7265
		// If we have a field ref with a default value of (PROV)
7266
		if (! $error)
7267
		{
7268
		    if (key_exists('ref', $this->fields) && $this->fields['ref']['notnull'] > 0 && ! is_null($this->fields['ref']['default']) && $this->fields['ref']['default'] == '(PROV)')
7269
		    {
7270
		        $sql="UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ref = '(PROV".$this->id.")' WHERE ref = '(PROV)' AND rowid = ".$this->id;
7271
		        $resqlupdate = $this->db->query($sql);
7272
		        if ($resqlupdate===false)
0 ignored issues
show
introduced by
The condition $resqlupdate === false is always false.
Loading history...
7273
		        {
7274
		            $error++;
7275
		            $this->errors[] = $this->db->lasterror();
7276
		        }
7277
		    }
7278
		}
7279
7280
		// Create extrafields
7281
		if (! $error)
7282
		{
7283
			$result=$this->insertExtraFields();
7284
			if ($result < 0) $error++;
7285
		}
7286
7287
		// Create lines
7288
		if (! empty($this->table_element_line) && ! empty($this->fk_element))
7289
		{
7290
			$num=(is_array($this->lines) ? count($this->lines) : 0);
1 ignored issue
show
introduced by
The condition is_array($this->lines) is always true.
Loading history...
7291
			for ($i = 0; $i < $num; $i++)
7292
			{
7293
				$line = $this->lines[$i];
7294
7295
				$keyforparent = $this->fk_element;
7296
				$line->$keyforparent = $this->id;
7297
7298
				// Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
7299
				//if (! is_object($line)) $line=json_decode(json_encode($line), false);  // convert recursively array into object.
7300
				if (! is_object($line)) $line = (object) $line;
7301
7302
				$result = $line->create($user, 1);
7303
				if ($result < 0)
7304
				{
7305
					$this->error=$this->db->lasterror();
7306
					$this->db->rollback();
7307
					return -1;
7308
				}
7309
			}
7310
		}
7311
7312
		// Triggers
7313
		if (! $error && ! $notrigger)
7314
		{
7315
			// Call triggers
7316
			$result=$this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
7317
			if ($result < 0) { $error++; }
7318
			// End call triggers
7319
		}
7320
7321
		// Commit or rollback
7322
		if ($error) {
7323
			$this->db->rollback();
7324
			return -1;
7325
		} else {
7326
			$this->db->commit();
7327
			return $this->id;
7328
		}
7329
	}
7330
7331
7332
	/**
7333
	 * Load object in memory from the database
7334
	 *
7335
	 * @param	int    $id				Id object
7336
	 * @param	string $ref				Ref
7337
	 * @param	string	$morewhere		More SQL filters (' AND ...')
7338
	 * @return 	int         			<0 if KO, 0 if not found, >0 if OK
7339
	 */
7340
	public function fetchCommon($id, $ref = null, $morewhere = '')
7341
	{
7342
		if (empty($id) && empty($ref) && empty($morewhere)) return -1;
7343
7344
		$sql = 'SELECT '.$this->getFieldList();
7345
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
7346
7347
		if (!empty($id))  $sql.= ' WHERE rowid = '.$id;
7348
		elseif (!empty($ref)) $sql.= " WHERE ref = ".$this->quote($ref, $this->fields['ref']);
7349
		else $sql.=' WHERE 1 = 1';	// usage with empty id and empty ref is very rare
7350
		if ($morewhere)   $sql.= $morewhere;
7351
		$sql.=' LIMIT 1';	// This is a fetch, to be sure to get only one record
7352
7353
		$res = $this->db->query($sql);
7354
		if ($res)
7355
		{
7356
			$obj = $this->db->fetch_object($res);
7357
			if ($obj)
7358
			{
7359
				$this->setVarsFromFetchObj($obj);
7360
				return $this->id;
7361
			}
7362
			else
7363
			{
7364
				return 0;
7365
			}
7366
		}
7367
		else
7368
		{
7369
			$this->error = $this->db->lasterror();
7370
			$this->errors[] = $this->error;
7371
			return -1;
7372
		}
7373
	}
7374
7375
	/**
7376
	 * Load object in memory from the database
7377
	 *
7378
	 * @param	string	$morewhere		More SQL filters (' AND ...')
7379
	 * @return 	int         			<0 if KO, 0 if not found, >0 if OK
7380
	 */
7381
	public function fetchLinesCommon($morewhere = '')
7382
	{
7383
		$objectlineclassname = get_class($this).'Line';
7384
		if (! class_exists($objectlineclassname))
7385
		{
7386
			$this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
7387
			return -1;
7388
		}
7389
7390
		$objectline = new $objectlineclassname($this->db);
7391
7392
		$sql = 'SELECT '.$objectline->getFieldList();
7393
		$sql.= ' FROM '.MAIN_DB_PREFIX.$objectline->table_element;
7394
		$sql.=' WHERE fk_'.$this->element.' = '.$this->id;
7395
		if ($morewhere)   $sql.= $morewhere;
7396
7397
		$resql = $this->db->query($sql);
7398
		if ($resql)
7399
		{
7400
			$num_rows = $this->db->num_rows($resql);
7401
			$i = 0;
7402
			while ($i < $num_rows)
7403
			{
7404
				$obj = $this->db->fetch_object($resql);
7405
				if ($obj)
7406
				{
7407
					$newline = new $objectlineclassname($this->db);
7408
					$newline->setVarsFromFetchObj($obj);
7409
7410
					$this->lines[] = $newline;
7411
				}
7412
				$i++;
7413
			}
7414
7415
			return 1;
7416
		}
7417
		else
7418
		{
7419
			$this->error = $this->db->lasterror();
7420
			$this->errors[] = $this->error;
7421
			return -1;
7422
		}
7423
	}
7424
7425
	/**
7426
	 * Update object into database
7427
	 *
7428
	 * @param  User $user      	User that modifies
7429
	 * @param  bool $notrigger 	false=launch triggers after, true=disable triggers
7430
	 * @return int             	<0 if KO, >0 if OK
7431
	 */
7432
	public function updateCommon(User $user, $notrigger = false)
7433
	{
7434
		global $conf, $langs;
7435
7436
		$error = 0;
7437
7438
		$now=dol_now();
7439
7440
		$fieldvalues = $this->setSaveQuery();
7441
		if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) $fieldvalues['date_modification']=$this->db->idate($now);
7442
		if (array_key_exists('fk_user_modif', $fieldvalues) && ! ($fieldvalues['fk_user_modif'] > 0)) $fieldvalues['fk_user_modif']=$user->id;
7443
		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
7444
7445
		$keys=array();
7446
		$values = array();
7447
		foreach ($fieldvalues as $k => $v) {
7448
			$keys[$k] = $k;
7449
			$value = $this->fields[$k];
7450
			$values[$k] = $this->quote($v, $value);
7451
			$tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
7452
		}
7453
7454
		// Clean and check mandatory
7455
		foreach($keys as $key)
7456
		{
7457
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';		// This is an implicit foreign key field
7458
			if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';					// This is an explicit foreign key field
7459
7460
			//var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7461
			/*
7462
			if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
7463
			{
7464
				$error++;
7465
				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7466
			}*/
7467
		}
7468
7469
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET '.implode(',', $tmp).' WHERE rowid='.$this->id ;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tmp seems to be defined by a foreach iteration on line 7447. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
7470
7471
		$this->db->begin();
7472
		if (! $error)
7473
		{
7474
			$res = $this->db->query($sql);
7475
			if ($res===false)
0 ignored issues
show
introduced by
The condition $res === false is always false.
Loading history...
7476
			{
7477
				$error++;
7478
				$this->errors[] = $this->db->lasterror();
7479
			}
7480
		}
7481
7482
		// Update extrafield
7483
		if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
7484
		{
7485
			$result=$this->insertExtraFields();
7486
			if ($result < 0)
7487
			{
7488
				$error++;
7489
			}
7490
		}
7491
7492
		// Triggers
7493
		if (! $error && ! $notrigger)
7494
		{
7495
			// Call triggers
7496
			$result=$this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
7497
			if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail
7498
			// End call triggers
7499
		}
7500
7501
		// Commit or rollback
7502
		if ($error) {
7503
			$this->db->rollback();
7504
			return -1;
7505
		} else {
7506
			$this->db->commit();
7507
			return $this->id;
7508
		}
7509
	}
7510
7511
	/**
7512
	 * Delete object in database
7513
	 *
7514
	 * @param 	User 	$user       			User that deletes
7515
	 * @param 	bool 	$notrigger  			false=launch triggers after, true=disable triggers
7516
	 * @param	int		$forcechilddeletion		0=no, 1=Force deletion of children
7517
	 * @return 	int             				<=0 if KO, >0 if OK
7518
	 */
7519
	public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0)
7520
	{
7521
		$error=0;
7522
7523
		$this->db->begin();
7524
7525
		if ($forcechilddeletion)	// Force also delete of childtables that should lock deletion in standard case when option force is off
7526
		{
7527
			foreach($this->childtables as $table)
7528
			{
7529
				$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$table.' WHERE '.$this->fk_element.' = '.$this->id;
7530
				$resql = $this->db->query($sql);
7531
				if (! $resql)
7532
				{
7533
					$this->error=$this->db->lasterror();
7534
					$this->errors[]=$this->error;
7535
					$this->db->rollback();
7536
					return -1;
7537
				}
7538
			}
7539
		}
7540
		elseif (! empty($this->fk_element) && ! empty($this->childtables))	// If object has childs linked with a foreign key field, we check all child tables.
7541
		{
7542
			$objectisused = $this->isObjectUsed($this->id);
7543
			if (! empty($objectisused))
7544
			{
7545
				dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
7546
				$this->error='ErrorRecordHasChildren';
7547
				$this->errors[]=$this->error;
7548
				$this->db->rollback();
7549
				return 0;
7550
			}
7551
		}
7552
7553
		// Delete cascade first
7554
		foreach($this->childtablesoncascade as $table)
7555
		{
7556
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$table.' WHERE '.$this->fk_element.' = '.$this->id;
7557
			$resql = $this->db->query($sql);
7558
			if (! $resql)
7559
			{
7560
				$this->error=$this->db->lasterror();
7561
				$this->errors[]=$this->error;
7562
				$this->db->rollback();
7563
				return -1;
7564
			}
7565
		}
7566
7567
		if (! $error) {
7568
			if (! $notrigger) {
7569
				// Call triggers
7570
				$result=$this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
7571
				if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail
7572
				// End call triggers
7573
			}
7574
		}
7575
7576
		if (! $error && ! empty($this->isextrafieldmanaged))
7577
		{
7578
			$sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element."_extrafields";
7579
			$sql.= " WHERE fk_object=" . $this->id;
7580
7581
			$resql = $this->db->query($sql);
7582
			if (! $resql)
7583
			{
7584
				$this->errors[] = $this->db->lasterror();
7585
				$error++;
7586
			}
7587
		}
7588
7589
		if (! $error)
7590
		{
7591
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
7592
7593
			$res = $this->db->query($sql);
7594
			if($res===false) {
0 ignored issues
show
introduced by
The condition $res === false is always false.
Loading history...
7595
				$error++;
7596
				$this->errors[] = $this->db->lasterror();
7597
			}
7598
		}
7599
7600
		// Commit or rollback
7601
		if ($error) {
7602
			$this->db->rollback();
7603
			return -1;
7604
		} else {
7605
			$this->db->commit();
7606
			return 1;
7607
		}
7608
	}
7609
7610
	/**
7611
	 *  Delete a line of object in database
7612
	 *
7613
	 *	@param  User	$user       User that delete
7614
	 *  @param	int		$idline		Id of line to delete
7615
	 *  @param 	bool 	$notrigger  false=launch triggers after, true=disable triggers
7616
	 *  @return int         		>0 if OK, <0 if KO
7617
	 */
7618
	public function deleteLineCommon(User $user, $idline, $notrigger = false)
7619
	{
7620
		global $conf;
7621
7622
		$error=0;
7623
7624
		$tmpforobjectclass = get_class($this);
7625
		$tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
7626
7627
		// Call trigger
7628
		$result=$this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
7629
		if ($result < 0) return -1;
7630
		// End call triggers
7631
7632
		$this->db->begin();
7633
7634
		$sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element_line;
7635
		$sql.= " WHERE rowid=".$idline;
7636
7637
		dol_syslog(get_class($this)."::deleteLineCommon", LOG_DEBUG);
7638
		$resql = $this->db->query($sql);
7639
		if (! $resql)
7640
		{
7641
			$this->error="Error ".$this->db->lasterror();
7642
			$error++;
7643
		}
7644
7645
		if (empty($error)) {
7646
			// Remove extrafields
7647
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
7648
			{
7649
				$tmpobjectline = new $tmpforobjectlineclass($this->db);
7650
				$tmpobjectline->id= $idline;
7651
				$result=$tmpobjectline->deleteExtraFields();
7652
				if ($result < 0)
7653
				{
7654
					$error++;
7655
					$this->error="Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
7656
				}
7657
			}
7658
		}
7659
7660
		if (empty($error)) {
7661
			$this->db->commit();
7662
			return 1;
7663
		} else {
7664
			dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
7665
			$this->db->rollback();
7666
			return -1;
7667
		}
7668
	}
7669
7670
	/**
7671
	 * Initialise object with example values
7672
	 * Id must be 0 if object instance is a specimen
7673
	 *
7674
	 * @return void
7675
	 */
7676
	public function initAsSpecimenCommon()
7677
	{
7678
	    global $user;
7679
7680
		$this->id = 0;
7681
		if (array_key_exists('label', $this->fields)) $this->label='This is label';
7682
		if (array_key_exists('note_public', $this->fields)) $this->note_public='Public note';
7683
		if (array_key_exists('note_private', $this->fields)) $this->note_private='Private note';
7684
		if (array_key_exists('date_creation', $this->fields)) $this->date_creation=(dol_now()-3600*24);
7685
		if (array_key_exists('date_modification', $this->fields)) $this->date_modification=(dol_now()-3600*24);
7686
		if (array_key_exists('fk_user_creat', $this->fields)) $this->fk_user_creat=$user->id;
7687
		if (array_key_exists('fk_user_modif', $this->fields)) $this->fk_user_modif=$user->id;
7688
		if (array_key_exists('date', $this->fields)) $this->date=dol_now();
7689
		// ...
7690
	}
7691
7692
7693
	/* Part for comments */
7694
7695
	/**
7696
	 * Load comments linked with current task
7697
	 *	@return boolean	1 if ok
7698
	 */
7699
	public function fetchComments()
7700
	{
7701
		require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
7702
7703
		$comment = new Comment($this->db);
7704
		$result=$comment->fetchAllFor($this->element, $this->id);
7705
		if ($result<0) {
7706
			$this->errors=array_merge($this->errors, $comment->errors);
7707
			return -1;
7708
		} else {
7709
			$this->comments = $comment->comments;
7710
		}
7711
		return count($this->comments);
7712
	}
7713
7714
    /**
7715
     * Return nb comments already posted
7716
     *
7717
     * @return int
7718
     */
7719
    public function getNbComments()
7720
    {
7721
        return count($this->comments);
7722
    }
7723
7724
    /**
7725
     * Trim object parameters
7726
     * @param string[] $parameters array of parameters to trim
7727
     *
7728
     * @return void
7729
     */
7730
    public function trimParameters($parameters)
7731
    {
7732
        if (!is_array($parameters)) return;
1 ignored issue
show
introduced by
The condition is_array($parameters) is always true.
Loading history...
7733
        foreach ($parameters as $parameter) {
7734
            if (isset($this->$parameter)) {
7735
                $this->$parameter = trim($this->$parameter);
7736
            }
7737
        }
7738
    }
7739
}
7740