Passed
Push — master ( 3cffbe...0f9140 )
by Alxarafe
23:50
created

Base/AlCommonObject.php (90 issues)

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