Passed
Push — master ( 284492...2c3033 )
by Alxarafe
27:10
created

dolibarr/htdocs/core/class/commonobject.class.php (5 issues)

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