Passed
Branch develop (e0c6c9)
by
unknown
30:45
created

CommonObject::swapContactStatus()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

Loading history...
1442
	}
1443
1444
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1445
	/**
1446
	 *	Load data for barcode into properties ->barcode_type*
1447
	 *	Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
1448
	 *  if it is not defined, ->element must be defined to know default barcode type.
1449
	 *
1450
	 *	@return		int			<0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
1451
	 */
1452
	public function fetch_barcode()
1453
	{
1454
        // phpcs:enable
1455
		global $conf;
1456
1457
		dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1458
1459
		$idtype=$this->barcode_type;
1460
		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
1461
		{
1462
			if ($this->element == 'product')      $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1463
			elseif ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1464
			else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1465
		}
1466
1467
		if ($idtype > 0)
1468
		{
1469
			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
1470
			{
1471
				$sql = "SELECT rowid, code, libelle as label, coder";
1472
				$sql.= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1473
				$sql.= " WHERE rowid = ".$idtype;
1474
				dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1475
				$resql = $this->db->query($sql);
1476
				if ($resql)
1477
				{
1478
					$obj = $this->db->fetch_object($resql);
1479
					$this->barcode_type       = $obj->rowid;
1480
					$this->barcode_type_code  = $obj->code;
1481
					$this->barcode_type_label = $obj->label;
1482
					$this->barcode_type_coder = $obj->coder;
1483
					return 1;
1484
				}
1485
				else
1486
				{
1487
					dol_print_error($this->db);
1488
					return -1;
1489
				}
1490
			}
1491
		}
1492
		return 0;
1493
	}
1494
1495
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1496
	/**
1497
	 *		Load the project with id $this->fk_project into this->project
1498
	 *
1499
	 *		@return		int			<0 if KO, >=0 if OK
1500
	 */
1501
	public function fetch_projet()
1502
	{
1503
        // phpcs:enable
1504
		include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1505
1506
		if (empty($this->fk_project) && ! empty($this->fk_projet)) $this->fk_project = $this->fk_projet;	// For backward compatibility
1507
		if (empty($this->fk_project)) return 0;
1508
1509
		$project = new Project($this->db);
1510
		$result = $project->fetch($this->fk_project);
1511
1512
		$this->projet = $project;	// deprecated
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$projet has been deprecated. ( Ignorable by Annotation )

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

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

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

2034
								$this->/** @scrutinizer ignore-call */ 
2035
               updateline(

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

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

Loading history...
2035
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
2036
									($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
2037
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start,
2038
									$line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2039
								);
2040
								break;
2041
							case 'commande':
2042
								$this->updateline(
2043
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
2044
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end,
2045
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
2046
									$line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2047
								);
2048
								break;
2049
							case 'facture':
2050
								$this->updateline(
2051
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
2052
									$line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,
2053
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
2054
									$line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice
2055
								);
2056
								break;
2057
							case 'supplier_proposal':
2058
								$this->updateline(
2059
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
2060
									($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
2061
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options,
2062
									$line->ref_fourn, $line->multicurrency_subprice
2063
								);
2064
								break;
2065
							case 'order_supplier':
2066
								$this->updateline(
2067
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
2068
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false,
2069
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2070
								);
2071
								break;
2072
							case 'invoice_supplier':
2073
								$this->updateline(
2074
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx,
2075
									$line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false,
2076
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2077
								);
2078
								break;
2079
							default:
2080
								dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2081
								break;
2082
						}
2083
					}
2084
				}
2085
2086
				return 1;
2087
			}
2088
			else
2089
			{
2090
				dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
2091
				$this->error=$this->db->error();
2092
				return -1;
2093
			}
2094
		}
2095
		else
2096
		{
2097
			dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2098
			$this->error='Status of the object is incompatible '.$this->statut;
2099
			return -2;
2100
		}
2101
	}
2102
2103
	/**
2104
	 *  Change the payments terms
2105
	 *
2106
	 *  @param		int		$id		Id of new payment terms
2107
	 *  @return		int				>0 if OK, <0 if KO
2108
	 */
2109
	public function setPaymentTerms($id)
2110
	{
2111
		dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
2112
		if ($this->statut >= 0 || $this->element == 'societe')
2113
		{
2114
			// TODO uniformize field name
2115
			$fieldname = 'fk_cond_reglement';
2116
			if ($this->element == 'societe') $fieldname = 'cond_reglement';
2117
			if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
2118
2119
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2120
			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
2121
			$sql .= ' WHERE rowid='.$this->id;
2122
2123
			if ($this->db->query($sql))
2124
			{
2125
				$this->cond_reglement_id = $id;
2126
				// for supplier
2127
				if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
2128
				$this->cond_reglement = $id;	// for compatibility
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$cond_reglement has been deprecated: Kept for compatibility ( Ignorable by Annotation )

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

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

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

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

Loading history...
2129
				return 1;
2130
			}
2131
			else
2132
			{
2133
				dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
2134
				$this->error=$this->db->error();
2135
				return -1;
2136
			}
2137
		}
2138
		else
2139
		{
2140
			dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2141
			$this->error='Status of the object is incompatible '.$this->statut;
2142
			return -2;
2143
		}
2144
	}
2145
2146
2147
	/**
2148
	 *  Change the retained warranty payments terms
2149
	 *
2150
	 *  @param		int		$id		Id of new payment terms
2151
	 *  @return		int				>0 if OK, <0 if KO
2152
	 */
2153
	public function setRetainedWarrantyPaymentTerms($id)
2154
	{
2155
	    dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
2156
	    if ($this->statut >= 0 || $this->element == 'societe')
2157
	    {
2158
	        $fieldname = 'retained_warranty_fk_cond_reglement';
2159
2160
	        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2161
	        $sql .= ' SET '.$fieldname.' = '.$id;
2162
	        $sql .= ' WHERE rowid='.$this->id;
2163
2164
	        if ($this->db->query($sql))
2165
	        {
2166
	            $this->retained_warranty_fk_cond_reglement = $id;
2167
	            return 1;
2168
	        }
2169
	        else
2170
	        {
2171
	            dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Erreur '.$sql.' - '.$this->db->error());
2172
	            $this->error=$this->db->error();
2173
	            return -1;
2174
	        }
2175
	    }
2176
	    else
2177
	    {
2178
	        dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
2179
	        $this->error='Status of the object is incompatible '.$this->statut;
2180
	        return -2;
2181
	    }
2182
	}
2183
2184
	/**
2185
	 *	Define delivery address
2186
	 *  @deprecated
2187
	 *
2188
	 *	@param      int		$id		Address id
2189
	 *	@return     int				<0 si ko, >0 si ok
2190
	 */
2191
	public function setDeliveryAddress($id)
2192
	{
2193
		$fieldname = 'fk_delivery_address';
2194
		if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
2195
2196
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
2197
		$sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
2198
2199
		if ($this->db->query($sql))
2200
		{
2201
			$this->fk_delivery_address = $id;
1 ignored issue
show
Deprecated Code introduced by
The property CommonObject::$fk_delivery_address has been deprecated. ( Ignorable by Annotation )

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

2201
			/** @scrutinizer ignore-deprecated */ $this->fk_delivery_address = $id;
Loading history...
2202
			return 1;
2203
		}
2204
		else
2205
		{
2206
			$this->error=$this->db->error();
2207
			dol_syslog(get_class($this).'::setDeliveryAddress Erreur '.$sql.' - '.$this->error);
2208
			return -1;
2209
		}
2210
	}
2211
2212
2213
	/**
2214
	 *  Change the shipping method
2215
	 *
2216
	 *  @param      int     $shipping_method_id     Id of shipping method
2217
	 *  @param      bool    $notrigger              false=launch triggers after, true=disable triggers
2218
	 *  @param      User	$userused               Object user
2219
	 *
2220
	 *  @return     int              1 if OK, 0 if KO
2221
	 */
2222
	public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null)
2223
	{
2224
		global $user;
2225
2226
		if (empty($userused)) $userused=$user;
2227
2228
		$error = 0;
2229
2230
		if (! $this->table_element) {
2231
			dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR);
2232
			return -1;
2233
		}
2234
2235
		$this->db->begin();
2236
2237
		if ($shipping_method_id<0) $shipping_method_id='NULL';
2238
		dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2239
2240
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2241
		$sql.= " SET fk_shipping_method = ".$shipping_method_id;
2242
		$sql.= " WHERE rowid=".$this->id;
2243
		$resql = $this->db->query($sql);
2244
		if (! $resql) {
2245
			dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2246
			$this->error = $this->db->lasterror();
2247
			$error++;
2248
		} else {
2249
			if (!$notrigger)
2250
			{
2251
				// Call trigger
2252
				$this->context=array('shippingmethodupdate'=>1);
2253
				$result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2254
				if ($result < 0) $error++;
2255
				// End call trigger
2256
			}
2257
		}
2258
		if ($error)
2259
		{
2260
			$this->db->rollback();
2261
			return -1;
2262
		} else {
2263
			$this->shipping_method_id = ($shipping_method_id=='NULL')?null:$shipping_method_id;
2264
			$this->db->commit();
2265
			return 1;
2266
		}
2267
	}
2268
2269
2270
	/**
2271
	 *  Change the warehouse
2272
	 *
2273
	 *  @param      int     $warehouse_id     Id of warehouse
2274
	 *  @return     int              1 if OK, 0 if KO
2275
	 */
2276
	public function setWarehouse($warehouse_id)
2277
	{
2278
		if (! $this->table_element) {
2279
			dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR);
2280
			return -1;
2281
		}
2282
		if ($warehouse_id<0) $warehouse_id='NULL';
2283
		dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2284
2285
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2286
		$sql.= " SET fk_warehouse = ".$warehouse_id;
2287
		$sql.= " WHERE rowid=".$this->id;
2288
2289
		if ($this->db->query($sql)) {
2290
			$this->warehouse_id = ($warehouse_id=='NULL')?null:$warehouse_id;
2291
			return 1;
2292
		} else {
2293
			dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2294
			$this->error=$this->db->error();
2295
			return 0;
2296
		}
2297
	}
2298
2299
2300
	/**
2301
	 *		Set last model used by doc generator
2302
	 *
2303
	 *		@param		User	$user		User object that make change
2304
	 *		@param		string	$modelpdf	Modele name
2305
	 *		@return		int					<0 if KO, >0 if OK
2306
	 */
2307
	public function setDocModel($user, $modelpdf)
2308
	{
2309
		if (! $this->table_element)
2310
		{
2311
			dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR);
2312
			return -1;
2313
		}
2314
2315
		$newmodelpdf=dol_trunc($modelpdf, 255);
2316
2317
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2318
		$sql.= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2319
		$sql.= " WHERE rowid = ".$this->id;
2320
		// if ($this->element == 'facture') $sql.= " AND fk_statut < 2";
2321
		// if ($this->element == 'propal')  $sql.= " AND fk_statut = 0";
2322
2323
		dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2324
		$resql=$this->db->query($sql);
2325
		if ($resql)
2326
		{
2327
			$this->modelpdf=$modelpdf;
2328
			return 1;
2329
		}
2330
		else
2331
		{
2332
			dol_print_error($this->db);
2333
			return 0;
2334
		}
2335
	}
2336
2337
2338
	/**
2339
	 *  Change the bank account
2340
	 *
2341
	 *  @param		int		$fk_account		Id of bank account
2342
	 *  @param      bool    $notrigger      false=launch triggers after, true=disable triggers
2343
	 *  @param      User	$userused		Object user
2344
	 *  @return		int				1 if OK, 0 if KO
2345
	 */
2346
	public function setBankAccount($fk_account, $notrigger = false, $userused = null)
2347
	{
2348
		global $user;
2349
2350
		if (empty($userused)) $userused=$user;
2351
2352
		$error = 0;
2353
2354
		if (! $this->table_element) {
2355
			dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR);
2356
			return -1;
2357
		}
2358
		$this->db->begin();
2359
2360
		if ($fk_account<0) $fk_account='NULL';
2361
		dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2362
2363
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2364
		$sql.= " SET fk_account = ".$fk_account;
2365
		$sql.= " WHERE rowid=".$this->id;
2366
2367
		$resql = $this->db->query($sql);
2368
		if (! $resql)
2369
		{
2370
			dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2371
			$this->error = $this->db->lasterror();
2372
			$error++;
2373
		}
2374
		else
2375
		{
2376
			if (!$notrigger)
2377
			{
2378
				// Call trigger
2379
				$this->context=array('bankaccountupdate'=>1);
2380
				$result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2381
				if ($result < 0) $error++;
2382
				// End call trigger
2383
			}
2384
		}
2385
		if ($error)
2386
		{
2387
			$this->db->rollback();
2388
			return -1;
2389
		}
2390
		else
2391
		{
2392
			$this->fk_account = ($fk_account=='NULL')?null:$fk_account;
2393
			$this->db->commit();
2394
			return 1;
2395
		}
2396
	}
2397
2398
2399
	// TODO: Move line related operations to CommonObjectLine?
2400
2401
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2402
	/**
2403
	 *  Save a new position (field rang) for details lines.
2404
	 *  You can choose to set position for lines with already a position or lines without any position defined.
2405
	 *
2406
	 * 	@param		boolean		$renum			   True to renum all already ordered lines, false to renum only not already ordered lines.
2407
	 * 	@param		string		$rowidorder		   ASC or DESC
2408
	 * 	@param		boolean		$fk_parent_line    Table with fk_parent_line field or not
2409
	 * 	@return		int                            <0 if KO, >0 if OK
2410
	 */
2411
	public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
2412
	{
2413
        // phpcs:enable
2414
		if (! $this->table_element_line)
2415
		{
2416
			dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR);
2417
			return -1;
2418
		}
2419
		if (! $this->fk_element)
2420
		{
2421
			dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR);
2422
			return -1;
2423
		}
2424
2425
		// Count number of lines to reorder (according to choice $renum)
2426
		$nl=0;
2427
		$sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2428
		$sql.= ' WHERE '.$this->fk_element.'='.$this->id;
2429
		if (! $renum) $sql.= ' AND rang = 0';
2430
		if ($renum) $sql.= ' AND rang <> 0';
2431
2432
		dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
2433
		$resql = $this->db->query($sql);
2434
		if ($resql)
2435
		{
2436
			$row = $this->db->fetch_row($resql);
2437
			$nl = $row[0];
2438
		}
2439
		else dol_print_error($this->db);
2440
		if ($nl > 0)
2441
		{
2442
			// The goal of this part is to reorder all lines, with all children lines sharing the same
2443
			// counter that parents.
2444
			$rows=array();
2445
2446
			// We first search all lines that are parent lines (for multilevel details lines)
2447
			$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2448
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2449
			if ($fk_parent_line) $sql.= ' AND fk_parent_line IS NULL';
2450
			$sql.= ' ORDER BY rang ASC, rowid '.$rowidorder;
2451
2452
			dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
2453
			$resql = $this->db->query($sql);
2454
			if ($resql)
2455
			{
2456
				$i=0;
2457
				$num = $this->db->num_rows($resql);
2458
				while ($i < $num)
2459
				{
2460
					$row = $this->db->fetch_row($resql);
2461
					$rows[] = $row[0];	// Add parent line into array rows
2462
					$childrens = $this->getChildrenOfLine($row[0]);
2463
					if (! empty($childrens))
2464
					{
2465
						foreach($childrens as $child)
2466
						{
2467
							array_push($rows, $child);
2468
						}
2469
					}
2470
					$i++;
2471
				}
2472
2473
				// Now we set a new number for each lines (parent and children with children included into parent tree)
2474
				if (! empty($rows))
2475
				{
2476
					foreach($rows as $key => $row)
2477
					{
2478
						$this->updateRangOfLine($row, ($key+1));
2479
					}
2480
				}
2481
			}
2482
			else
2483
			{
2484
				dol_print_error($this->db);
2485
			}
2486
		}
2487
		return 1;
2488
	}
2489
2490
	/**
2491
	 * 	Get children of line
2492
	 *
2493
	 * 	@param	int		$id		Id of parent line
2494
	 * 	@return	array			Array with list of children lines id
2495
	 */
2496
	public function getChildrenOfLine($id)
2497
	{
2498
		$rows=array();
2499
2500
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2501
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2502
		$sql.= ' AND fk_parent_line = '.$id;
2503
		$sql.= ' ORDER BY rang ASC';
2504
2505
		dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG);
2506
		$resql = $this->db->query($sql);
2507
		if ($resql)
2508
		{
2509
			$i=0;
2510
			$num = $this->db->num_rows($resql);
2511
			while ($i < $num)
2512
			{
2513
				$row = $this->db->fetch_row($resql);
2514
				$rows[$i] = $row[0];
2515
				$i++;
2516
			}
2517
		}
2518
2519
		return $rows;
2520
	}
2521
2522
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2523
	/**
2524
	 * 	Update a line to have a lower rank
2525
	 *
2526
	 * 	@param 	int			$rowid				Id of line
2527
	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2528
	 * 	@return	void
2529
	 */
2530
	public function line_up($rowid, $fk_parent_line = true)
2531
	{
2532
        // phpcs:enable
2533
		$this->line_order(false, 'ASC', $fk_parent_line);
2534
2535
		// Get rang of line
2536
		$rang = $this->getRangOfLine($rowid);
2537
2538
		// Update position of line
2539
		$this->updateLineUp($rowid, $rang);
2540
	}
2541
2542
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2543
	/**
2544
	 * 	Update a line to have a higher rank
2545
	 *
2546
	 * 	@param	int			$rowid				Id of line
2547
	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2548
	 * 	@return	void
2549
	 */
2550
	public function line_down($rowid, $fk_parent_line = true)
2551
	{
2552
        // phpcs:enable
2553
		$this->line_order(false, 'ASC', $fk_parent_line);
2554
2555
		// Get rang of line
2556
		$rang = $this->getRangOfLine($rowid);
2557
2558
		// Get max value for rang
2559
		$max = $this->line_max();
2560
2561
		// Update position of line
2562
		$this->updateLineDown($rowid, $rang, $max);
2563
	}
2564
2565
	/**
2566
	 * 	Update position of line (rang)
2567
	 *
2568
	 * 	@param	int		$rowid		Id of line
2569
	 * 	@param	int		$rang		Position
2570
	 * 	@return	void
2571
	 */
2572
	public function updateRangOfLine($rowid, $rang)
2573
	{
2574
		$fieldposition = 'rang';	// @TODO Rename 'rang' into 'position'
2575
		if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2576
2577
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2578
		$sql.= ' WHERE rowid = '.$rowid;
2579
2580
		dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
2581
		if (! $this->db->query($sql))
2582
		{
2583
			dol_print_error($this->db);
2584
		}
2585
	}
2586
2587
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2588
	/**
2589
	 * 	Update position of line with ajax (rang)
2590
	 *
2591
	 * 	@param	array	$rows	Array of rows
2592
	 * 	@return	void
2593
	 */
2594
	public function line_ajaxorder($rows)
2595
	{
2596
        // phpcs:enable
2597
		$num = count($rows);
2598
		for ($i = 0 ; $i < $num ; $i++)
2599
		{
2600
			$this->updateRangOfLine($rows[$i], ($i+1));
2601
		}
2602
	}
2603
2604
	/**
2605
	 * 	Update position of line up (rang)
2606
	 *
2607
	 * 	@param	int		$rowid		Id of line
2608
	 * 	@param	int		$rang		Position
2609
	 * 	@return	void
2610
	 */
2611
	public function updateLineUp($rowid, $rang)
2612
	{
2613
		if ($rang > 1)
2614
		{
2615
			$fieldposition = 'rang';
2616
			if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2617
2618
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang ;
2619
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2620
			$sql.= ' AND rang = '.($rang - 1);
2621
			if ($this->db->query($sql) )
2622
			{
2623
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang - 1);
2624
				$sql.= ' WHERE rowid = '.$rowid;
2625
				if (! $this->db->query($sql) )
2626
				{
2627
					dol_print_error($this->db);
2628
				}
2629
			}
2630
			else
2631
			{
2632
				dol_print_error($this->db);
2633
			}
2634
		}
2635
	}
2636
2637
	/**
2638
	 * 	Update position of line down (rang)
2639
	 *
2640
	 * 	@param	int		$rowid		Id of line
2641
	 * 	@param	int		$rang		Position
2642
	 * 	@param	int		$max		Max
2643
	 * 	@return	void
2644
	 */
2645
	public function updateLineDown($rowid, $rang, $max)
2646
	{
2647
		if ($rang < $max)
2648
		{
2649
			$fieldposition = 'rang';
2650
			if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2651
2652
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2653
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2654
			$sql.= ' AND rang = '.($rang+1);
2655
			if ($this->db->query($sql) )
2656
			{
2657
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang+1);
2658
				$sql.= ' WHERE rowid = '.$rowid;
2659
				if (! $this->db->query($sql) )
2660
				{
2661
					dol_print_error($this->db);
2662
				}
2663
			}
2664
			else
2665
			{
2666
				dol_print_error($this->db);
2667
			}
2668
		}
2669
	}
2670
2671
	/**
2672
	 * 	Get position of line (rang)
2673
	 *
2674
	 * 	@param		int		$rowid		Id of line
2675
	 *  @return		int     			Value of rang in table of lines
2676
	 */
2677
	public function getRangOfLine($rowid)
2678
	{
2679
		$sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2680
		$sql.= ' WHERE rowid ='.$rowid;
2681
2682
		dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
2683
		$resql = $this->db->query($sql);
2684
		if ($resql)
2685
		{
2686
			$row = $this->db->fetch_row($resql);
2687
			return $row[0];
2688
		}
2689
	}
2690
2691
	/**
2692
	 * 	Get rowid of the line relative to its position
2693
	 *
2694
	 * 	@param		int		$rang		Rang value
2695
	 *  @return     int     			Rowid of the line
2696
	 */
2697
	public function getIdOfLine($rang)
2698
	{
2699
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2700
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2701
		$sql.= ' AND rang = '.$rang;
2702
		$resql = $this->db->query($sql);
2703
		if ($resql)
2704
		{
2705
			$row = $this->db->fetch_row($resql);
2706
			return $row[0];
2707
		}
2708
	}
2709
2710
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2711
	/**
2712
	 * 	Get max value used for position of line (rang)
2713
	 *
2714
	 * 	@param		int		$fk_parent_line		Parent line id
2715
	 *  @return     int  			   			Max value of rang in table of lines
2716
	 */
2717
	public function line_max($fk_parent_line = 0)
2718
	{
2719
        // phpcs:enable
2720
		// Search the last rang with fk_parent_line
2721
		if ($fk_parent_line)
2722
		{
2723
			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2724
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2725
			$sql.= ' AND fk_parent_line = '.$fk_parent_line;
2726
2727
			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2728
			$resql = $this->db->query($sql);
2729
			if ($resql)
2730
			{
2731
				$row = $this->db->fetch_row($resql);
2732
				if (! empty($row[0]))
2733
				{
2734
					return $row[0];
2735
				}
2736
				else
2737
				{
2738
					return $this->getRangOfLine($fk_parent_line);
2739
				}
2740
			}
2741
		}
2742
		// If not, search the last rang of element
2743
		else
2744
		{
2745
			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2746
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2747
2748
			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2749
			$resql = $this->db->query($sql);
2750
			if ($resql)
2751
			{
2752
				$row = $this->db->fetch_row($resql);
2753
				return $row[0];
2754
			}
2755
		}
2756
	}
2757
2758
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2759
	/**
2760
	 *  Update external ref of element
2761
	 *
2762
	 *  @param      string		$ref_ext	Update field ref_ext
2763
	 *  @return     int      		   		<0 if KO, >0 if OK
2764
	 */
2765
	public function update_ref_ext($ref_ext)
2766
	{
2767
        // phpcs:enable
2768
		if (! $this->table_element)
2769
		{
2770
			dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
2771
			return -1;
2772
		}
2773
2774
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2775
		$sql.= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
2776
		$sql.= " WHERE ".(isset($this->table_rowid)?$this->table_rowid:'rowid')." = ". $this->id;
2777
2778
		dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
2779
		if ($this->db->query($sql))
2780
		{
2781
			$this->ref_ext = $ref_ext;
2782
			return 1;
2783
		}
2784
		else
2785
		{
2786
			$this->error=$this->db->error();
2787
			return -1;
2788
		}
2789
	}
2790
2791
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2792
	/**
2793
	 *  Update note of element
2794
	 *
2795
	 *  @param      string		$note		New value for note
2796
	 *  @param		string		$suffix		'', '_public' or '_private'
2797
	 *  @return     int      		   		<0 if KO, >0 if OK
2798
	 */
2799
	public function update_note($note, $suffix = '')
2800
	{
2801
        // phpcs:enable
2802
		global $user;
2803
2804
		if (! $this->table_element)
2805
		{
2806
			$this->error='update_note was called on objet with property table_element not defined';
2807
			dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
2808
			return -1;
2809
		}
2810
		if (! in_array($suffix, array('','_public','_private')))
2811
		{
2812
			$this->error='update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
2813
			dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
2814
			return -2;
2815
		}
2816
		// Special cas
2817
		//var_dump($this->table_element);exit;
2818
		if ($this->table_element == 'product') $suffix='';
2819
2820
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2821
		$sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL");
2822
		$sql.= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))?"fk_user_mod":"fk_user_modif")." = ".$user->id;
2823
		$sql.= " WHERE rowid =". $this->id;
2824
2825
		dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
2826
		if ($this->db->query($sql))
2827
		{
2828
			if ($suffix == '_public') $this->note_public = $note;
2829
			elseif ($suffix == '_private') $this->note_private = $note;
2830
			else
2831
			{
2832
				$this->note = $note;      // deprecated
2833
				$this->note_private = $note;
2834
			}
2835
			return 1;
2836
		}
2837
		else
2838
		{
2839
			$this->error=$this->db->lasterror();
2840
			return -1;
2841
		}
2842
	}
2843
2844
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2845
	/**
2846
	 * 	Update public note (kept for backward compatibility)
2847
	 *
2848
	 * @param      string		$note		New value for note
2849
	 * @return     int      		   		<0 if KO, >0 if OK
2850
	 * @deprecated
2851
	 * @see update_note()
2852
	 */
2853
	public function update_note_public($note)
2854
	{
2855
        // phpcs:enable
2856
		return $this->update_note($note, '_public');
2857
	}
2858
2859
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2860
	/**
2861
	 *	Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
2862
	 *  Must be called at end of methods addline or updateline.
2863
	 *
2864
	 *	@param	int		$exclspec          	>0 = Exclude special product (product_type=9)
2865
	 *  @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
2866
	 *  @param	int		$nodatabaseupdate	1=Do not update database. Update only properties of object.
2867
	 *  @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.
2868
	 *	@return	int    			           	<0 if KO, >0 if OK
2869
	 */
2870
	public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null)
2871
	{
2872
        // phpcs:enable
2873
		global $conf, $hookmanager, $action;
2874
2875
		// Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
2876
		$MODULE = "";
2877
		if ($this->element == 'propal')
2878
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
2879
		elseif ($this->element == 'commande' || $this->element == 'order')
2880
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
2881
		elseif ($this->element == 'facture' || $this->element == 'invoice')
2882
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
2883
		elseif ($this->element == 'facture_fourn' || $this->element == 'supplier_invoice')
2884
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
2885
		elseif ($this->element == 'order_supplier' || $this->element == 'supplier_order')
2886
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
2887
		elseif ($this->element == 'supplier_proposal')
2888
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
2889
2890
		if (! empty($MODULE)) {
2891
			if (! empty($conf->global->$MODULE)) {
2892
				$modsactivated = explode(',', $conf->global->$MODULE);
2893
				foreach ($modsactivated as $mod) {
2894
					if ($conf->$mod->enabled)
2895
						return 1; // update was disabled by specific setup
2896
				}
2897
			}
2898
		}
2899
2900
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2901
2902
		if ($roundingadjust == '-1') $roundingadjust='auto';	// For backward compatibility
2903
2904
		$forcedroundingmode=$roundingadjust;
2905
		if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode=$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
2906
		elseif ($forcedroundingmode == 'auto') $forcedroundingmode='0';
2907
2908
		$error=0;
2909
2910
		$multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
2911
2912
		// Define constants to find lines to sum
2913
		$fieldtva='total_tva';
2914
		$fieldlocaltax1='total_localtax1';
2915
		$fieldlocaltax2='total_localtax2';
2916
		$fieldup='subprice';
2917
		if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
2918
		{
2919
			$fieldtva='tva';
2920
			$fieldup='pu_ht';
2921
		}
2922
		if ($this->element == 'expensereport')
2923
		{
2924
			$fieldup='value_unit';
2925
		}
2926
2927
		$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,';
2928
		$sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
2929
			if ($this->table_element_line == 'facturedet') $sql.= ', situation_percent';
2930
			$sql.= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
2931
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2932
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2933
		if ($exclspec)
2934
		{
2935
			$product_field='product_type';
2936
			if ($this->table_element_line == 'contratdet') $product_field='';    // contratdet table has no product_type field
2937
			if ($product_field) $sql.= ' AND '.$product_field.' <> 9';
2938
		}
2939
		$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
2940
2941
		dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2942
		$resql = $this->db->query($sql);
2943
		if ($resql)
2944
		{
2945
			$this->total_ht  = 0;
2946
			$this->total_tva = 0;
2947
			$this->total_localtax1 = 0;
2948
			$this->total_localtax2 = 0;
2949
			$this->total_ttc = 0;
2950
			$total_ht_by_vats  = array();
2951
			$total_tva_by_vats = array();
2952
			$total_ttc_by_vats = array();
2953
			$this->multicurrency_total_ht	= 0;
2954
			$this->multicurrency_total_tva	= 0;
2955
			$this->multicurrency_total_ttc	= 0;
2956
2957
			$num = $this->db->num_rows($resql);
2958
			$i = 0;
2959
			while ($i < $num)
2960
			{
2961
				$obj = $this->db->fetch_object($resql);
2962
2963
				// Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
2964
				$parameters=array('fk_element' => $obj->rowid);
2965
				$reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2966
2967
				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'
2968
				{
2969
					$localtax_array=array($obj->localtax1_type,$obj->localtax1_tx,$obj->localtax2_type,$obj->localtax2_tx);
2970
					$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);
2971
					$diff=price2num($tmpcal[1] - $obj->total_tva, 'MT', 1);
2972
					if ($diff)
2973
					{
2974
						$sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid;
2975
						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);
2976
								$resqlfix=$this->db->query($sqlfix);
2977
								if (! $resqlfix) dol_print_error($this->db, 'Failed to update line');
2978
								$obj->total_tva = $tmpcal[1];
2979
								$obj->total_ttc = $tmpcal[2];
2980
						//
2981
					}
2982
				}
2983
2984
				$this->total_ht        += $obj->total_ht;		// The field visible at end of line detail
2985
				$this->total_tva       += $obj->total_tva;
2986
				$this->total_localtax1 += $obj->total_localtax1;
2987
				$this->total_localtax2 += $obj->total_localtax2;
2988
				$this->total_ttc       += $obj->total_ttc;
2989
				$this->multicurrency_total_ht        += $obj->multicurrency_total_ht;		// The field visible at end of line detail
2990
				$this->multicurrency_total_tva       += $obj->multicurrency_total_tva;
2991
				$this->multicurrency_total_ttc       += $obj->multicurrency_total_ttc;
2992
2993
				if (! isset($total_ht_by_vats[$obj->vatrate]))  $total_ht_by_vats[$obj->vatrate]=0;
2994
				if (! isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate]=0;
2995
				if (! isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate]=0;
2996
				$total_ht_by_vats[$obj->vatrate]  += $obj->total_ht;
2997
				$total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
2998
				$total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
2999
3000
				if ($forcedroundingmode == '1')	// Check if we need adjustement onto line for vat. TODO This works on the company currency but not on multicurrency
3001
				{
3002
					$tmpvat=price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
3003
					$diff=price2num($total_tva_by_vats[$obj->vatrate]-$tmpvat, 'MT', 1);
3004
					//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";
3005
					if ($diff)
3006
					{
3007
						if (abs($diff) > 0.1) { dol_syslog('A rounding difference was detected into TOTAL but is too high to be corrected', LOG_WARNING); exit; }
3008
						$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;
3009
						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);
3010
								$resqlfix=$this->db->query($sqlfix);
3011
								if (! $resqlfix) dol_print_error($this->db, 'Failed to update line');
3012
								$this->total_tva -= $diff;
3013
								$this->total_ttc -= $diff;
3014
								$total_tva_by_vats[$obj->vatrate] -= $diff;
3015
								$total_ttc_by_vats[$obj->vatrate] -= $diff;
3016
					}
3017
				}
3018
3019
				$i++;
3020
			}
3021
3022
			// Add revenue stamp to total
3023
			$this->total_ttc       			+= isset($this->revenuestamp)?$this->revenuestamp:0;
3024
			$this->multicurrency_total_ttc  += isset($this->revenuestamp)?($this->revenuestamp * $multicurrency_tx):0;
3025
3026
			// Situations totals
3027
			if ($this->situation_cycle_ref && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE )
3028
			{
3029
				$prev_sits = $this->get_prev_sits();
3030
3031
				foreach ($prev_sits as $sit) {				// $sit is an object Facture loaded with a fetch.
3032
					$this->total_ht -= $sit->total_ht;
3033
					$this->total_tva -= $sit->total_tva;
3034
					$this->total_localtax1 -= $sit->total_localtax1;
3035
					$this->total_localtax2 -= $sit->total_localtax2;
3036
					$this->total_ttc -= $sit->total_ttc;
3037
					$this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
3038
					$this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
3039
					$this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
3040
				}
3041
			}
3042
3043
			$this->db->free($resql);
3044
3045
			// Now update global field total_ht, total_ttc and tva
3046
			$fieldht='total_ht';
3047
			$fieldtva='tva';
3048
			$fieldlocaltax1='localtax1';
3049
			$fieldlocaltax2='localtax2';
3050
			$fieldttc='total_ttc';
3051
			// Specific code for backward compatibility with old field names
3052
			if ($this->element == 'facture' || $this->element == 'facturerec')             $fieldht='total';
3053
			if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='total_tva';
3054
			if ($this->element == 'propal')                                                $fieldttc='total';
3055
			if ($this->element == 'expensereport')                                         $fieldtva='total_tva';
3056
			if ($this->element == 'supplier_proposal')                                     $fieldttc='total';
3057
3058
			if (empty($nodatabaseupdate))
3059
			{
3060
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
3061
				$sql .= " ".$fieldht."='".price2num($this->total_ht)."',";
3062
				$sql .= " ".$fieldtva."='".price2num($this->total_tva)."',";
3063
				$sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',";
3064
				$sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',";
3065
				$sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'";
3066
						$sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'";
3067
						$sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'";
3068
						$sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'";
3069
				$sql .= ' WHERE rowid = '.$this->id;
3070
3071
3072
				dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3073
				$resql=$this->db->query($sql);
3074
				if (! $resql)
3075
				{
3076
					$error++;
3077
					$this->error=$this->db->lasterror();
3078
					$this->errors[]=$this->db->lasterror();
3079
				}
3080
			}
3081
3082
			if (! $error)
3083
			{
3084
				return 1;
3085
			}
3086
			else
3087
			{
3088
				return -1;
3089
			}
3090
		}
3091
		else
3092
		{
3093
			dol_print_error($this->db, 'Bad request in update_price');
3094
			return -1;
3095
		}
3096
	}
3097
3098
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3099
	/**
3100
	 *	Add objects linked in llx_element_element.
3101
	 *
3102
	 *	@param		string	$origin		Linked element type
3103
	 *	@param		int		$origin_id	Linked element id
3104
	 *	@return		int					<=0 if KO, >0 if OK
3105
	 *	@see		fetchObjectLinked(), updateObjectLinked(), deleteObjectLinked()
3106
	 */
3107
	public function add_object_linked($origin = null, $origin_id = null)
3108
	{
3109
		// phpcs:enable
3110
		$origin = (! empty($origin) ? $origin : $this->origin);
3111
		$origin_id = (! empty($origin_id) ? $origin_id : $this->origin_id);
3112
3113
		// Special case
3114
		if ($origin == 'order') $origin='commande';
3115
		if ($origin == 'invoice') $origin='facture';
3116
		if ($origin == 'invoice_template') $origin='facturerec';
3117
		if ($origin == 'supplierorder') $origin='order_supplier';
3118
		$this->db->begin();
3119
3120
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
3121
		$sql.= "fk_source";
3122
		$sql.= ", sourcetype";
3123
		$sql.= ", fk_target";
3124
		$sql.= ", targettype";
3125
		$sql.= ") VALUES (";
3126
		$sql.= $origin_id;
3127
		$sql.= ", '".$this->db->escape($origin)."'";
3128
		$sql.= ", ".$this->id;
3129
		$sql.= ", '".$this->db->escape($this->element)."'";
3130
		$sql.= ")";
3131
3132
		dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG);
3133
		if ($this->db->query($sql))
3134
		{
3135
			$this->db->commit();
3136
			return 1;
3137
		}
3138
		else
3139
		{
3140
			$this->error=$this->db->lasterror();
3141
			$this->db->rollback();
3142
			return 0;
3143
		}
3144
	}
3145
3146
	/**
3147
	 *	Fetch array of objects linked to current object (object of enabled modules only). Links are loaded into
3148
	 *		this->linkedObjectsIds array and
3149
	 *		this->linkedObjects array if $loadalsoobjects = 1
3150
	 *  Possible usage for parameters:
3151
	 *  - all parameters empty -> we look all link to current object (current object can be source or target)
3152
	 *  - source id+type -> will get target list linked to source
3153
	 *  - target id+type -> will get source list linked to target
3154
	 *  - source id+type + target type -> will get target list of the type
3155
	 *  - target id+type + target source -> will get source list of the type
3156
	 *
3157
	 *	@param	int		$sourceid			Object source id (if not defined, id of object)
3158
	 *	@param  string	$sourcetype			Object source type (if not defined, element name of object)
3159
	 *	@param  int		$targetid			Object target id (if not defined, id of object)
3160
	 *	@param  string	$targettype			Object target type (if not defined, elemennt name of object)
3161
	 *	@param  string	$clause				'OR' or 'AND' clause used when both source id and target id are provided
3162
	 *  @param  int		$alsosametype		0=Return only links to object that differs from source type. 1=Include also link to objects of same type.
3163
	 *  @param  string	$orderby			SQL 'ORDER BY' clause
3164
	 *  @param	int		$loadalsoobjects	Load also array this->linkedObjects (Use 0 to increase performances)
3165
	 *	@return int							<0 if KO, >0 if OK
3166
	 *  @see	add_object_linked(), updateObjectLinked(), deleteObjectLinked()
3167
	 */
3168
	public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
3169
	{
3170
		global $conf;
3171
3172
		$this->linkedObjectsIds=array();
3173
		$this->linkedObjects=array();
3174
3175
		$justsource=false;
3176
		$justtarget=false;
3177
		$withtargettype=false;
3178
		$withsourcetype=false;
3179
3180
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid))
3181
		{
3182
			$justsource=true;  // the source (id and type) is a search criteria
3183
			if (! empty($targettype)) $withtargettype=true;
3184
		}
3185
		if (! empty($targetid) && ! empty($targettype) && empty($sourceid))
3186
		{
3187
			$justtarget=true;  // the target (id and type) is a search criteria
3188
			if (! empty($sourcetype)) $withsourcetype=true;
3189
		}
3190
3191
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3192
		$targetid = (! empty($targetid) ? $targetid : $this->id);
3193
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3194
		$targettype = (! empty($targettype) ? $targettype : $this->element);
3195
3196
		/*if (empty($sourceid) && empty($targetid))
3197
		 {
3198
		 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
3199
		 return -1;
3200
		 }*/
3201
3202
		// Links between objects are stored in table element_element
3203
		$sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype';
3204
		$sql.= ' FROM '.MAIN_DB_PREFIX.'element_element';
3205
		$sql.= " WHERE ";
3206
		if ($justsource || $justtarget)
3207
		{
3208
			if ($justsource)
3209
			{
3210
				$sql.= "fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."'";
3211
				if ($withtargettype) $sql.= " AND targettype = '".$targettype."'";
3212
			}
3213
			elseif ($justtarget)
3214
			{
3215
				$sql.= "fk_target = ".$targetid." AND targettype = '".$targettype."'";
3216
				if ($withsourcetype) $sql.= " AND sourcetype = '".$sourcetype."'";
3217
			}
3218
		}
3219
		else
3220
		{
3221
			$sql.= "(fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."')";
3222
			$sql.= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$targettype."')";
3223
		}
3224
		$sql .= ' ORDER BY '.$orderby;
3225
3226
		dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3227
		$resql = $this->db->query($sql);
3228
		if ($resql)
3229
		{
3230
			$num = $this->db->num_rows($resql);
3231
			$i = 0;
3232
			while ($i < $num)
3233
			{
3234
				$obj = $this->db->fetch_object($resql);
3235
				if ($justsource || $justtarget)
3236
				{
3237
					if ($justsource)
3238
					{
3239
						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3240
					}
3241
					elseif ($justtarget)
3242
					{
3243
						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3244
					}
3245
				}
3246
				else
3247
				{
3248
					if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype)
3249
					{
3250
						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3251
					}
3252
					if ($obj->fk_target == $targetid && $obj->targettype == $targettype)
3253
					{
3254
						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3255
					}
3256
				}
3257
				$i++;
3258
			}
3259
3260
			if (! empty($this->linkedObjectsIds))
3261
			{
3262
				$tmparray = $this->linkedObjectsIds;
3263
				foreach($tmparray as $objecttype => $objectids)       // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
3264
				{
3265
					// Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
3266
					$module = $element = $subelement = $objecttype;
3267
					if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
3268
						&& preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs))
3269
					{
3270
						$module = $element = $regs[1];
3271
						$subelement = $regs[2];
3272
					}
3273
3274
					$classpath = $element.'/class';
3275
					// To work with non standard classpath or module name
3276
					if ($objecttype == 'facture')			{
3277
						$classpath = 'compta/facture/class';
3278
					}
3279
					elseif ($objecttype == 'facturerec')			{
3280
						$classpath = 'compta/facture/class'; $module = 'facture';
3281
					}
3282
					elseif ($objecttype == 'propal')			{
3283
						$classpath = 'comm/propal/class';
3284
					}
3285
					elseif ($objecttype == 'supplier_proposal')			{
3286
						$classpath = 'supplier_proposal/class';
3287
					}
3288
					elseif ($objecttype == 'shipping')			{
3289
						$classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon';
3290
					}
3291
					elseif ($objecttype == 'delivery')			{
3292
						$classpath = 'livraison/class'; $subelement = 'livraison'; $module = 'livraison_bon';
3293
					}
3294
					elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier')	{
3295
						$classpath = 'fourn/class'; $module = 'fournisseur';
3296
					}
3297
					elseif ($objecttype == 'fichinter')			{
3298
						$classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter';
3299
					}
3300
					elseif ($objecttype == 'subscription')			{
3301
						$classpath = 'adherents/class'; $module = 'adherent';
3302
					}
3303
3304
					// Set classfile
3305
					$classfile = strtolower($subelement); $classname = ucfirst($subelement);
3306
3307
					if ($objecttype == 'order') {
3308
						$classfile = 'commande'; $classname = 'Commande';
3309
					}
3310
					elseif ($objecttype == 'invoice_supplier') {
3311
						$classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur';
3312
					}
3313
					elseif ($objecttype == 'order_supplier')   {
3314
						$classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur';
3315
					}
3316
					elseif ($objecttype == 'supplier_proposal')   {
3317
						$classfile = 'supplier_proposal'; $classname = 'SupplierProposal';
3318
					}
3319
					elseif ($objecttype == 'facturerec')   {
3320
						$classfile = 'facture-rec'; $classname = 'FactureRec';
3321
					}
3322
					elseif ($objecttype == 'subscription')   {
3323
						$classfile = 'subscription'; $classname = 'Subscription';
3324
					}
3325
3326
					// Here $module, $classfile and $classname are set
3327
					if ($conf->$module->enabled && (($element != $this->element) || $alsosametype))
3328
					{
3329
						if ($loadalsoobjects)
3330
						{
3331
							dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
3332
							//print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
3333
							if (class_exists($classname))
3334
							{
3335
								foreach($objectids as $i => $objectid)	// $i is rowid into llx_element_element
3336
								{
3337
									$object = new $classname($this->db);
3338
									$ret = $object->fetch($objectid);
3339
									if ($ret >= 0)
3340
									{
3341
										$this->linkedObjects[$objecttype][$i] = $object;
3342
									}
3343
								}
3344
							}
3345
						}
3346
					}
3347
					else
3348
					{
3349
						unset($this->linkedObjectsIds[$objecttype]);
3350
					}
3351
				}
3352
			}
3353
			return 1;
3354
		}
3355
		else
3356
		{
3357
			dol_print_error($this->db);
3358
			return -1;
3359
		}
3360
	}
3361
3362
	/**
3363
	 *	Update object linked of a current object
3364
	 *
3365
	 *	@param	int		$sourceid		Object source id
3366
	 *	@param  string	$sourcetype		Object source type
3367
	 *	@param  int		$targetid		Object target id
3368
	 *	@param  string	$targettype		Object target type
3369
	 *	@return							int	>0 if OK, <0 if KO
3370
	 *	@see	add_object_linked(), fetObjectLinked(), deleteObjectLinked()
3371
	 */
3372
	public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '')
3373
	{
3374
		$updatesource=false;
3375
		$updatetarget=false;
3376
3377
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource=true;
3378
		elseif (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $updatetarget=true;
3379
3380
		$sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET ";
3381
		if ($updatesource)
3382
		{
3383
			$sql.= "fk_source = ".$sourceid;
3384
			$sql.= ", sourcetype = '".$this->db->escape($sourcetype)."'";
3385
			$sql.= " WHERE fk_target = ".$this->id;
3386
			$sql.= " AND targettype = '".$this->db->escape($this->element)."'";
3387
		}
3388
		elseif ($updatetarget)
3389
		{
3390
			$sql.= "fk_target = ".$targetid;
3391
			$sql.= ", targettype = '".$this->db->escape($targettype)."'";
3392
			$sql.= " WHERE fk_source = ".$this->id;
3393
			$sql.= " AND sourcetype = '".$this->db->escape($this->element)."'";
3394
		}
3395
3396
		dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG);
3397
		if ($this->db->query($sql))
3398
		{
3399
			return 1;
3400
		}
3401
		else
3402
		{
3403
			$this->error=$this->db->lasterror();
3404
			return -1;
3405
		}
3406
	}
3407
3408
	/**
3409
	 *	Delete all links between an object $this
3410
	 *
3411
	 *	@param	int		$sourceid		Object source id
3412
	 *	@param  string	$sourcetype		Object source type
3413
	 *	@param  int		$targetid		Object target id
3414
	 *	@param  string	$targettype		Object target type
3415
	 *  @param	int		$rowid			Row id of line to delete. If defined, other parameters are not used.
3416
	 *	@return     					int	>0 if OK, <0 if KO
3417
	 *	@see	add_object_linked(), updateObjectLinked(), fetchObjectLinked()
3418
	 */
3419
	public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = '')
3420
	{
3421
		$deletesource=false;
3422
		$deletetarget=false;
3423
3424
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource=true;
3425
		elseif (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $deletetarget=true;
3426
3427
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3428
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3429
		$targetid = (! empty($targetid) ? $targetid : $this->id);
3430
		$targettype = (! empty($targettype) ? $targettype : $this->element);
3431
3432
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element";
3433
		$sql.= " WHERE";
3434
		if ($rowid > 0)
3435
		{
3436
			$sql.=" rowid = ".$rowid;
3437
		}
3438
		else
3439
		{
3440
			if ($deletesource)
3441
			{
3442
				$sql.= " fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3443
				$sql.= " AND fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."'";
3444
			}
3445
			elseif ($deletetarget)
3446
			{
3447
				$sql.= " fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'";
3448
				$sql.= " AND fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."'";
3449
			}
3450
			else
3451
			{
3452
				$sql.= " (fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."')";
3453
				$sql.= " OR";
3454
				$sql.= " (fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."')";
3455
			}
3456
		}
3457
3458
		dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG);
3459
		if ($this->db->query($sql))
3460
		{
3461
			return 1;
3462
		}
3463
		else
3464
		{
3465
			$this->error=$this->db->lasterror();
3466
			$this->errors[]=$this->error;
3467
			return -1;
3468
		}
3469
	}
3470
3471
	/**
3472
	 *      Set status of an object
3473
	 *
3474
	 *      @param	int		$status			Status to set
3475
	 *      @param	int		$elementId		Id of element to force (use this->id by default)
3476
	 *      @param	string	$elementType	Type of element to force (use this->table_element by default)
3477
	 *      @param	string	$trigkey		Trigger key to use for trigger
3478
	 *      @return int						<0 if KO, >0 if OK
3479
	 */
3480
	public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '')
3481
	{
3482
		global $user,$langs,$conf;
3483
3484
		$savElementId=$elementId;  // To be used later to know if we were using the method using the id of this or not.
3485
3486
		$elementId = (!empty($elementId)?$elementId:$this->id);
3487
		$elementTable = (!empty($elementType)?$elementType:$this->table_element);
3488
3489
		$this->db->begin();
3490
3491
		$fieldstatus="fk_statut";
3492
		if ($elementTable == 'facture_rec') $fieldstatus="suspended";
3493
		if ($elementTable == 'mailing') $fieldstatus="statut";
3494
		if ($elementTable == 'cronjob') $fieldstatus="status";
3495
		if ($elementTable == 'user') $fieldstatus="statut";
3496
		if ($elementTable == 'expensereport') $fieldstatus="fk_statut";
3497
		if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status";
3498
3499
		$sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable;
3500
		$sql.= " SET ".$fieldstatus." = ".$status;
3501
		// If status = 1 = validated, update also fk_user_valid
3502
		if ($status == 1 && $elementTable == 'expensereport') $sql.=", fk_user_valid = ".$user->id;
3503
		$sql.= " WHERE rowid=".$elementId;
3504
3505
		dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
3506
		if ($this->db->query($sql))
3507
		{
3508
			$error = 0;
3509
3510
			// Try autoset of trigkey
3511
			if (empty($trigkey))
3512
			{
3513
				if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_SIGN';   // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
3514
				if ($this->element == 'supplier_proposal' && $status == 3) $trigkey='SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
3515
				if ($this->element == 'supplier_proposal' && $status == 4) $trigkey='SUPPLIER_PROPOSAL_CLOSE';  // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
3516
				if ($this->element == 'fichinter' && $status == 3) $trigkey='FICHINTER_CLASSIFY_DONE';
3517
				if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED';
3518
				if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED';
3519
			}
3520
3521
			if ($trigkey)
3522
			{
3523
				// Appel des triggers
3524
				include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
3525
				$interface=new Interfaces($this->db);
3526
				$result=$interface->run_triggers($trigkey, $this, $user, $langs, $conf);
3527
				if ($result < 0) {
3528
					$error++; $this->errors=$interface->errors;
3529
				}
3530
				// Fin appel triggers
3531
			}
3532
3533
			if (! $error)
3534
			{
3535
				$this->db->commit();
3536
3537
				if (empty($savElementId))    // If the element we update was $this (so $elementId is null)
3538
				{
3539
					$this->statut = $status;
3540
					$this->status = $status;
3541
				}
3542
3543
				return 1;
3544
			}
3545
			else
3546
			{
3547
				$this->db->rollback();
3548
				dol_syslog(get_class($this)."::setStatus ".$this->error, LOG_ERR);
3549
				return -1;
3550
			}
3551
		}
3552
		else
3553
		{
3554
			$this->error=$this->db->lasterror();
3555
			$this->db->rollback();
3556
			return -1;
3557
		}
3558
	}
3559
3560
3561
	/**
3562
	 *  Load type of canvas of an object if it exists
3563
	 *
3564
	 *  @param      int		$id     Record id
3565
	 *  @param      string	$ref    Record ref
3566
	 *  @return		int				<0 if KO, 0 if nothing done, >0 if OK
3567
	 */
3568
	public function getCanvas($id = 0, $ref = '')
3569
	{
3570
		global $conf;
3571
3572
		if (empty($id) && empty($ref)) return 0;
3573
		if (! empty($conf->global->MAIN_DISABLE_CANVAS)) return 0;    // To increase speed. Not enabled by default.
3574
3575
		// Clean parameters
3576
		$ref = trim($ref);
3577
3578
		$sql = "SELECT rowid, canvas";
3579
		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
3580
		$sql.= " WHERE entity IN (".getEntity($this->element).")";
3581
		if (! empty($id))  $sql.= " AND rowid = ".$id;
3582
		if (! empty($ref)) $sql.= " AND ref = '".$this->db->escape($ref)."'";
3583
3584
		$resql = $this->db->query($sql);
3585
		if ($resql)
3586
		{
3587
			$obj = $this->db->fetch_object($resql);
3588
			if ($obj)
3589
			{
3590
				$this->canvas   = $obj->canvas;
3591
				return 1;
3592
			}
3593
			else return 0;
3594
		}
3595
		else
3596
		{
3597
			dol_print_error($this->db);
3598
			return -1;
3599
		}
3600
	}
3601
3602
3603
	/**
3604
	 * 	Get special code of a line
3605
	 *
3606
	 * 	@param	int		$lineid		Id of line
3607
	 * 	@return	int					Special code
3608
	 */
3609
	public function getSpecialCode($lineid)
3610
	{
3611
		$sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line;
3612
		$sql.= ' WHERE rowid = '.$lineid;
3613
		$resql = $this->db->query($sql);
3614
		if ($resql)
3615
		{
3616
			$row = $this->db->fetch_row($resql);
3617
			return $row[0];
3618
		}
3619
	}
3620
3621
	/**
3622
	 *  Function to check if an object is used by others.
3623
	 *  Check is done into this->childtables. There is no check into llx_element_element.
3624
	 *
3625
	 *  @param	int		$id			Force id of object
3626
	 *  @return	int					<0 if KO, 0 if not used, >0 if already used
3627
	 */
3628
	public function isObjectUsed($id = 0)
3629
	{
3630
		global $langs;
3631
3632
		if (empty($id)) $id=$this->id;
3633
3634
		// Check parameters
3635
		if (! isset($this->childtables) || ! is_array($this->childtables) || count($this->childtables) == 0)
3636
		{
3637
			dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
3638
			return -1;
3639
		}
3640
3641
		$arraytoscan = $this->childtables;
3642
		// For backward compatibility, we check if array is old format array('table1', 'table2', ...)
3643
		$tmparray=array_keys($this->childtables);
3644
		if (is_numeric($tmparray[0]))
3645
		{
3646
			$arraytoscan = array_flip($this->childtables);
3647
		}
3648
3649
		// Test if child exists
3650
		$haschild=0;
3651
		foreach($arraytoscan as $table => $elementname)
3652
		{
3653
			//print $id.'-'.$table.'-'.$elementname.'<br>';
3654
			// Check if third party can be deleted
3655
			$sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table;
3656
			$sql.= " WHERE ".$this->fk_element." = ".$id;
3657
			$resql=$this->db->query($sql);
3658
			if ($resql)
3659
			{
3660
				$obj=$this->db->fetch_object($resql);
3661
				if ($obj->nb > 0)
3662
				{
3663
					$langs->load("errors");
3664
					//print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
3665
					$haschild += $obj->nb;
3666
					if (is_numeric($elementname))	// old usage
3667
					{
3668
						$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $table);
3669
					}
3670
					else	// new usage: $elementname=Translation key
3671
					{
3672
						$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $langs->transnoentitiesnoconv($elementname));
3673
					}
3674
					break;    // We found at least one, we stop here
3675
				}
3676
			}
3677
			else
3678
			{
3679
				$this->errors[]=$this->db->lasterror();
3680
				return -1;
3681
			}
3682
		}
3683
		if ($haschild > 0)
3684
		{
3685
			$this->errors[]="ErrorRecordHasChildren";
3686
			return $haschild;
3687
		}
3688
		else return 0;
3689
	}
3690
3691
	/**
3692
	 *  Function to say how many lines object contains
3693
	 *
3694
	 *	@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
3695
	 *  @return	int						<0 if KO, 0 if no predefined products, nb of lines with predefined products if found
3696
	 */
3697
	public function hasProductsOrServices($predefined = -1)
3698
	{
3699
		$nb=0;
3700
3701
		foreach($this->lines as $key => $val)
3702
		{
3703
			$qualified=0;
3704
			if ($predefined == -1) $qualified=1;
3705
			if ($predefined == 1 && $val->fk_product > 0) $qualified=1;
3706
			if ($predefined == 0 && $val->fk_product <= 0) $qualified=1;
3707
			if ($predefined == 2 && $val->fk_product > 0 && $val->product_type==0) $qualified=1;
3708
			if ($predefined == 3 && $val->fk_product > 0 && $val->product_type==1) $qualified=1;
3709
			if ($qualified) $nb++;
3710
		}
3711
		dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
3712
		return $nb;
3713
	}
3714
3715
	/**
3716
	 * Function that returns the total amount HT of discounts applied for all lines.
3717
	 *
3718
	 * @return 	float
3719
	 */
3720
	public function getTotalDiscount()
3721
	{
3722
		$total_discount=0.00;
3723
3724
		$sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
3725
		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element."det";
3726
		$sql.= " WHERE ".$this->fk_element." = ".$this->id;
3727
3728
		dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
3729
		$resql = $this->db->query($sql);
3730
		if ($resql)
3731
		{
3732
			$num=$this->db->num_rows($resql);
3733
			$i=0;
3734
			while ($i < $num)
3735
			{
3736
				$obj = $this->db->fetch_object($resql);
3737
3738
				$pu_ht = $obj->pu_ht;
3739
				$qty= $obj->qty;
3740
				$total_ht = $obj->total_ht;
3741
3742
				$total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
3743
				$total_discount += $total_discount_line;
3744
3745
				$i++;
3746
			}
3747
		}
3748
3749
		//print $total_discount; exit;
3750
		return price2num($total_discount);
3751
	}
3752
3753
3754
	/**
3755
	 * Return into unit=0, the calculated total of weight and volume of all lines * qty
3756
	 * Calculate by adding weight and volume of each product line, so properties ->volume/volume_units/weight/weight_units must be loaded on line.
3757
	 *
3758
	 * @return  array                           array('weight'=>...,'volume'=>...)
3759
	 */
3760
	public function getTotalWeightVolume()
3761
	{
3762
		$totalWeight = 0;
3763
		$totalVolume = 0;
3764
		// defined for shipment only
3765
		$totalOrdered = '';
3766
		// defined for shipment only
3767
		$totalToShip = '';
3768
3769
		foreach ($this->lines as $line)
3770
		{
3771
			if (isset($line->qty_asked))
3772
			{
3773
				if (empty($totalOrdered)) $totalOrdered=0;  // Avoid warning because $totalOrdered is ''
3774
				$totalOrdered+=$line->qty_asked;    // defined for shipment only
3775
			}
3776
			if (isset($line->qty_shipped))
3777
			{
3778
				if (empty($totalToShip)) $totalToShip=0;    // Avoid warning because $totalToShip is ''
3779
				$totalToShip+=$line->qty_shipped;   // defined for shipment only
3780
			}
3781
			elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty))
3782
			{
3783
				if (empty($totalToShip)) $totalToShip=0;
3784
				$totalToShip+=$line->qty;   // defined for reception only
3785
			}
3786
3787
			// Define qty, weight, volume, weight_units, volume_units
3788
			if ($this->element == 'shipping') {
3789
				// for shipments
3790
				$qty = $line->qty_shipped ? $line->qty_shipped : 0;
3791
			}
3792
			else {
3793
				$qty = $line->qty ? $line->qty : 0;
3794
			}
3795
3796
			$weight = $line->weight ? $line->weight : 0;
3797
            ($weight==0 && !empty($line->product->weight))? $weight=$line->product->weight: 0;
3798
			$volume = $line->volume ? $line->volume : 0;
3799
			($volume==0 && !empty($line->product->volume))? $volume=$line->product->volume: 0;
3800
3801
			$weight_units=$line->weight_units;
3802
			($weight_units==0 && !empty($line->product->weight_units))? $weight_units=$line->product->weight_units: 0;
3803
			$volume_units=$line->volume_units;
3804
			($volume_units==0 && !empty($line->product->volume_units))? $volume_units=$line->product->volume_units: 0;
3805
3806
			$weightUnit=0;
3807
			$volumeUnit=0;
3808
			if (! empty($weight_units)) $weightUnit = $weight_units;
3809
			if (! empty($volume_units)) $volumeUnit = $volume_units;
3810
3811
			if (empty($totalWeight)) $totalWeight=0;  // Avoid warning because $totalWeight is ''
3812
			if (empty($totalVolume)) $totalVolume=0;  // Avoid warning because $totalVolume is ''
3813
3814
			//var_dump($line->volume_units);
3815
			if ($weight_units < 50)   // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3816
			{
3817
				$trueWeightUnit=pow(10, $weightUnit);
3818
				$totalWeight += $weight * $qty * $trueWeightUnit;
3819
			}
3820
			else {
3821
		if ($weight_units == 99) {
3822
			// conversion 1 Pound = 0.45359237 KG
3823
			$trueWeightUnit = 0.45359237;
3824
			$totalWeight += $weight * $qty * $trueWeightUnit;
3825
		} elseif ($weight_units == 98) {
3826
			// conversion 1 Ounce = 0.0283495 KG
3827
			$trueWeightUnit = 0.0283495;
3828
			$totalWeight += $weight * $qty * $trueWeightUnit;
3829
		}
3830
		else
3831
					$totalWeight += $weight * $qty;   // This may be wrong if we mix different units
3832
			}
3833
			if ($volume_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3834
			{
3835
				//print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
3836
				$trueVolumeUnit=pow(10, $volumeUnit);
3837
				//print $line->volume;
3838
				$totalVolume += $volume * $qty * $trueVolumeUnit;
3839
			}
3840
			else
3841
			{
3842
				$totalVolume += $volume * $qty;   // This may be wrong if we mix different units
3843
			}
3844
		}
3845
3846
		return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
3847
	}
3848
3849
3850
	/**
3851
	 *	Set extra parameters
3852
	 *
3853
	 *	@return	int      <0 if KO, >0 if OK
3854
	 */
3855
	public function setExtraParameters()
3856
	{
3857
		$this->db->begin();
3858
3859
		$extraparams = (! empty($this->extraparams) ? json_encode($this->extraparams) : null);
3860
3861
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3862
		$sql.= " SET extraparams = ".(! empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
3863
		$sql.= " WHERE rowid = ".$this->id;
3864
3865
		dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
3866
		$resql = $this->db->query($sql);
3867
		if (! $resql)
3868
		{
3869
			$this->error=$this->db->lasterror();
3870
			$this->db->rollback();
3871
			return -1;
3872
		}
3873
		else
3874
		{
3875
			$this->db->commit();
3876
			return 1;
3877
		}
3878
	}
3879
3880
3881
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3882
	/**
3883
	 *    Return incoterms informations
3884
	 *    TODO Use a cache for label get
3885
	 *
3886
	 *    @return	string	incoterms info
3887
	 */
3888
	public function display_incoterms()
3889
	{
3890
        // phpcs:enable
3891
		$out = '';
3892
		$this->label_incoterms = '';
3893
		if (!empty($this->fk_incoterms))
3894
		{
3895
			$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3896
			$result = $this->db->query($sql);
3897
			if ($result)
3898
			{
3899
				$res = $this->db->fetch_object($result);
3900
				$out .= $res->code;
3901
			}
3902
		}
3903
3904
		$out .= (($res->code && $this->location_incoterms)?' - ':'').$this->location_incoterms;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $res does not seem to be defined for all execution paths leading up to this point.
Loading history...
3905
3906
		return $out;
3907
	}
3908
3909
	/**
3910
	 *    Return incoterms informations for pdf display
3911
	 *
3912
	 *    @return	string		incoterms info
3913
	 */
3914
	public function getIncotermsForPDF()
3915
	{
3916
		$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3917
		$resql = $this->db->query($sql);
3918
		if ($resql)
3919
		{
3920
			$num = $this->db->num_rows($resql);
3921
			if ($num > 0)
3922
			{
3923
				$res = $this->db->fetch_object($resql);
3924
				return 'Incoterm : '.$res->code.' - '.$this->location_incoterms;
3925
			}
3926
			else
3927
			{
3928
				return '';
3929
			}
3930
		}
3931
		else
3932
		{
3933
			$this->errors[] = $this->db->lasterror();
3934
			return false;
3935
		}
3936
	}
3937
3938
	/**
3939
	 *    Define incoterms values of current object
3940
	 *
3941
	 *    @param	int		$id_incoterm     Id of incoterm to set or '' to remove
3942
	 * 	  @param 	string  $location		 location of incoterm
3943
	 *    @return	int     		<0 if KO, >0 if OK
3944
	 */
3945
	public function setIncoterms($id_incoterm, $location)
3946
	{
3947
		if ($this->id && $this->table_element)
3948
		{
3949
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3950
			$sql.= " SET fk_incoterms = ".($id_incoterm > 0 ? $id_incoterm : "null");
3951
			$sql.= ", location_incoterms = ".($id_incoterm > 0 ? "'".$this->db->escape($location)."'" : "null");
3952
			$sql.= " WHERE rowid = " . $this->id;
3953
			dol_syslog(get_class($this).'::setIncoterms', LOG_DEBUG);
3954
			$resql=$this->db->query($sql);
3955
			if ($resql)
3956
			{
3957
				$this->fk_incoterms = $id_incoterm;
3958
				$this->location_incoterms = $location;
3959
3960
				$sql = 'SELECT libelle FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3961
				$res = $this->db->query($sql);
3962
				if ($res)
3963
				{
3964
					$obj = $this->db->fetch_object($res);
3965
					$this->label_incoterms = $obj->libelle;
3966
				}
3967
				return 1;
3968
			}
3969
			else
3970
			{
3971
				$this->errors[] = $this->db->lasterror();
3972
				return -1;
3973
			}
3974
		}
3975
		else return -1;
3976
	}
3977
3978
3979
	// --------------------
3980
	// TODO: All functions here must be redesigned and moved as they are not business functions but output functions
3981
	// --------------------
3982
3983
	/* This is to show add lines */
3984
3985
	/**
3986
	 *	Show add free and predefined products/services form
3987
	 *
3988
	 *  @param	int		        $dateSelector       1=Show also date range input fields
3989
	 *  @param	Societe			$seller				Object thirdparty who sell
3990
	 *  @param	Societe			$buyer				Object thirdparty who buy
3991
	 *  @param	string			$defaulttpldir		Directory where to find the template
3992
	 *	@return	void
3993
	 */
3994
	public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
3995
	{
3996
		global $conf,$user,$langs,$object,$hookmanager,$extrafields;
3997
		global $form,$bcnd,$var;
3998
3999
		// Line extrafield
4000
		if (! is_object($extrafields))
4001
		{
4002
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4003
			$extrafields = new ExtraFields($this->db);
4004
		}
4005
		$extrafields->fetch_name_optionals_label($this->table_element_line);
4006
4007
		// Output template part (modules that overwrite templates must declare this into descriptor)
4008
		// Use global variables + $dateSelector + $seller and $buyer
4009
		$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4010
		foreach($dirtpls as $module => $reldir)
4011
		{
4012
			if (!empty($module))
4013
			{
4014
				$tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
4015
			}
4016
			else
4017
			{
4018
				$tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
4019
			}
4020
4021
			if (empty($conf->file->strict_mode)) {
4022
				$res=@include $tpl;
4023
			} else {
4024
				$res=include $tpl; // for debug
4025
			}
4026
			if ($res) break;
4027
		}
4028
	}
4029
4030
4031
4032
	/* This is to show array of line of details */
4033
4034
4035
	/**
4036
	 *	Return HTML table for object lines
4037
	 *	TODO Move this into an output class file (htmlline.class.php)
4038
	 *	If lines are into a template, title must also be into a template
4039
	 *	But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
4040
	 *
4041
	 *	@param	string		$action				Action code
4042
	 *	@param  string		$seller            	Object of seller third party
4043
	 *	@param  string  	$buyer             	Object of buyer third party
4044
	 *	@param	int			$selected		   	Object line selected
4045
	 *	@param  int	    	$dateSelector      	1=Show also date range input fields
4046
	 *  @param	string		$defaulttpldir		Directory where to find the template
4047
	 *	@return	void
4048
	 */
4049
	public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
4050
	{
4051
		global $conf, $hookmanager, $langs, $user, $object, $form, $extrafields;
4052
		// TODO We should not use global var for this
4053
		global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
4054
4055
		// Define usemargins
4056
		$usemargins=0;
4057
		if (! empty($conf->margin->enabled) && ! empty($this->element) && in_array($this->element, array('facture','facturerec','propal','commande'))) $usemargins=1;
4058
4059
		$num = count($this->lines);
4060
4061
		// Line extrafield
4062
		if (! is_object($extrafields))
4063
		{
4064
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4065
			$extrafields = new ExtraFields($this->db);
4066
		}
4067
		$extrafields->fetch_name_optionals_label($this->table_element_line);
4068
4069
		$parameters = array('num'=>$num, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$this->table_element_line);
4070
		$reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4071
		if (empty($reshook))
4072
		{
4073
			// Output template part (modules that overwrite templates must declare this into descriptor)
4074
			// Use global variables + $dateSelector + $seller and $buyer
4075
			$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4076
			foreach($dirtpls as $module => $reldir)
4077
			{
4078
				if (!empty($module))
4079
				{
4080
					$tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
4081
				}
4082
				else
4083
				{
4084
					$tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
4085
				}
4086
				if (empty($conf->file->strict_mode)) {
4087
					$res=@include $tpl;
4088
				} else {
4089
					$res=include $tpl; // for debug
4090
				}
4091
				if ($res) break;
4092
			}
4093
		}
4094
4095
		$var = true;
4096
		$i	 = 0;
4097
4098
		print "<tbody>\n";
4099
		foreach ($this->lines as $line)
4100
		{
4101
			//Line extrafield
4102
			$line->fetch_optionals();
4103
4104
			//if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
4105
			if (is_object($hookmanager))   // Old code is commented on preceding line.
4106
			{
4107
				if (empty($line->fk_parent_line))
4108
				{
4109
					$parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'table_element_line'=>$line->table_element);
4110
					$reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
4111
				}
4112
				else
4113
				{
4114
					$parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'table_element_line'=>$line->table_element, 'fk_parent_line'=>$line->fk_parent_line);
4115
					$reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
4116
				}
4117
			}
4118
			if (empty($reshook))
4119
			{
4120
				$this->printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
4121
			}
4122
4123
			$i++;
4124
		}
4125
		print "</tbody><!-- end printObjectLines() -->\n";
4126
	}
4127
4128
	/**
4129
	 *	Return HTML content of a detail line
4130
	 *	TODO Move this into an output class file (htmlline.class.php)
4131
	 *
4132
	 *	@param	string      $action				GET/POST action
4133
	 *	@param  CommonObjectLine $line		       	Selected object line to output
4134
	 *	@param  string	    $var               	Is it a an odd line (true)
4135
	 *	@param  int		    $num               	Number of line (0)
4136
	 *	@param  int		    $i					I
4137
	 *	@param  int		    $dateSelector      	1=Show also date range input fields
4138
	 *	@param  string	    $seller            	Object of seller third party
4139
	 *	@param  string	    $buyer             	Object of buyer third party
4140
	 *	@param	int			$selected		   	Object line selected
4141
	 *  @param  Extrafields	$extrafields		Object of extrafields
4142
	 *  @param	string		$defaulttpldir		Directory where to find the template
4143
	 *	@return	void
4144
	 */
4145
	public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
4146
	{
4147
		global $conf,$langs,$user,$object,$hookmanager;
4148
		global $form,$bc,$bcdd;
4149
		global $object_rights, $disableedit, $disablemove, $disableremove;   // TODO We should not use global var for this !
4150
4151
		$object_rights = $this->getRights();
4152
4153
		$element=$this->element;
4154
4155
		$text=''; $description=''; $type=0;
4156
4157
		// Show product and description
4158
		$type=(! empty($line->product_type)?$line->product_type:$line->fk_product_type);
4159
		// Try to enhance type detection using date_start and date_end for free lines where type was not saved.
4160
		if (! empty($line->date_start)) $type=1; // deprecated
4161
		if (! empty($line->date_end)) $type=1; // deprecated
4162
4163
		// Ligne en mode visu
4164
		if ($action != 'editline' || $selected != $line->id)
4165
		{
4166
			// Product
4167
			if ($line->fk_product > 0)
4168
			{
4169
				$product_static = new Product($this->db);
4170
				$product_static->fetch($line->fk_product);
4171
4172
				$product_static->ref = $line->ref; //can change ref in hook
4173
				$product_static->label = $line->label; //can change label in hook
4174
				$text=$product_static->getNomUrl(1);
4175
4176
				// Define output language and label
4177
				if (! empty($conf->global->MAIN_MULTILANGS))
4178
				{
4179
					if (property_exists($this, 'socid') && ! is_object($this->thirdparty))
4180
					{
4181
						dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
4182
						return;
4183
					}
4184
4185
					$prod = new Product($this->db);
4186
					$prod->fetch($line->fk_product);
4187
4188
					$outputlangs = $langs;
4189
					$newlang='';
4190
					if (empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang=GETPOST('lang_id', 'aZ09');
4191
					if (! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang) && is_object($this->thirdparty)) $newlang=$this->thirdparty->default_lang;		// To use language of customer
4192
					if (! empty($newlang))
4193
					{
4194
						$outputlangs = new Translate("", $conf);
4195
						$outputlangs->setDefaultLang($newlang);
4196
					}
4197
4198
					$label = (! empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
4199
				}
4200
				else
4201
				{
4202
					$label = $line->product_label;
4203
				}
4204
4205
				$text.= ' - '.(! empty($line->label)?$line->label:$label);
4206
				$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.
4207
			}
4208
4209
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4210
4211
			// Output template part (modules that overwrite templates must declare this into descriptor)
4212
			// Use global variables + $dateSelector + $seller and $buyer
4213
			$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4214
			foreach($dirtpls as $module => $reldir)
4215
			{
4216
				if (!empty($module))
4217
				{
4218
					$tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
4219
				}
4220
				else
4221
				{
4222
					$tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
4223
				}
4224
4225
				if (empty($conf->file->strict_mode)) {
4226
					$res=@include $tpl;
4227
				} else {
4228
					$res=include $tpl; // for debug
4229
				}
4230
				if ($res) break;
4231
			}
4232
		}
4233
4234
		// Line in update mode
4235
		if ($this->statut == 0 && $action == 'editline' && $selected == $line->id)
4236
		{
4237
			$label = (! empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
4238
			$placeholder=' placeholder="'.$langs->trans("Label").'"';
4239
4240
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4241
4242
			// Output template part (modules that overwrite templates must declare this into descriptor)
4243
			// Use global variables + $dateSelector + $seller and $buyer
4244
			$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4245
			foreach($dirtpls as $module => $reldir)
4246
			{
4247
				if (!empty($module))
4248
				{
4249
					$tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
4250
				}
4251
				else
4252
				{
4253
					$tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
4254
				}
4255
4256
				if (empty($conf->file->strict_mode)) {
4257
					$res=@include $tpl;
4258
				} else {
4259
					$res=include $tpl; // for debug
4260
				}
4261
				if ($res) break;
4262
			}
4263
		}
4264
	}
4265
4266
4267
	/* This is to show array of line of details of source object */
4268
4269
4270
	/**
4271
	 * 	Return HTML table table of source object lines
4272
	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
4273
	 *  If lines are into a template, title must also be into a template
4274
	 *  But for the moment we don't know if it's possible, so we keep the method available on overloaded objects.
4275
	 *
4276
	 *	@param	string		$restrictlist		''=All lines, 'services'=Restrict to services only
4277
	 *  @param  array       $selectedLines      Array of lines id for selected lines
4278
	 *  @return	void
4279
	 */
4280
	public function printOriginLinesList($restrictlist = '', $selectedLines = array())
4281
	{
4282
		global $langs, $hookmanager, $conf, $form;
4283
4284
		print '<tr class="liste_titre">';
4285
		print '<td>'.$langs->trans('Ref').'</td>';
4286
		print '<td>'.$langs->trans('Description').'</td>';
4287
		print '<td class="right">'.$langs->trans('VATRate').'</td>';
4288
		print '<td class="right">'.$langs->trans('PriceUHT').'</td>';
4289
		if (!empty($conf->multicurrency->enabled)) print '<td class="right">'.$langs->trans('PriceUHTCurrency').'</td>';
4290
		print '<td class="right">'.$langs->trans('Qty').'</td>';
4291
		if($conf->global->PRODUCT_USE_UNITS)
4292
		{
4293
			print '<td class="left">'.$langs->trans('Unit').'</td>';
4294
		}
4295
		print '<td class="right">'.$langs->trans('ReductionShort').'</td>';
4296
        print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
4297
        print '</tr>';
4298
		$var = true;
4299
		$i	 = 0;
4300
4301
		if (! empty($this->lines))
4302
		{
4303
			foreach ($this->lines as $line)
4304
			{
4305
				if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
4306
				{
4307
					if (empty($line->fk_parent_line))
4308
					{
4309
						$parameters=array('line'=>$line,'var'=>$var,'i'=>$i);
4310
						$action='';
4311
						$hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
4312
					}
4313
				}
4314
				else
4315
				{
4316
					$this->printOriginLine($line, $var, $restrictlist, '/core/tpl', $selectedLines);
4317
				}
4318
4319
				$i++;
4320
			}
4321
		}
4322
	}
4323
4324
	/**
4325
	 * 	Return HTML with a line of table array of source object lines
4326
	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
4327
	 *  If lines are into a template, title must also be into a template
4328
	 *  But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
4329
	 *
4330
	 * 	@param	CommonObjectLine	$line				Line
4331
	 * 	@param	string				$var				Var
4332
	 *	@param	string				$restrictlist		''=All lines, 'services'=Restrict to services only (strike line if not)
4333
	 *  @param	string				$defaulttpldir		Directory where to find the template
4334
	 *  @param  array       		$selectedLines      Array of lines id for selected lines
4335
	 * 	@return	void
4336
	 */
4337
	public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
4338
	{
4339
		global $langs, $conf;
4340
4341
		//var_dump($line);
4342
		if (!empty($line->date_start))
4343
		{
4344
			$date_start=$line->date_start;
4345
		}
4346
		else
4347
		{
4348
			$date_start=$line->date_debut_prevue;
0 ignored issues
show
Bug introduced by
The property date_debut_prevue does not seem to exist on CommonObjectLine.
Loading history...
4349
			if ($line->date_debut_reel) $date_start=$line->date_debut_reel;
0 ignored issues
show
Bug introduced by
The property date_debut_reel does not seem to exist on CommonObjectLine.
Loading history...
4350
		}
4351
		if (!empty($line->date_end))
4352
		{
4353
			$date_end=$line->date_end;
4354
		}
4355
		else
4356
		{
4357
			$date_end=$line->date_fin_prevue;
0 ignored issues
show
Bug introduced by
The property date_fin_prevue does not seem to exist on CommonObjectLine.
Loading history...
4358
			if ($line->date_fin_reel) $date_end=$line->date_fin_reel;
0 ignored issues
show
Bug introduced by
The property date_fin_reel does not seem to exist on CommonObjectLine.
Loading history...
4359
		}
4360
4361
        $this->tpl['id'] = $line->id;
4362
4363
		$this->tpl['label'] = '';
4364
		if (! empty($line->fk_parent_line)) $this->tpl['label'].= img_picto('', 'rightarrow');
4365
4366
		if (($line->info_bits & 2) == 2)  // TODO Not sure this is used for source object
4367
		{
4368
			$discount=new DiscountAbsolute($this->db);
4369
			$discount->fk_soc = $this->socid;
4370
			$this->tpl['label'].= $discount->getNomUrl(0, 'discount');
4371
		}
4372
		elseif (! empty($line->fk_product))
4373
		{
4374
			$productstatic = new Product($this->db);
4375
			$productstatic->id = $line->fk_product;
4376
			$productstatic->ref = $line->ref;
4377
			$productstatic->type = $line->fk_product_type;
4378
			if (empty($productstatic->ref)) {
4379
				$line->fetch_product();
4380
				$productstatic = $line->product;
4381
			}
4382
4383
			$this->tpl['label'].= $productstatic->getNomUrl(1);
4384
			$this->tpl['label'].= ' - '.(! empty($line->label)?$line->label:$line->product_label);
4385
			// Dates
4386
			if ($line->product_type == 1 && ($date_start || $date_end))
4387
			{
4388
				$this->tpl['label'].= get_date_range($date_start, $date_end);
4389
			}
4390
		}
4391
		else
4392
		{
4393
			$this->tpl['label'].= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
4394
			if (!empty($line->desc)) {
4395
				$this->tpl['label'].=$line->desc;
4396
			}else {
4397
				$this->tpl['label'].= ($line->label ? '&nbsp;'.$line->label : '');
4398
			}
4399
4400
			// Dates
4401
			if ($line->product_type == 1 && ($date_start || $date_end))
4402
			{
4403
				$this->tpl['label'].= get_date_range($date_start, $date_end);
4404
			}
4405
		}
4406
4407
		if (! empty($line->desc))
4408
		{
4409
			if ($line->desc == '(CREDIT_NOTE)')  // TODO Not sure this is used for source object
4410
			{
4411
				$discount=new DiscountAbsolute($this->db);
4412
				$discount->fetch($line->fk_remise_except);
4413
				$this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
4414
			}
4415
			elseif ($line->desc == '(DEPOSIT)')  // TODO Not sure this is used for source object
4416
			{
4417
				$discount=new DiscountAbsolute($this->db);
4418
				$discount->fetch($line->fk_remise_except);
4419
				$this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
4420
			}
4421
			elseif ($line->desc == '(EXCESS RECEIVED)')
4422
			{
4423
				$discount=new DiscountAbsolute($this->db);
4424
				$discount->fetch($line->fk_remise_except);
4425
				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
4426
			}
4427
			elseif ($line->desc == '(EXCESS PAID)')
4428
			{
4429
				$discount=new DiscountAbsolute($this->db);
4430
				$discount->fetch($line->fk_remise_except);
4431
				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
4432
			}
4433
			else
4434
			{
4435
				$this->tpl['description'] = dol_trunc($line->desc, 60);
4436
			}
4437
		}
4438
		else
4439
		{
4440
			$this->tpl['description'] = '&nbsp;';
4441
		}
4442
4443
        // VAT Rate
4444
        $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
4445
        $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
4446
        if (! empty($line->vat_src_code) && ! preg_match('/\(/', $this->tpl['vat_rate'])) $this->tpl['vat_rate'].=' ('.$line->vat_src_code.')';
4447
4448
		$this->tpl['price'] = price($line->subprice);
4449
		$this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
4450
		$this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
4451
		if ($conf->global->PRODUCT_USE_UNITS) $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
4452
		$this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
4453
4454
		// Is the line strike or not
4455
		$this->tpl['strike']=0;
4456
		if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) $this->tpl['strike']=1;
4457
4458
		// Output template part (modules that overwrite templates must declare this into descriptor)
4459
		// Use global variables + $dateSelector + $seller and $buyer
4460
		$dirtpls=array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4461
		foreach($dirtpls as $module => $reldir)
4462
		{
4463
			if (!empty($module))
4464
			{
4465
				$tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
4466
			}
4467
			else
4468
			{
4469
				$tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
4470
			}
4471
4472
			if (empty($conf->file->strict_mode)) {
4473
				$res=@include $tpl;
4474
			} else {
4475
				$res=include $tpl; // for debug
4476
			}
4477
			if ($res) break;
4478
		}
4479
	}
4480
4481
4482
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4483
	/**
4484
	 *	Add resources to the current object : add entry into llx_element_resources
4485
	 *	Need $this->element & $this->id
4486
	 *
4487
	 *	@param		int		$resource_id		Resource id
4488
	 *	@param		string	$resource_type		'resource'
4489
	 *	@param		int		$busy				Busy or not
4490
	 *	@param		int		$mandatory			Mandatory or not
4491
	 *	@return		int							<=0 if KO, >0 if OK
4492
	 */
4493
	public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
4494
	{
4495
        // phpcs:enable
4496
		$this->db->begin();
4497
4498
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources (";
4499
		$sql.= "resource_id";
4500
		$sql.= ", resource_type";
4501
		$sql.= ", element_id";
4502
		$sql.= ", element_type";
4503
		$sql.= ", busy";
4504
		$sql.= ", mandatory";
4505
		$sql.= ") VALUES (";
4506
		$sql.= $resource_id;
4507
		$sql.= ", '".$this->db->escape($resource_type)."'";
4508
		$sql.= ", '".$this->db->escape($this->id)."'";
4509
		$sql.= ", '".$this->db->escape($this->element)."'";
4510
		$sql.= ", '".$this->db->escape($busy)."'";
4511
		$sql.= ", '".$this->db->escape($mandatory)."'";
4512
		$sql.= ")";
4513
4514
		dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
4515
		if ($this->db->query($sql))
4516
		{
4517
			$this->db->commit();
4518
			return 1;
4519
		}
4520
		else
4521
		{
4522
			$this->error=$this->db->lasterror();
4523
			$this->db->rollback();
4524
			return  0;
4525
		}
4526
	}
4527
4528
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4529
	/**
4530
	 *    Delete a link to resource line
4531
	 *
4532
	 *    @param	int		$rowid			Id of resource line to delete
4533
	 *    @param	int		$element		element name (for trigger) TODO: use $this->element into commonobject class
4534
	 *    @param	int		$notrigger		Disable all triggers
4535
	 *    @return   int						>0 if OK, <0 if KO
4536
	 */
4537
	public function delete_resource($rowid, $element, $notrigger = 0)
4538
	{
4539
        // phpcs:enable
4540
		global $user;
4541
4542
		$this->db->begin();
4543
4544
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources";
4545
		$sql.= " WHERE rowid=".$rowid;
4546
4547
		dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
4548
4549
		$resql=$this->db->query($sql);
4550
		if (! $resql)
4551
		{
4552
			$this->error=$this->db->lasterror();
4553
			$this->db->rollback();
4554
			return -1;
4555
		}
4556
		else
4557
		{
4558
			if (! $notrigger)
4559
			{
4560
				$result=$this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
4561
				if ($result < 0) { $this->db->rollback(); return -1; }
4562
			}
4563
			$this->db->commit();
4564
			return 1;
4565
		}
4566
	}
4567
4568
4569
	/**
4570
	 * Overwrite magic function to solve problem of cloning object that are kept as references
4571
	 *
4572
	 * @return void
4573
	 */
4574
	public function __clone()
4575
	{
4576
		// Force a copy of this->lines, otherwise it will point to same object.
4577
		if (isset($this->lines) && is_array($this->lines))
4578
		{
4579
			$nboflines=count($this->lines);
4580
			for($i=0; $i < $nboflines; $i++)
4581
			{
4582
				$this->lines[$i] = clone $this->lines[$i];
4583
			}
4584
		}
4585
	}
4586
4587
	/**
4588
	 * Common function for all objects extending CommonObject for generating documents
4589
	 *
4590
	 * @param 	string 		$modelspath 	Relative folder where generators are placed
4591
	 * @param 	string 		$modele 		Generator to use. Caller must set it to obj->modelpdf or GETPOST('modelpdf','alpha') for example.
4592
	 * @param 	Translate 	$outputlangs 	Output language to use
4593
	 * @param 	int 		$hidedetails 	1 to hide details. 0 by default
4594
	 * @param 	int 		$hidedesc 		1 to hide product description. 0 by default
4595
	 * @param 	int 		$hideref 		1 to hide product reference. 0 by default
4596
	 * @param   null|array  $moreparams     Array to provide more information
4597
	 * @return 	int 						>0 if OK, <0 if KO
4598
	 * @see	addFileIntoDatabaseIndex()
4599
	 */
4600
	protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
4601
	{
4602
		global $conf, $langs, $user, $hookmanager;
4603
4604
		$srctemplatepath='';
4605
4606
		$parameters = array('modelspath'=>$modelspath,'modele'=>$modele,'outputlangs'=>$outputlangs,'hidedetails'=>$hidedetails,'hidedesc'=>$hidedesc,'hideref'=>$hideref, 'moreparams'=>$moreparams);
4607
		$reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $action seems to be never defined.
Loading history...
4608
4609
		if(empty($reshook))
4610
		{
4611
		dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs)?$outputlangs->defaultlang:'null'));
4612
4613
		// Increase limit for PDF build
4614
		$err=error_reporting();
4615
		error_reporting(0);
4616
		@set_time_limit(120);
4617
		error_reporting($err);
4618
4619
		// If selected model is a filename template (then $modele="modelname" or "modelname:filename")
4620
		$tmp=explode(':', $modele, 2);
4621
		if (! empty($tmp[1]))
4622
		{
4623
			$modele=$tmp[0];
4624
			$srctemplatepath=$tmp[1];
4625
		}
4626
4627
		// Search template files
4628
		$file=''; $classname=''; $filefound=0;
4629
		$dirmodels=array('/');
4630
		if (is_array($conf->modules_parts['models'])) $dirmodels=array_merge($dirmodels, $conf->modules_parts['models']);
4631
		foreach($dirmodels as $reldir)
4632
		{
4633
			foreach(array('doc','pdf') as $prefix)
4634
			{
4635
				if (in_array(get_class($this), array('Adherent'))) $file = $prefix."_".$modele.".class.php";     // Member module use prefix_module.class.php
4636
				else $file = $prefix."_".$modele.".modules.php";
4637
4638
				// On verifie l'emplacement du modele
4639
				$file=dol_buildpath($reldir.$modelspath.$file, 0);
4640
				if (file_exists($file))
4641
				{
4642
					$filefound=1;
4643
					$classname=$prefix.'_'.$modele;
4644
					break;
4645
				}
4646
			}
4647
			if ($filefound) break;
4648
		}
4649
4650
		// If generator was found
4651
		if ($filefound)
4652
		{
4653
			global $db;  // Required to solve a conception default making an include of code using $db instead of $this->db just after.
4654
4655
			require_once $file;
4656
4657
			$obj = new $classname($this->db);
4658
4659
			// If generator is ODT, we must have srctemplatepath defined, if not we set it.
4660
			if ($obj->type == 'odt' && empty($srctemplatepath))
4661
			{
4662
				$varfortemplatedir=$obj->scandir;
4663
				if ($varfortemplatedir && ! empty($conf->global->$varfortemplatedir))
4664
				{
4665
					$dirtoscan=$conf->global->$varfortemplatedir;
4666
4667
					$listoffiles=array();
4668
4669
					// Now we add first model found in directories scanned
4670
					$listofdir=explode(',', $dirtoscan);
4671
					foreach($listofdir as $key => $tmpdir)
4672
					{
4673
						$tmpdir=trim($tmpdir);
4674
						$tmpdir=preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
4675
						if (! $tmpdir) { unset($listofdir[$key]); continue; }
4676
						if (is_dir($tmpdir))
4677
						{
4678
							$tmpfiles=dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
4679
							if (count($tmpfiles)) $listoffiles=array_merge($listoffiles, $tmpfiles);
4680
						}
4681
					}
4682
4683
					if (count($listoffiles))
4684
					{
4685
						foreach($listoffiles as $record)
4686
						{
4687
							$srctemplatepath=$record['fullname'];
4688
							break;
4689
						}
4690
					}
4691
				}
4692
4693
				if (empty($srctemplatepath))
4694
				{
4695
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
4696
					return -1;
4697
				}
4698
			}
4699
4700
			if ($obj->type == 'odt' && ! empty($srctemplatepath))
4701
			{
4702
				if (! dol_is_file($srctemplatepath))
4703
				{
4704
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
4705
					return -1;
4706
				}
4707
			}
4708
4709
			// We save charset_output to restore it because write_file can change it if needed for
4710
			// output format that does not support UTF8.
4711
			$sav_charset_output=$outputlangs->charset_output;
4712
4713
			if (in_array(get_class($this), array('Adherent')))
4714
			{
4715
				$arrayofrecords = array();   // The write_file of templates of adherent class need this var
4716
				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams);
4717
			}
4718
			else
4719
			{
4720
				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
4721
			}
4722
			// After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
4723
4724
			if ($resultwritefile > 0)
4725
			{
4726
				$outputlangs->charset_output=$sav_charset_output;
4727
4728
				// We delete old preview
4729
				require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4730
				dol_delete_preview($this);
4731
4732
				// Index file in database
4733
				if (! empty($obj->result['fullpath']))
4734
				{
4735
					$destfull = $obj->result['fullpath'];
4736
					$upload_dir = dirname($destfull);
4737
					$destfile = basename($destfull);
4738
					$rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
4739
4740
					if (! preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir))     // If not a tmp dir
4741
					{
4742
						$filename = basename($destfile);
4743
						$rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
4744
						$rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
4745
4746
						include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
4747
						$ecmfile=new EcmFiles($this->db);
4748
						$result = $ecmfile->fetch(0, '', ($rel_dir?$rel_dir.'/':'').$filename);
4749
4750
						// Set the public "share" key
4751
						$setsharekey = false;
4752
						if ($this->element == 'propal')
4753
						{
4754
							$useonlinesignature = $conf->global->MAIN_FEATURES_LEVEL;	// Replace this with 1 when feature to make online signature is ok
4755
							if ($useonlinesignature) $setsharekey=true;
4756
							if (! empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4757
						}
4758
						if ($this->element == 'commande' && ! empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) {
4759
							$setsharekey=true;
4760
						}
4761
						if ($this->element == 'facture' && ! empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) {
4762
							$setsharekey=true;
4763
						}
4764
						if ($this->element == 'bank_account' && ! empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) {
4765
							$setsharekey=true;
4766
						}
4767
4768
						if ($setsharekey)
4769
						{
4770
							if (empty($ecmfile->share))	// Because object not found or share not set yet
4771
							{
4772
								require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
4773
								$ecmfile->share = getRandomPassword(true);
4774
							}
4775
						}
4776
4777
						if ($result > 0)
4778
						{
4779
							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4780
							$ecmfile->fullpath_orig = '';
4781
							$ecmfile->gen_or_uploaded = 'generated';
4782
							$ecmfile->description = '';    // indexed content
4783
							$ecmfile->keyword = '';        // keyword content
4784
							$result = $ecmfile->update($user);
4785
							if ($result < 0)
4786
							{
4787
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4788
							}
4789
						}
4790
						else
4791
						{
4792
							$ecmfile->entity = $conf->entity;
4793
							$ecmfile->filepath = $rel_dir;
4794
							$ecmfile->filename = $filename;
4795
							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4796
							$ecmfile->fullpath_orig = '';
4797
							$ecmfile->gen_or_uploaded = 'generated';
4798
							$ecmfile->description = '';    // indexed content
4799
							$ecmfile->keyword = '';        // keyword content
4800
							$ecmfile->src_object_type = $this->table_element;
4801
							$ecmfile->src_object_id   = $this->id;
4802
4803
							$result = $ecmfile->create($user);
4804
							if ($result < 0)
4805
							{
4806
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4807
							}
4808
						}
4809
4810
						/*$this->result['fullname']=$destfull;
4811
						$this->result['filepath']=$ecmfile->filepath;
4812
						$this->result['filename']=$ecmfile->filename;*/
4813
						//var_dump($obj->update_main_doc_field);exit;
4814
4815
						// Update the last_main_doc field into main object (if documenent generator has property ->update_main_doc_field set)
4816
						$update_main_doc_field=0;
4817
						if (! empty($obj->update_main_doc_field)) $update_main_doc_field=1;
4818
						if ($update_main_doc_field && ! empty($this->table_element))
4819
						{
4820
							$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath.'/'.$ecmfile->filename)."'";
4821
							$sql.= ' WHERE rowid = '.$this->id;
4822
4823
							$resql = $this->db->query($sql);
4824
							if (! $resql) dol_print_error($this->db);
4825
							else
4826
							{
4827
							    $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
4828
							}
4829
						}
4830
					}
4831
				}
4832
				else
4833
				{
4834
					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);
4835
				}
4836
4837
				// Success in building document. We build meta file.
4838
				dol_meta_create($this);
4839
4840
				return 1;
4841
			}
4842
			else
4843
			{
4844
				$outputlangs->charset_output=$sav_charset_output;
4845
				dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors);
4846
				return -1;
4847
			}
4848
		}
4849
		else
4850
		{
4851
			$this->error=$langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists", $file);
4852
			dol_print_error('', $this->error);
4853
			return -1;
4854
		}
4855
		}
4856
		else return $reshook;
4857
	}
4858
4859
	/**
4860
	 *  Build thumb
4861
	 *  @TODO Move this into files.lib.php
4862
	 *
4863
	 *  @param      string	$file           Path file in UTF8 to original file to create thumbs from.
4864
	 *	@return		void
4865
	 */
4866
	public function addThumbs($file)
4867
	{
4868
		global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality;
4869
4870
		require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';		// This define also $maxwidthsmall, $quality, ...
4871
4872
		$file_osencoded=dol_osencode($file);
4873
		if (file_exists($file_osencoded))
4874
		{
4875
			// Create small thumbs for company (Ratio is near 16/9)
4876
			// Used on logon for example
4877
			vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
4878
4879
			// Create mini thumbs for company (Ratio is near 16/9)
4880
			// Used on menu or for setup page for example
4881
			vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
4882
		}
4883
	}
4884
4885
4886
	/* Functions common to commonobject and commonobjectline */
4887
4888
	/* For default values */
4889
4890
	/**
4891
	 * Return the default value to use for a field when showing the create form of object.
4892
	 * Return values in this order:
4893
	 * 1) If parameter is available into POST, we return it first.
4894
	 * 2) If not but an alternate value was provided as parameter of function, we return it.
4895
	 * 3) If not but a constant $conf->global->OBJECTELEMENT_FIELDNAME is set, we return it (It is better to use the dedicated table).
4896
	 * 4) Return value found into database (TODO No yet implemented)
4897
	 *
4898
	 * @param   string              $fieldname          Name of field
4899
	 * @param   string              $alternatevalue     Alternate value to use
4900
	 * @return  string|string[]                         Default value (can be an array if the GETPOST return an array)
4901
	 **/
4902
	public function getDefaultCreateValueFor($fieldname, $alternatevalue = null)
4903
	{
4904
		global $conf, $_POST;
4905
4906
		// If param here has been posted, we use this value first.
4907
		if (isset($_POST[$fieldname])) return GETPOST($fieldname, 2);
4908
4909
		if (isset($alternatevalue)) return $alternatevalue;
4910
4911
		$newelement=$this->element;
4912
		if ($newelement == 'facture') $newelement='invoice';
4913
		if ($newelement == 'commande') $newelement='order';
4914
		if (empty($newelement))
4915
		{
4916
			dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
4917
			return '';
4918
		}
4919
4920
		$keyforfieldname=strtoupper($newelement.'_DEFAULT_'.$fieldname);
4921
		//var_dump($keyforfieldname);
4922
		if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname;
4923
4924
		// TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
4925
	}
4926
4927
4928
	/* For triggers */
4929
4930
4931
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4932
	/**
4933
	 * Call trigger based on this instance.
4934
	 * Some context information may also be provided into array property this->context.
4935
	 * NB:  Error from trigger are stacked in interface->errors
4936
	 * NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction.
4937
	 *
4938
	 * @param   string    $trigger_name   trigger's name to execute
4939
	 * @param   User      $user           Object user
4940
	 * @return  int                       Result of run_triggers
4941
	 */
4942
	public function call_trigger($trigger_name, $user)
4943
	{
4944
		// phpcs:enable
4945
		global $langs,$conf;
4946
4947
		include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
4948
		$interface=new Interfaces($this->db);
4949
		$result=$interface->run_triggers($trigger_name, $this, $user, $langs, $conf);
4950
4951
		if ($result < 0)
4952
		{
4953
			if (!empty($this->errors))
4954
			{
4955
				$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.
4956
			}
4957
			else
4958
			{
4959
				$this->errors=$interface->errors;
4960
			}
4961
		}
4962
		return $result;
4963
	}
4964
4965
4966
	/* Functions for extrafields */
4967
4968
4969
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4970
	/**
4971
	 *  Function to get extra fields of an object into $this->array_options
4972
	 *  This method is in most cases called by method fetch of objects but you can call it separately.
4973
	 *
4974
	 *  @param	int		$rowid			Id of line. Use the id of object if not defined. Deprecated. Function must be called without parameters.
4975
	 *  @param  array	$optionsArray   Array resulting of call of extrafields->fetch_name_optionals_label(). Deprecated. Function must be called without parameters.
4976
	 *  @return	int						<0 if error, 0 if no values of extrafield to find nor found, 1 if an attribute is found and value loaded
4977
	 */
4978
	public function fetch_optionals($rowid = null, $optionsArray = null)
4979
	{
4980
		// phpcs:enable
4981
		global $extrafields;
4982
4983
		if (empty($rowid)) $rowid=$this->id;
4984
4985
		// To avoid SQL errors. Probably not the better solution though
4986
		if (!$this->table_element) {
4987
			return 0;
4988
		}
4989
4990
		$this->array_options=array();
4991
4992
		if (! is_array($optionsArray))
4993
		{
4994
			// If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
4995
			if (! isset($extrafields) || ! is_object($extrafields))
4996
			{
4997
				require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4998
				$extrafields = new ExtraFields($this->db);
4999
			}
5000
5001
			// Load array of extrafields for elementype = $this->table_element
5002
			if (empty($extrafields->attributes[$this->table_element]['loaded']))
5003
			{
5004
				$extrafields->fetch_name_optionals_label($this->table_element);
5005
			}
5006
			$optionsArray = (! empty($extrafields->attributes[$this->table_element]['label'])?$extrafields->attributes[$this->table_element]['label']:null);
5007
		}
5008
		else
5009
		{
5010
			global $extrafields;
5011
			dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
5012
		}
5013
5014
		$table_element = $this->table_element;
5015
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
5016
5017
		// Request to get complementary values
5018
		if (is_array($optionsArray) && count($optionsArray) > 0)
5019
		{
5020
			$sql = "SELECT rowid";
5021
			foreach ($optionsArray as $name => $label)
5022
			{
5023
				if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate')
5024
				{
5025
					$sql.= ", ".$name;
5026
				}
5027
			}
5028
			$sql.= " FROM ".MAIN_DB_PREFIX.$table_element."_extrafields";
5029
			$sql.= " WHERE fk_object = ".$rowid;
5030
5031
			//dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG);		// Too verbose
5032
			$resql=$this->db->query($sql);
5033
			if ($resql)
5034
			{
5035
				$this->array_options = array();
5036
				$numrows=$this->db->num_rows($resql);
5037
				if ($numrows)
5038
				{
5039
					$tab = $this->db->fetch_array($resql);
5040
5041
					foreach ($tab as $key => $value)
5042
					{
5043
						// 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)
5044
						if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && ! is_int($key))
5045
						{
5046
							// we can add this attribute to object
5047
							if (! empty($extrafields) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime')))
5048
							{
5049
								//var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
5050
								$this->array_options["options_".$key]=$this->db->jdate($value);
5051
							}
5052
							else
5053
							{
5054
								$this->array_options["options_".$key]=$value;
5055
							}
5056
5057
							//var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
5058
						}
5059
					}
5060
5061
					// If field is a computed field, value must become result of compute
5062
					foreach ($tab as $key => $value) {
5063
						if (! empty($extrafields) && !empty($extrafields->attributes[$this->table_element]['computed'][$key]))
5064
						{
5065
							$this->array_options["options_".$key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0);
5066
						}
5067
					}
5068
				}
5069
5070
				$this->db->free($resql);
5071
5072
				if ($numrows) return $numrows;
5073
				else return 0;
5074
			}
5075
			else
5076
			{
5077
				dol_print_error($this->db);
5078
				return -1;
5079
			}
5080
		}
5081
		return 0;
5082
	}
5083
5084
	/**
5085
	 *	Delete all extra fields values for the current object.
5086
	 *
5087
	 *  @return	int		<0 if KO, >0 if OK
5088
	 */
5089
	public function deleteExtraFields()
5090
	{
5091
		$this->db->begin();
5092
5093
		$table_element = $this->table_element;
5094
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
5095
5096
		$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
5097
		dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
5098
		$resql=$this->db->query($sql_del);
5099
		if (! $resql)
5100
		{
5101
			$this->error=$this->db->lasterror();
5102
			$this->db->rollback();
5103
			return -1;
5104
		}
5105
		else
5106
		{
5107
			$this->db->commit();
5108
			return 1;
5109
		}
5110
	}
5111
5112
	/**
5113
	 *	Add/Update all extra fields values for the current object.
5114
	 *  Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
5115
	 *  This function delete record with all extrafields and insert them again from the array $this->array_options.
5116
	 *
5117
	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
5118
	 *  @param	User		$userused		Object user
5119
	 *  @return int 						-1=error, O=did nothing, 1=OK
5120
	 *  @see updateExtraField(), setValueFrom()
5121
	 */
5122
	public function insertExtraFields($trigger = '', $userused = null)
5123
	{
5124
		global $conf,$langs,$user;
5125
5126
		if (empty($userused)) $userused=$user;
5127
5128
		$error=0;
5129
5130
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
5131
5132
		if (! empty($this->array_options))
5133
		{
5134
			// Check parameters
5135
			$langs->load('admin');
5136
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5137
			$extrafields = new ExtraFields($this->db);
5138
			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
5139
5140
			//Eliminate copied source object extra_fields that do not exist in target object
5141
			$new_array_options=array();
5142
			foreach ($this->array_options as $key => $value) {
5143
				if (in_array(substr($key, 8), array_keys($target_extrafields)))	// We remove the 'options_' from $key for test
5144
					$new_array_options[$key] = $value;
5145
				elseif (in_array($key, array_keys($target_extrafields)))		// We test on $key that does not contains the 'options_' prefix
5146
					$new_array_options['options_'.$key] = $value;
5147
			}
5148
5149
			foreach($new_array_options as $key => $value)
5150
			{
5151
			   	$attributeKey      = substr($key, 8);   // Remove 'options_' prefix
5152
			   	$attributeType     = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
5153
			   	$attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
5154
			   	$attributeParam    = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
5155
			   	$attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
5156
				$attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
5157
5158
5159
			   	if ($attributeRequired)
5160
			   	{
5161
			   		$mandatorypb=false;
5162
			   		if ($attributeType == 'link' && $this->array_options[$key] == '-1') $mandatorypb=true;
5163
			   		if ($this->array_options[$key] === '') $mandatorypb=true;
5164
			   		if ($mandatorypb)
5165
			   		{
5166
			   		    dol_syslog("Mandatory extra field ".$key." is empty");
5167
			   			$this->errors[]=$langs->trans('ErrorFieldRequired', $attributeLabel);
5168
			   			return -1;
5169
			   		}
5170
			   	}
5171
5172
				//dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5173
				//dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5174
5175
				if (!empty($attrfieldcomputed))
5176
				{
5177
					if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS))
5178
					{
5179
						$value = dol_eval($attrfieldcomputed, 1, 0);
5180
					    dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
5181
					    $new_array_options[$key] = $value;
5182
					}
5183
					else
5184
					{
5185
						$new_array_options[$key] = null;
5186
					}
5187
				}
5188
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
			   				$new_array_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
							$new_array_options[$key] = null;
5214
						}
5215
						//dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5216
						$new_array_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 'password':
5225
			   			$algo='';
5226
			   			if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']))
5227
			   			{
5228
			   				// If there is an encryption choice, we use it to crypt data before insert
5229
			   				$tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
5230
			   				$algo=reset($tmparrays);
5231
			   				if ($algo != '')
5232
			   				{
5233
			   					//global $action;		// $action may be 'create', 'update', 'update_extras'...
5234
			   					//var_dump($action);
5235
			   					//var_dump($this->oldcopy);exit;
5236
			   					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
5237
			   					{
5238
			   						//var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
5239
				   					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.
5240
				   					{
5241
				   						$new_array_options[$key] = $this->array_options[$key];	// Value is kept
5242
				   					}
5243
									else
5244
									{
5245
										// var_dump($algo);
5246
										$newvalue = dol_hash($this->array_options[$key], $algo);
5247
										$new_array_options[$key] = $newvalue;
5248
									}
5249
			   					}
5250
			   					else
5251
			   					{
5252
			   						$new_array_options[$key] = $this->array_options[$key];	// Value is kept
5253
			   					}
5254
			   				}
5255
			   			}
5256
			   			else	// Common usage
5257
			   			{
5258
			   				$new_array_options[$key] = $this->array_options[$key];
5259
			   			}
5260
			   			break;
5261
			   		case 'price':
5262
						$new_array_options[$key] = price2num($this->array_options[$key]);
5263
						break;
5264
					case 'date':
5265
					case 'datetime':
5266
						// If data is a string instead of a timestamp, we convert it
5267
						if (! is_int($this->array_options[$key])) {
5268
							$this->array_options[$key] = strtotime($this->array_options[$key]);
5269
						}
5270
						$new_array_options[$key] = $this->db->idate($this->array_options[$key]);
5271
						break;
5272
		   			case 'link':
5273
						$param_list=array_keys($attributeParam['options']);
5274
						// 0 : ObjectName
5275
						// 1 : classPath
5276
						$InfoFieldList = explode(":", $param_list[0]);
5277
						dol_include_once($InfoFieldList[1]);
5278
						if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
5279
						{
5280
							if ($value == '-1')	// -1 is key for no defined in combo list of objects
5281
							{
5282
								$new_array_options[$key]='';
5283
							}
5284
							elseif ($value)
5285
							{
5286
								$object = new $InfoFieldList[0]($this->db);
5287
								if (is_numeric($value)) $res=$object->fetch($value);
5288
								else $res=$object->fetch('', $value);
5289
5290
								if ($res > 0) $new_array_options[$key]=$object->id;
5291
								else
5292
								{
5293
									$this->error="Id/Ref '".$value."' for object '".$object->element."' not found";
5294
									$this->db->rollback();
5295
									return -1;
5296
								}
5297
							}
5298
						}
5299
						else
5300
						{
5301
							dol_syslog('Error bad setup of extrafield', LOG_WARNING);
5302
						}
5303
						break;
5304
			   	}
5305
			}
5306
5307
			$this->db->begin();
5308
5309
			$table_element = $this->table_element;
5310
			if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
5311
5312
			dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
5313
5314
			$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
5315
			$this->db->query($sql_del);
5316
5317
			$sql = "INSERT INTO ".MAIN_DB_PREFIX.$table_element."_extrafields (fk_object";
5318
			foreach($new_array_options as $key => $value)
5319
			{
5320
				$attributeKey = substr($key, 8);   // Remove 'options_' prefix
5321
				// Add field of attribut
5322
				if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator
5323
					$sql.=",".$attributeKey;
5324
			}
5325
			// We must insert a default value for fields for other entities that are mandatory to avoid not null error
5326
			if (is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']))
5327
			{
5328
    			foreach($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as  $tmpkey => $tmpval)
5329
    			{
5330
    			    if (! isset($extrafields->attributes[$this->table_element]['type'][$tmpkey]))    // If field not already added previously
5331
    			    {
5332
    			        $sql.=",".$tmpkey;
5333
    			    }
5334
    			}
5335
			}
5336
			$sql .= ") VALUES (".$this->id;
5337
5338
			foreach($new_array_options as $key => $value)
5339
			{
5340
				$attributeKey = substr($key, 8);   // Remove 'options_' prefix
5341
				// Add field of attribute
5342
				if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator)
5343
				{
5344
					if ($new_array_options[$key] != '')
5345
					{
5346
						$sql.=",'".$this->db->escape($new_array_options[$key])."'";
5347
					}
5348
					else
5349
					{
5350
						$sql.=",null";
5351
					}
5352
				}
5353
			}
5354
			// We must insert a default value for fields for other entities that are mandatory to avoid not null error
5355
			if (is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']))
5356
			{
5357
			    foreach($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as  $tmpkey => $tmpval)
5358
    			{
5359
    			    if (! isset($extrafields->attributes[$this->table_element]['type'][$tmpkey]))    // If field not already added previously
5360
    			    {
5361
                        if (in_array($tmpval, array('int', 'double'))) $sql.=", 0";
5362
                        else $sql.=", ''";
5363
    			    }
5364
    			}
5365
			}
5366
5367
			$sql.=")";
5368
5369
			$resql = $this->db->query($sql);
5370
5371
			if (! $resql)
5372
			{
5373
				$this->error=$this->db->lasterror();
5374
				$error++;
5375
			}
5376
5377
			if (! $error && $trigger)
5378
			{
5379
				// Call trigger
5380
				$this->context=array('extrafieldaddupdate'=>1);
5381
				$result=$this->call_trigger($trigger, $userused);
5382
				if ($result < 0) $error++;
5383
				// End call trigger
5384
			}
5385
5386
			if ($error)
5387
			{
5388
				$this->db->rollback();
5389
				return -1;
5390
			}
5391
			else
5392
			{
5393
				$this->db->commit();
5394
				return 1;
5395
			}
5396
		}
5397
		else return 0;
5398
	}
5399
5400
	/**
5401
	 *	Update an extra field value for the current object.
5402
	 *  Data to describe values to update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
5403
	 *
5404
	 *  @param  string      $key    		Key of the extrafield (without starting 'options_')
5405
	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
5406
	 *  @param	User		$userused		Object user
5407
	 *  @return int                 		-1=error, O=did nothing, 1=OK
5408
	 *  @see setValueFrom(), insertExtraFields()
5409
	 */
5410
	public function updateExtraField($key, $trigger = null, $userused = null)
5411
	{
5412
		global $conf,$langs,$user;
5413
5414
		if (empty($userused)) $userused=$user;
5415
5416
		$error=0;
5417
5418
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
5419
5420
		if (! empty($this->array_options) && isset($this->array_options["options_".$key]))
5421
		{
5422
			// Check parameters
5423
			$langs->load('admin');
5424
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5425
			$extrafields = new ExtraFields($this->db);
5426
			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
5427
5428
			$value=$this->array_options["options_".$key];
5429
5430
			$attributeType     = $extrafields->attributes[$this->table_element]['type'][$key];
5431
			$attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$key];
5432
			$attributeParam    = $extrafields->attributes[$this->table_element]['param'][$key];
5433
			$attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
5434
5435
			//dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5436
			//dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5437
5438
			switch ($attributeType)
5439
			{
5440
				case 'int':
5441
					if (!is_numeric($value) && $value!='')
5442
					{
5443
						$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5444
						return -1;
5445
					}
5446
					elseif ($value=='')
5447
					{
5448
						$this->array_options["options_".$key] = null;
5449
					}
5450
					break;
5451
				case 'double':
5452
					$value = price2num($value);
5453
					if (!is_numeric($value) && $value!='')
5454
					{
5455
						dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5456
						$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5457
						return -1;
5458
					}
5459
					elseif ($value=='')
5460
					{
5461
						$this->array_options["options_".$key] = null;
5462
					}
5463
					//dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5464
					$this->array_options["options_".$key] = $value;
5465
					break;
5466
			 	/*case 'select':	// Not required, we chosed value='0' for undefined values
5467
             		if ($value=='-1')
5468
             		{
5469
             			$this->array_options[$key] = null;
5470
             		}
5471
             		break;*/
5472
				case 'price':
5473
					$this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
5474
					break;
5475
				case 'date':
5476
					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5477
					break;
5478
				case 'datetime':
5479
					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5480
					break;
5481
				case 'link':
5482
					$param_list=array_keys($attributeParam['options']);
5483
					// 0 : ObjectName
5484
					// 1 : classPath
5485
					$InfoFieldList = explode(":", $param_list[0]);
5486
					dol_include_once($InfoFieldList[1]);
5487
					if ($value)
5488
					{
5489
						$object = new $InfoFieldList[0]($this->db);
5490
						$object->fetch(0, $value);
5491
						$this->array_options["options_".$key]=$object->id;
5492
					}
5493
					break;
5494
			}
5495
5496
			$this->db->begin();
5497
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key."='".$this->db->escape($this->array_options["options_".$key])."'";
5498
			$sql .= " WHERE fk_object = ".$this->id;
5499
			$resql = $this->db->query($sql);
5500
			if (! $resql)
5501
			{
5502
				$error++;
5503
				$this->error=$this->db->lasterror();
5504
			}
5505
5506
			if (! $error && $trigger)
5507
			{
5508
				// Call trigger
5509
				$this->context=array('extrafieldupdate'=>1);
5510
				$result=$this->call_trigger($trigger, $userused);
5511
				if ($result < 0) $error++;
5512
				// End call trigger
5513
			}
5514
5515
			if ($error)
5516
			{
5517
				dol_syslog(get_class($this) . "::".__METHOD__ . $this->error, LOG_ERR);
5518
				$this->db->rollback();
5519
				return -1;
5520
			}
5521
			else
5522
			{
5523
				$this->db->commit();
5524
				return 1;
5525
			}
5526
		}
5527
		else return 0;
5528
	}
5529
5530
5531
	/**
5532
	 * Return HTML string to put an input field into a page
5533
	 * Code very similar with showInputField of extra fields
5534
	 *
5535
	 * @param  array   		$val	       Array of properties for field to show
5536
	 * @param  string  		$key           Key of attribute
5537
	 * @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)
5538
	 * @param  string  		$moreparam     To add more parameters on html input tag
5539
	 * @param  string  		$keysuffix     Prefix string to add into name and id of field (can be used to avoid duplicate names)
5540
	 * @param  string  		$keyprefix     Suffix string to add into name and id of field (can be used to avoid duplicate names)
5541
	 * @param  string|int	$morecss       Value for css to define style/length of field. May also be a numeric.
5542
	 * @return string
5543
	 */
5544
	public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0)
5545
	{
5546
		global $conf,$langs,$form;
5547
5548
		if (! is_object($form))
5549
		{
5550
			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5551
			$form=new Form($this->db);
5552
		}
5553
5554
		$val=$this->fields[$key];
5555
5556
		$out='';
5557
        $type='';
5558
        $param = array();
5559
        $param['options']=array();
5560
        $size =$this->fields[$key]['size'];
5561
        // Because we work on extrafields
5562
        if(preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)){
5563
            $param['options']=array($reg[1].':'.$reg[2]=>'N');
5564
            $type ='link';
5565
        } elseif(preg_match('/^link:(.*):(.*)/i', $val['type'], $reg)) {
5566
            $param['options']=array($reg[1].':'.$reg[2]=>'N');
5567
            $type ='link';
5568
        } elseif(preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
5569
            $param['options']=array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4]=>'N');
5570
            $type ='sellist';
5571
        } elseif(preg_match('/varchar\((\d+)\)/', $val['type'], $reg)) {
5572
            $param['options']=array();
5573
            $type ='varchar';
5574
            $size=$reg[1];
5575
        } elseif(preg_match('/varchar/', $val['type'])) {
5576
            $param['options']=array();
5577
            $type ='varchar';
5578
        } elseif(is_array($this->fields[$key]['arrayofkeyval'])) {
5579
            $param['options']=$this->fields[$key]['arrayofkeyval'];
5580
            $type ='select';
5581
        } else {
5582
            $param['options']=array();
5583
            $type =$this->fields[$key]['type'];
5584
        }
5585
5586
		$label=$this->fields[$key]['label'];
5587
		//$elementtype=$this->fields[$key]['elementtype'];	// Seems not used
5588
		$default=$this->fields[$key]['default'];
5589
		$computed=$this->fields[$key]['computed'];
5590
		$unique=$this->fields[$key]['unique'];
5591
		$required=$this->fields[$key]['required'];
5592
5593
		$langfile=$this->fields[$key]['langfile'];
5594
		$list=$this->fields[$key]['list'];
5595
		$hidden=(in_array(abs($this->fields[$key]['visible']), array(0,2)) ? 1 : 0);
5596
5597
		$objectid = $this->id;
5598
5599
5600
		if ($computed)
5601
		{
5602
			if (! preg_match('/^search_/', $keyprefix)) return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
5603
			else return '';
5604
		}
5605
5606
5607
		// Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
5608
		if (empty($morecss) && ! empty($val['css']))
5609
		{
5610
		    $morecss = $val['css'];
5611
		}
5612
		elseif (empty($morecss))
5613
		{
5614
			if ($type == 'date')
5615
			{
5616
				$morecss = 'minwidth100imp';
5617
			}
5618
			elseif ($type == 'datetime')
5619
			{
5620
				$morecss = 'minwidth200imp';
5621
			}
5622
			elseif (in_array($type, array('int','integer','price')) || preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type))
5623
			{
5624
				$morecss = 'maxwidth75';
5625
			} elseif ($type == 'url') {
5626
				$morecss='minwidth400';
5627
			}
5628
			elseif ($type == 'boolean')
5629
			{
5630
				$morecss='';
5631
			}
5632
			else
5633
			{
5634
				if (round($size) < 12)
5635
				{
5636
					$morecss = 'minwidth100';
5637
				}
5638
				elseif (round($size) <= 48)
5639
				{
5640
					$morecss = 'minwidth200';
5641
				}
5642
				else
5643
				{
5644
					$morecss = 'minwidth400';
5645
				}
5646
			}
5647
		}
5648
5649
		if (in_array($type, array('date','datetime')))
5650
		{
5651
			$tmp=explode(',', $size);
5652
			$newsize=$tmp[0];
5653
5654
			$showtime = in_array($type, array('datetime')) ? 1 : 0;
5655
5656
			// Do not show current date when field not required (see selectDate() method)
5657
			if (!$required && $value == '') $value = '-1';
5658
5659
			// TODO Must also support $moreparam
5660
			$out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
5661
		}
5662
		elseif (in_array($type, array('int','integer')))
5663
		{
5664
			$tmp=explode(',', $size);
5665
			$newsize=$tmp[0];
5666
			$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:'').'>';
5667
		}
5668
		elseif (in_array($type, array('real')))
5669
		{
5670
		    $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5671
		}
5672
		elseif (preg_match('/varchar/', $type))
5673
		{
5674
			$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:'').'>';
5675
		}
5676
		elseif (in_array($type, array('mail', 'phone', 'url')))
5677
		{
5678
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5679
		}
5680
		elseif ($type == 'text')
5681
		{
5682
			if (! preg_match('/search_/', $keyprefix))		// If keyprefix is search_ or search_options_, we must just use a simple text field
5683
			{
5684
				require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5685
				$doleditor=new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
5686
				$out=$doleditor->Create(1);
5687
			}
5688
			else
5689
			{
5690
				$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5691
			}
5692
		}
5693
		elseif ($type == 'html')
5694
		{
5695
			if (! preg_match('/search_/', $keyprefix))		// If keyprefix is search_ or search_options_, we must just use a simple text field
5696
			{
5697
				require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5698
				$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%');
5699
				$out=$doleditor->Create(1);
5700
			}
5701
			else
5702
			{
5703
				$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5704
			}
5705
		}
5706
		elseif ($type == 'boolean')
5707
		{
5708
			$checked='';
5709
			if (!empty($value)) {
5710
				$checked=' checked value="1" ';
5711
			} else {
5712
				$checked=' value="1" ';
5713
			}
5714
			$out='<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam?$moreparam:'').'>';
5715
		}
5716
		elseif ($type == 'price')
5717
		{
5718
			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5719
				$value=price($value);
5720
			}
5721
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> '.$langs->getCurrencySymbol($conf->currency);
5722
		}
5723
		elseif (preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type))
5724
		{
5725
			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5726
				$value=price($value);
5727
			}
5728
			$out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> ';
5729
		}
5730
		elseif ($type == 'select')
5731
		{
5732
			$out = '';
5733
			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5734
			{
5735
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5736
				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5737
			}
5738
5739
			$out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5740
                if((! isset($this->fields[$key]['default'])) ||($this->fields[$key]['notnull']!=1))$out.='<option value="0">&nbsp;</option>';
5741
			foreach ($param['options'] as $key => $val)
0 ignored issues
show
introduced by
$val is overwriting one of the parameters of this function.
Loading history...
introduced by
$key is overwriting one of the parameters of this function.
Loading history...
5742
			{
5743
				if ((string) $key == '') continue;
5744
				list($val, $parent) = explode('|', $val);
5745
				$out.='<option value="'.$key.'"';
5746
				$out.= (((string) $value == (string) $key)?' selected':'');
5747
				$out.= (!empty($parent)?' parent="'.$parent.'"':'');
5748
				$out.='>'.$val.'</option>';
5749
			}
5750
			$out.='</select>';
5751
		}
5752
		elseif ($type == 'sellist')
5753
		{
5754
			$out = '';
5755
			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5756
			{
5757
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5758
				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5759
			}
5760
5761
			$out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5762
			if (is_array($param['options']))
5763
			{
5764
				$param_list=array_keys($param['options']);
5765
				$InfoFieldList = explode(":", $param_list[0]);
5766
				$parentName='';
5767
				$parentField='';
5768
				// 0 : tableName
5769
				// 1 : label field name
5770
				// 2 : key fields name (if differ of rowid)
5771
				// 3 : key field parent (for dependent lists)
5772
				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5773
				$keyList=(empty($InfoFieldList[2])?'rowid':$InfoFieldList[2].' as rowid');
5774
5775
5776
				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4]))
5777
				{
5778
					if (strpos($InfoFieldList[4], 'extra.') !== false)
5779
					{
5780
						$keyList='main.'.$InfoFieldList[2].' as rowid';
5781
					} else {
5782
						$keyList=$InfoFieldList[2].' as rowid';
5783
					}
5784
				}
5785
				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3]))
5786
				{
5787
					list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
5788
					$keyList.= ', '.$parentField;
5789
				}
5790
5791
				$fields_label = explode('|', $InfoFieldList[1]);
5792
				if (is_array($fields_label))
1 ignored issue
show
introduced by
The condition is_array($fields_label) is always true.
Loading history...
5793
				{
5794
					$keyList .=', ';
5795
					$keyList .= implode(', ', $fields_label);
5796
				}
5797
5798
				$sqlwhere='';
5799
				$sql = 'SELECT '.$keyList;
5800
				$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
5801
				if (!empty($InfoFieldList[4]))
5802
				{
5803
					// can use SELECT request
5804
					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5805
						$InfoFieldList[4]=str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
5806
					}
5807
5808
					// current object id can be use into filter
5809
					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5810
						$InfoFieldList[4]=str_replace('$ID$', $objectid, $InfoFieldList[4]);
5811
					} else {
5812
						$InfoFieldList[4]=str_replace('$ID$', '0', $InfoFieldList[4]);
5813
					}
5814
					//We have to join on extrafield table
5815
					if (strpos($InfoFieldList[4], 'extra')!==false)
5816
					{
5817
						$sql.= ' as main, '.MAIN_DB_PREFIX .$InfoFieldList[0].'_extrafields as extra';
5818
						$sqlwhere.= ' WHERE extra.fk_object=main.'.$InfoFieldList[2]. ' AND '.$InfoFieldList[4];
5819
					}
5820
					else
5821
					{
5822
						$sqlwhere.= ' WHERE '.$InfoFieldList[4];
5823
					}
5824
				}
5825
				else
5826
				{
5827
					$sqlwhere.= ' WHERE 1=1';
5828
				}
5829
				// Some tables may have field, some other not. For the moment we disable it.
5830
				if (in_array($InfoFieldList[0], array('tablewithentity')))
5831
				{
5832
					$sqlwhere.= ' AND entity = '.$conf->entity;
5833
				}
5834
				$sql.=$sqlwhere;
5835
				//print $sql;
5836
5837
				$sql .= ' ORDER BY ' . implode(', ', $fields_label);
5838
5839
				dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
5840
				$resql = $this->db->query($sql);
5841
				if ($resql)
5842
				{
5843
					$out.='<option value="0">&nbsp;</option>';
5844
					$num = $this->db->num_rows($resql);
5845
					$i = 0;
5846
					while ($i < $num)
5847
					{
5848
						$labeltoshow='';
5849
						$obj = $this->db->fetch_object($resql);
5850
5851
						// Several field into label (eq table:code|libelle:rowid)
5852
						$notrans = false;
5853
						$fields_label = explode('|', $InfoFieldList[1]);
5854
						if (is_array($fields_label))
5855
						{
5856
							$notrans = true;
5857
							foreach ($fields_label as $field_toshow)
5858
							{
5859
								$labeltoshow.= $obj->$field_toshow.' ';
5860
							}
5861
						}
5862
						else
5863
						{
5864
							$labeltoshow=$obj->{$InfoFieldList[1]};
5865
						}
5866
						$labeltoshow=dol_trunc($labeltoshow, 45);
5867
5868
						if ($value == $obj->rowid)
5869
						{
5870
							foreach ($fields_label as $field_toshow)
5871
							{
5872
								$translabel=$langs->trans($obj->$field_toshow);
5873
								if ($translabel!=$obj->$field_toshow) {
5874
									$labeltoshow=dol_trunc($translabel, 18).' ';
5875
								}else {
5876
									$labeltoshow=dol_trunc($obj->$field_toshow, 18).' ';
5877
								}
5878
							}
5879
							$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5880
						}
5881
						else
5882
						{
5883
							if (! $notrans)
5884
							{
5885
								$translabel=$langs->trans($obj->{$InfoFieldList[1]});
5886
								if ($translabel!=$obj->{$InfoFieldList[1]}) {
5887
									$labeltoshow=dol_trunc($translabel, 18);
5888
								}
5889
								else {
5890
									$labeltoshow=dol_trunc($obj->{$InfoFieldList[1]}, 18);
5891
								}
5892
							}
5893
							if (empty($labeltoshow)) $labeltoshow='(not defined)';
5894
							if ($value==$obj->rowid)
5895
							{
5896
								$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5897
							}
5898
5899
							if (!empty($InfoFieldList[3]) && $parentField)
5900
							{
5901
								$parent = $parentName.':'.$obj->{$parentField};
5902
							}
5903
5904
							$out.='<option value="'.$obj->rowid.'"';
5905
							$out.= ($value==$obj->rowid?' selected':'');
5906
							$out.= (!empty($parent)?' parent="'.$parent.'"':'');
5907
							$out.='>'.$labeltoshow.'</option>';
5908
						}
5909
5910
						$i++;
5911
					}
5912
					$this->db->free($resql);
5913
				}
5914
				else {
5915
					print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
5916
				}
5917
			}
5918
			$out.='</select>';
5919
		}
5920
		elseif ($type == 'checkbox')
5921
		{
5922
			$value_arr=explode(',', $value);
5923
			$out=$form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options'])?null:$param['options']), $value_arr, '', 0, '', 0, '100%');
5924
		}
5925
		elseif ($type == 'radio')
5926
		{
5927
			$out='';
5928
			foreach ($param['options'] as $keyopt => $val)
0 ignored issues
show
introduced by
$val is overwriting one of the parameters of this function.
Loading history...
5929
			{
5930
				$out.='<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'');
5931
				$out.=' value="'.$keyopt.'"';
5932
				$out.=' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
5933
				$out.= ($value==$keyopt?'checked':'');
5934
				$out.='/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$val.'</label><br>';
5935
			}
5936
		}
5937
		elseif ($type == 'chkbxlst')
5938
		{
5939
			if (is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
5940
				$value_arr = $value;
5941
			}
5942
			else {
5943
				$value_arr = explode(',', $value);
5944
			}
5945
5946
			if (is_array($param['options'])) {
5947
				$param_list = array_keys($param['options']);
5948
				$InfoFieldList = explode(":", $param_list[0]);
5949
				$parentName='';
5950
				$parentField='';
5951
				// 0 : tableName
5952
				// 1 : label field name
5953
				// 2 : key fields name (if differ of rowid)
5954
				// 3 : key field parent (for dependent lists)
5955
				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5956
				$keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
5957
5958
				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3])) {
5959
					list ( $parentName, $parentField ) = explode('|', $InfoFieldList[3]);
5960
					$keyList .= ', ' . $parentField;
5961
				}
5962
				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4])) {
5963
					if (strpos($InfoFieldList[4], 'extra.') !== false) {
5964
						$keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
5965
					} else {
5966
						$keyList = $InfoFieldList[2] . ' as rowid';
5967
					}
5968
				}
5969
5970
				$fields_label = explode('|', $InfoFieldList[1]);
5971
				if (is_array($fields_label)) {
1 ignored issue
show
introduced by
The condition is_array($fields_label) is always true.
Loading history...
5972
					$keyList .= ', ';
5973
					$keyList .= implode(', ', $fields_label);
5974
				}
5975
5976
				$sqlwhere = '';
5977
				$sql = 'SELECT ' . $keyList;
5978
				$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
5979
				if (! empty($InfoFieldList[4])) {
5980
5981
					// can use SELECT request
5982
					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5983
						$InfoFieldList[4]=str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
5984
					}
5985
5986
					// current object id can be use into filter
5987
					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5988
						$InfoFieldList[4]=str_replace('$ID$', $objectid, $InfoFieldList[4]);
5989
					} else {
5990
						$InfoFieldList[4]=str_replace('$ID$', '0', $InfoFieldList[4]);
5991
					}
5992
5993
					// We have to join on extrafield table
5994
					if (strpos($InfoFieldList[4], 'extra') !== false) {
5995
						$sql .= ' as main, ' . MAIN_DB_PREFIX . $InfoFieldList[0] . '_extrafields as extra';
5996
						$sqlwhere .= ' WHERE extra.fk_object=main.' . $InfoFieldList[2] . ' AND ' . $InfoFieldList[4];
5997
					} else {
5998
						$sqlwhere .= ' WHERE ' . $InfoFieldList[4];
5999
					}
6000
				} else {
6001
					$sqlwhere .= ' WHERE 1=1';
6002
				}
6003
				// Some tables may have field, some other not. For the moment we disable it.
6004
				if (in_array($InfoFieldList[0], array ('tablewithentity')))
6005
				{
6006
					$sqlwhere .= ' AND entity = ' . $conf->entity;
6007
				}
6008
				// $sql.=preg_replace('/^ AND /','',$sqlwhere);
6009
				// print $sql;
6010
6011
				$sql .= $sqlwhere;
6012
				dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
6013
				$resql = $this->db->query($sql);
6014
				if ($resql) {
6015
					$num = $this->db->num_rows($resql);
6016
					$i = 0;
6017
6018
					$data=array();
6019
6020
					while ( $i < $num ) {
6021
						$labeltoshow = '';
6022
						$obj = $this->db->fetch_object($resql);
6023
6024
						$notrans = false;
6025
						// Several field into label (eq table:code|libelle:rowid)
6026
						$fields_label = explode('|', $InfoFieldList[1]);
6027
						if (is_array($fields_label)) {
6028
							$notrans = true;
6029
							foreach ($fields_label as $field_toshow) {
6030
								$labeltoshow .= $obj->$field_toshow . ' ';
6031
							}
6032
						} else {
6033
							$labeltoshow = $obj->{$InfoFieldList[1]};
6034
						}
6035
						$labeltoshow = dol_trunc($labeltoshow, 45);
6036
6037
						if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
6038
							foreach ($fields_label as $field_toshow) {
6039
								$translabel = $langs->trans($obj->$field_toshow);
6040
								if ($translabel != $obj->$field_toshow) {
6041
									$labeltoshow = dol_trunc($translabel, 18) . ' ';
6042
								} else {
6043
									$labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
6044
								}
6045
							}
6046
6047
							$data[$obj->rowid]=$labeltoshow;
6048
						} else {
6049
							if (! $notrans) {
6050
								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
6051
								if ($translabel != $obj->{$InfoFieldList[1]}) {
6052
									$labeltoshow = dol_trunc($translabel, 18);
6053
								} else {
6054
									$labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
6055
								}
6056
							}
6057
							if (empty($labeltoshow))
6058
								$labeltoshow = '(not defined)';
6059
6060
								if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
6061
									$data[$obj->rowid]=$labeltoshow;
6062
								}
6063
6064
								if (! empty($InfoFieldList[3]) && $parentField) {
6065
									$parent = $parentName . ':' . $obj->{$parentField};
6066
								}
6067
6068
								$data[$obj->rowid]=$labeltoshow;
6069
						}
6070
6071
						$i ++;
6072
					}
6073
					$this->db->free($resql);
6074
6075
					$out=$form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
6076
				} else {
6077
					print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
6078
				}
6079
			}
6080
		}
6081
		elseif ($type == 'link')
6082
		{
6083
			$param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
6084
			$showempty=(($required && $default != '')?0:1);
6085
			$out=$form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty);
6086
			if ($conf->global->MAIN_FEATURES_LEVEL >= 2)
6087
			{
6088
            			list($class,$classfile)=explode(':', $param_list[0]);
6089
            			if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) $url_path=dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
6090
            			else $url_path=dol_buildpath(dirname(dirname($classfile)).'/'.$class.'_card.php', 1);
6091
            			$out.='<a class="butActionNew" href="'.$url_path.'?action=create&backtopage='.$_SERVER['PHP_SELF'].'"><span class="fa fa-plus-circle valignmiddle"></span></a>';
6092
            			// TODO Add Javascript code to add input fields contents to new elements urls
6093
			}
6094
		}
6095
		elseif ($type == 'password')
6096
		{
6097
			// If prefix is 'search_', field is used as a filter, we use a common text field.
6098
			$out='<input type="'.($keyprefix=='search_'?'text':'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'>';
6099
		}
6100
		elseif ($type == 'array')
6101
		{
6102
			$newval = $val;
6103
			$newval['type'] = 'varchar(256)';
6104
6105
			$out='';
6106
6107
			$inputs = array();
6108
			if(! empty($value)) {
6109
				foreach($value as $option) {
0 ignored issues
show
Bug introduced by
The expression $value of type string is not traversable.
Loading history...
6110
					$out.= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
6111
					$out.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
6112
				}
6113
			}
6114
6115
			$out.= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
6116
6117
			$newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
6118
			$newInput.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
6119
6120
			if(! empty($conf->use_javascript_ajax)) {
6121
				$out.= '
6122
					<script>
6123
					$(document).ready(function() {
6124
						$("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
6125
							$("'.dol_escape_js($newInput).'").insertBefore(this);
6126
						});
6127
6128
						$(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
6129
							$(this).parent().remove();
6130
						});
6131
					});
6132
					</script>';
6133
			}
6134
		}
6135
		if (!empty($hidden)) {
6136
			$out='<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
6137
		}
6138
		/* Add comments
6139
		 if ($type == 'date') $out.=' (YYYY-MM-DD)';
6140
		 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
6141
		 */
6142
		return $out;
6143
	}
6144
6145
	/**
6146
	 * Return HTML string to show a field into a page
6147
	 * Code very similar with showOutputField of extra fields
6148
	 *
6149
	 * @param  array   $val		       Array of properties of field to show
6150
	 * @param  string  $key            Key of attribute
6151
	 * @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)
6152
	 * @param  string  $moreparam      To add more parametes on html input tag
6153
	 * @param  string  $keysuffix      Prefix string to add into name and id of field (can be used to avoid duplicate names)
6154
	 * @param  string  $keyprefix      Suffix string to add into name and id of field (can be used to avoid duplicate names)
6155
	 * @param  mixed   $morecss        Value for css to define size. May also be a numeric.
6156
	 * @return string
6157
	 */
6158
	public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
6159
	{
6160
		global $conf,$langs,$form;
6161
6162
		if (! is_object($form))
6163
		{
6164
			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
6165
			$form=new Form($this->db);
6166
		}
6167
6168
		$objectid = $this->id;
6169
		$label = $val['label'];
6170
		$type  = $val['type'];
6171
		$size  = $val['css'];
6172
6173
		// Convert var to be able to share same code than showOutputField of extrafields
6174
		if (preg_match('/varchar\((\d+)\)/', $type, $reg))
6175
		{
6176
			$type = 'varchar';		// convert varchar(xx) int varchar
6177
			$size = $reg[1];
6178
		}
6179
		elseif (preg_match('/varchar/', $type)) $type = 'varchar';		// convert varchar(xx) int varchar
6180
		if (is_array($val['arrayofkeyval'])) $type='select';
6181
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) $type='link';
6182
6183
		$default=$val['default'];
6184
		$computed=$val['computed'];
6185
		$unique=$val['unique'];
6186
		$required=$val['required'];
6187
		$param=$val['param'];
6188
		if (is_array($val['arrayofkeyval'])) $param['options'] = $val['arrayofkeyval'];
6189
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg))
6190
		{
6191
			$type='link';
6192
			$param['options']=array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
6193
		}
6194
        elseif(preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6195
            $param['options'] = array($reg[1] . ':' . $reg[2] . ':' . $reg[3] . ':' . $reg[4] => 'N');
6196
            $type = 'sellist';
6197
        }
6198
6199
6200
		$langfile=$val['langfile'];
6201
		$list=$val['list'];
6202
		$help=$val['help'];
6203
		$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)
6204
6205
		if ($hidden) return '';
6206
6207
		// If field is a computed field, value must become result of compute
6208
		if ($computed)
6209
		{
6210
			// Make the eval of compute string
6211
			//var_dump($computed);
6212
			$value = dol_eval($computed, 1, 0);
6213
		}
6214
6215
		if (empty($morecss))
6216
		{
6217
			if ($type == 'date')
6218
			{
6219
				$morecss = 'minwidth100imp';
6220
			}
6221
			elseif ($type == 'datetime' || $type == 'timestamp')
6222
			{
6223
				$morecss = 'minwidth200imp';
6224
			}
6225
			elseif (in_array($type, array('int','double','price')))
6226
			{
6227
				$morecss = 'maxwidth75';
6228
			}
6229
			elseif ($type == 'url')
6230
			{
6231
				$morecss='minwidth400';
6232
			}
6233
			elseif ($type == 'boolean')
6234
			{
6235
				$morecss='';
6236
			}
6237
			else
6238
			{
6239
				if (round($size) < 12)
6240
				{
6241
					$morecss = 'minwidth100';
6242
				}
6243
				elseif (round($size) <= 48)
6244
				{
6245
					$morecss = 'minwidth200';
6246
				}
6247
				else
6248
				{
6249
					$morecss = 'minwidth400';
6250
				}
6251
			}
6252
		}
6253
6254
		// Format output value differently according to properties of field
6255
		if ($key == 'ref' && method_exists($this, 'getNomUrl')) $value=$this->getNomUrl(1, '', 0, '', 1);
6256
		elseif ($key == 'status' && method_exists($this, 'getLibStatut')) $value=$this->getLibStatut(3);
6257
		elseif ($type == 'date')
6258
		{
6259
			if(! empty($value)) {
6260
				$value=dol_print_date($value, 'day');
6261
			} else {
6262
				$value='';
6263
			}
6264
		}
6265
		elseif ($type == 'datetime' || $type == 'timestamp')
6266
		{
6267
			if(! empty($value)) {
6268
				$value=dol_print_date($value, 'dayhour');
6269
			} else {
6270
				$value='';
6271
			}
6272
		}
6273
		elseif ($type == 'double' || $type == 'real')
6274
		{
6275
			if (!empty($value)) {
6276
				$value=price($value);
6277
			}
6278
		}
6279
		elseif ($type == 'boolean')
6280
		{
6281
			$checked='';
6282
			if (!empty($value)) {
6283
				$checked=' checked ';
6284
			}
6285
			$value='<input type="checkbox" '.$checked.' '.($moreparam?$moreparam:'').' readonly disabled>';
6286
		}
6287
		elseif ($type == 'mail')
6288
		{
6289
			$value=dol_print_email($value, 0, 0, 0, 64, 1, 1);
6290
		}
6291
		elseif ($type == 'url')
6292
		{
6293
			$value=dol_print_url($value, '_blank', 32, 1);
6294
		}
6295
		elseif ($type == 'phone')
6296
		{
6297
			$value=dol_print_phone($value, '', 0, 0, '', '&nbsp;', 1);
6298
		}
6299
		elseif ($type == 'price')
6300
		{
6301
			$value=price($value, 0, $langs, 0, 0, -1, $conf->currency);
6302
		}
6303
		elseif ($type == 'select')
6304
		{
6305
			$value=$param['options'][$value];
6306
		}
6307
		elseif ($type == 'sellist')
6308
		{
6309
			$param_list=array_keys($param['options']);
6310
			$InfoFieldList = explode(":", $param_list[0]);
6311
6312
			$selectkey="rowid";
6313
			$keyList='rowid';
6314
6315
			if (count($InfoFieldList)>=3)
6316
			{
6317
				$selectkey = $InfoFieldList[2];
6318
				$keyList=$InfoFieldList[2].' as rowid';
6319
			}
6320
6321
			$fields_label = explode('|', $InfoFieldList[1]);
6322
			if(is_array($fields_label)) {
1 ignored issue
show
introduced by
The condition is_array($fields_label) is always true.
Loading history...
6323
				$keyList .=', ';
6324
				$keyList .= implode(', ', $fields_label);
6325
			}
6326
6327
			$sql = 'SELECT '.$keyList;
6328
			$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
6329
			if (strpos($InfoFieldList[4], 'extra')!==false)
6330
			{
6331
				$sql.= ' as main';
6332
			}
6333
			if ($selectkey=='rowid' && empty($value)) {
6334
				$sql.= " WHERE ".$selectkey."=0";
6335
			} elseif ($selectkey=='rowid') {
6336
				$sql.= " WHERE ".$selectkey."=".$this->db->escape($value);
6337
			}else {
6338
				$sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6339
			}
6340
6341
			//$sql.= ' AND entity = '.$conf->entity;
6342
6343
			dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
6344
			$resql = $this->db->query($sql);
6345
			if ($resql)
6346
			{
6347
				$value='';	// value was used, so now we reste it to use it to build final output
6348
6349
				$obj = $this->db->fetch_object($resql);
6350
6351
				// Several field into label (eq table:code|libelle:rowid)
6352
				$fields_label = explode('|', $InfoFieldList[1]);
6353
6354
				if(is_array($fields_label) && count($fields_label)>1)
6355
				{
6356
					foreach ($fields_label as $field_toshow)
6357
					{
6358
						$translabel='';
6359
						if (!empty($obj->$field_toshow)) {
6360
							$translabel=$langs->trans($obj->$field_toshow);
6361
						}
6362
						if ($translabel!=$field_toshow) {
6363
							$value.=dol_trunc($translabel, 18).' ';
6364
						}else {
6365
							$value.=$obj->$field_toshow.' ';
6366
						}
6367
					}
6368
				}
6369
				else
6370
				{
6371
					$translabel='';
6372
					if (!empty($obj->{$InfoFieldList[1]})) {
6373
						$translabel=$langs->trans($obj->{$InfoFieldList[1]});
6374
					}
6375
					if ($translabel!=$obj->{$InfoFieldList[1]}) {
6376
						$value=dol_trunc($translabel, 18);
6377
					}else {
6378
						$value=$obj->{$InfoFieldList[1]};
6379
					}
6380
				}
6381
			}
6382
			else dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
6383
		}
6384
		elseif ($type == 'radio')
6385
		{
6386
			$value=$param['options'][$value];
6387
		}
6388
		elseif ($type == 'checkbox')
6389
		{
6390
			$value_arr=explode(',', $value);
6391
			$value='';
6392
			if (is_array($value_arr) && count($value_arr)>0)
6393
			{
6394
				$toprint=array();
6395
				foreach ($value_arr as $keyval=>$valueval) {
6396
					$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$param['options'][$valueval].'</li>';
6397
				}
6398
				$value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6399
			}
6400
		}
6401
		elseif ($type == 'chkbxlst')
6402
		{
6403
			$value_arr = explode(',', $value);
6404
6405
			$param_list = array_keys($param['options']);
6406
			$InfoFieldList = explode(":", $param_list[0]);
6407
6408
			$selectkey = "rowid";
6409
			$keyList = 'rowid';
6410
6411
			if (count($InfoFieldList) >= 3) {
6412
				$selectkey = $InfoFieldList[2];
6413
				$keyList = $InfoFieldList[2] . ' as rowid';
6414
			}
6415
6416
			$fields_label = explode('|', $InfoFieldList[1]);
6417
			if (is_array($fields_label)) {
1 ignored issue
show
introduced by
The condition is_array($fields_label) is always true.
Loading history...
6418
				$keyList .= ', ';
6419
				$keyList .= implode(', ', $fields_label);
6420
			}
6421
6422
			$sql = 'SELECT ' . $keyList;
6423
			$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
6424
			if (strpos($InfoFieldList[4], 'extra') !== false) {
6425
				$sql .= ' as main';
6426
			}
6427
			// $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6428
			// $sql.= ' AND entity = '.$conf->entity;
6429
6430
			dol_syslog(get_class($this) . ':showOutputField:$type=chkbxlst', LOG_DEBUG);
6431
			$resql = $this->db->query($sql);
6432
			if ($resql) {
6433
				$value = ''; // value was used, so now we reste it to use it to build final output
6434
				$toprint=array();
6435
				while ( $obj = $this->db->fetch_object($resql) ) {
6436
6437
					// Several field into label (eq table:code|libelle:rowid)
6438
					$fields_label = explode('|', $InfoFieldList[1]);
6439
					if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
6440
						if (is_array($fields_label) && count($fields_label) > 1) {
6441
							foreach ($fields_label as $field_toshow) {
6442
								$translabel = '';
6443
								if (! empty($obj->$field_toshow)) {
6444
									$translabel = $langs->trans($obj->$field_toshow);
6445
								}
6446
								if ($translabel != $field_toshow) {
6447
									$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6448
								} else {
6449
									$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->$field_toshow.'</li>';
6450
								}
6451
							}
6452
						} else {
6453
							$translabel = '';
6454
							if (! empty($obj->{$InfoFieldList[1]})) {
6455
								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
6456
							}
6457
							if ($translabel != $obj->{$InfoFieldList[1]}) {
6458
								$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6459
							} else {
6460
								$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->{$InfoFieldList[1]}.'</li>';
6461
							}
6462
						}
6463
					}
6464
				}
6465
				$value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6466
			} else {
6467
				dol_syslog(get_class($this) . '::showOutputField error ' . $this->db->lasterror(), LOG_WARNING);
6468
			}
6469
		}
6470
		elseif ($type == 'link')
6471
		{
6472
			$out='';
6473
6474
			// only if something to display (perf)
6475
			if ($value)
6476
			{
6477
				$param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
6478
6479
				$InfoFieldList = explode(":", $param_list[0]);
6480
				$classname=$InfoFieldList[0];
6481
				$classpath=$InfoFieldList[1];
6482
				$getnomurlparam=(empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]);
6483
				if (! empty($classpath))
6484
				{
6485
					dol_include_once($InfoFieldList[1]);
6486
					if ($classname && class_exists($classname))
6487
					{
6488
						$object = new $classname($this->db);
6489
						$object->fetch($value);
6490
						$value=$object->getNomUrl($getnomurlparam);
6491
					}
6492
				}
6493
				else
6494
				{
6495
					dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6496
					return 'Error bad setup of extrafield';
6497
				}
6498
			}
6499
			else $value='';
6500
		}
6501
		elseif ($type == 'text' || $type == 'html')
6502
		{
6503
			$value=dol_htmlentitiesbr($value);
6504
		}
6505
		elseif ($type == 'password')
6506
		{
6507
			$value=preg_replace('/./i', '*', $value);
6508
		}
6509
		elseif ($type == 'array')
6510
		{
6511
			$value = implode('<br>', $value);
6512
		}
6513
6514
		//print $type.'-'.$size;
6515
		$out=$value;
6516
6517
		return $out;
6518
	}
6519
6520
6521
	/**
6522
	 * Function to show lines of extrafields with output datas
6523
	 *
6524
	 * @param 	Extrafields $extrafields    Extrafield Object
6525
	 * @param 	string      $mode           Show output (view) or input (edit) for extrafield
6526
	 * @param 	array       $params         Optional parameters. Example: array('style'=>'class="oddeven"', 'colspan'=>$colspan)
6527
	 * @param 	string      $keysuffix      Suffix string to add after name and id of field (can be used to avoid duplicate names)
6528
	 * @param 	string      $keyprefix      Prefix string to add before name and id of field (can be used to avoid duplicate names)
6529
	 * @param	string		$onetrtd		All fields in same tr td (TODO field not used ?)
6530
	 * @return 	string
6531
	 */
6532
	public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = 0)
6533
	{
6534
		global $db, $conf, $langs, $action, $form;
6535
6536
		if (! is_object($form)) $form=new Form($db);
6537
6538
		$out = '';
6539
6540
		if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0)
6541
		{
6542
			$out .= "\n";
6543
			$out .= '<!-- showOptionalsInput --> ';
6544
			$out .= "\n";
6545
6546
            $extrafields_collapse_num = '';
6547
			$e = 0;
6548
			foreach($extrafields->attributes[$this->table_element]['label'] as $key=>$label)
6549
			{
6550
				// Show only the key field in params
6551
				if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) continue;
6552
6553
				// @TODO Add test also on 'enabled' (different than 'list' that is 'visibility')
6554
				$enabled = 1;
6555
6556
				$visibility = 1;
6557
				if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key]))
6558
				{
6559
				    $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1);
6560
				}
6561
6562
				$perms = 1;
6563
				if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key]))
6564
				{
6565
					$perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1);
6566
				}
6567
6568
				if (($mode == 'create' || $mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3) continue;	// <> -1 and <> 1 and <> 3 = not visible on forms, only on list
6569
				elseif($mode == 'view' && empty($visibility)) continue;
6570
				if (empty($perms)) continue;
6571
6572
				// Load language if required
6573
				if (! empty($extrafields->attributes[$this->table_element]['langfile'][$key])) $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
6574
6575
				$colspan='3';
6576
				if (is_array($params) && count($params)>0) {
6577
					if (array_key_exists('colspan', $params)) {
6578
						$colspan=$params['colspan'];
6579
					}
6580
				}
6581
6582
				switch($mode) {
6583
					case "view":
6584
						$value=$this->array_options["options_".$key.$keysuffix];
6585
						break;
6586
					case "edit":
6587
						$getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, 'none');				// GETPOST can get value from GET, POST or setup of default values.
6588
						// GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
6589
						if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix))
6590
						{
6591
							if (is_array($getposttemp)) {
6592
								// $getposttemp is an array but following code expects a comma separated string
6593
								$value = implode(",", $getposttemp);
6594
							} else {
6595
								$value = $getposttemp;
6596
							}
6597
						} else {
6598
							$value = $this->array_options["options_" . $key];			// No GET, no POST, no default value, so we take value of object.
6599
						}
6600
						//var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
6601
						break;
6602
				}
6603
6604
				if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate')
6605
				{
6606
                    $extrafields_collapse_num = '';
6607
                    $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
6608
                    if (!empty($extrafield_param) && is_array($extrafield_param)) {
6609
                        $extrafield_param_list = array_keys($extrafield_param['options']);
6610
6611
                        if (count($extrafield_param_list)>0) {
6612
                            $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
6613
6614
                            if ($extrafield_collapse_display_value==1 || $extrafield_collapse_display_value==2) {
6615
                                $extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
6616
                            }
6617
                        }
6618
                    }
6619
6620
					$out .= $extrafields->showSeparator($key, $this);
6621
				}
6622
				else
6623
				{
6624
					$class=(!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
6625
					$csstyle='';
6626
					if (is_array($params) && count($params)>0) {
6627
						if (array_key_exists('class', $params)) {
6628
							$class.=$params['class'].' ';
6629
						}
6630
						if (array_key_exists('style', $params)) {
6631
							$csstyle=$params['style'];
6632
						}
6633
					}
6634
6635
					// add html5 elements
6636
					$domData  = ' data-element="extrafield"';
6637
					$domData .= ' data-targetelement="'.$this->element.'"';
6638
					$domData .= ' data-targetid="'.$this->id.'"';
6639
6640
					$html_id = !empty($this->id) ? 'extrarow-'.$this->element.'_'.$key.'_'.$this->id : '';
6641
6642
					$out .= '<tr id="'.$html_id.'" '.$csstyle.' class="'.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$extrafields_collapse_num.'" '.$domData.' >';
6643
6644
					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) { $colspan='0'; }
6645
6646
					if ($action == 'selectlines') { $colspan++; }
6647
6648
					// Convert date into timestamp format (value in memory must be a timestamp)
6649
					if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime')))
6650
					{
6651
						$datenotinstring = $this->array_options['options_' . $key];
6652
						if (! is_numeric($this->array_options['options_' . $key]))	// For backward compatibility
6653
						{
6654
							$datenotinstring = $this->db->jdate($datenotinstring);
6655
						}
6656
						$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;
6657
					}
6658
					// Convert float submited string into real php numeric (value in memory must be a php numeric)
6659
					if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price','double')))
6660
					{
6661
						$value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?price2num(GETPOST($keyprefix.'options_'.$key.$keysuffix, 'alpha', 3)):$this->array_options['options_'.$key];
6662
					}
6663
6664
					$labeltoshow = $langs->trans($label);
6665
6666
					$out .= '<td class="';
6667
					//$out .= "titlefield";
6668
					//if (GETPOST('action', 'none') == 'create') $out.='create';
6669
					// BUG #11554 : For public page, use red dot for required fields, instead of bold label
6670
					$tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
6671
					if ($tpl_context=="public") {	// Public page : red dot instead of fieldrequired characters
6672
						$out .= '">';
6673
						if (! empty($extrafields->attributes[$this->table_element]['help'][$key])) $out .= $form->textwithpicto($labeltoshow, $extrafields->attributes[$this->table_element]['help'][$key]);
6674
						else $out .= $labeltoshow;
6675
						if ($mode != 'view' && ! empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= '&nbsp;<font color="red">*</font>';
6676
					} else {
6677
						if ($mode != 'view' && ! empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= ' fieldrequired';
6678
						$out .= '">';
6679
						if (! empty($extrafields->attributes[$this->table_element]['help'][$key])) $out .= $form->textwithpicto($labeltoshow, $extrafields->attributes[$this->table_element]['help'][$key]);
6680
						else $out .= $labeltoshow;
6681
					}
6682
					$out .= '</td>';
6683
6684
					$html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
6685
6686
					$out .='<td id="'.$html_id.'" class="'.$this->element.'_extras_'.$key.'" '.($colspan?' colspan="'.$colspan.'"':'').'>';
6687
					//$out .='<td id="'.$html_id.'" class="'.$this->element.'_extras_'.$key.'">';
6688
6689
					switch($mode) {
6690
						case "view":
6691
							$out .= $extrafields->showOutputField($key, $value);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $value does not seem to be defined for all execution paths leading up to this point.
Loading history...
6692
							break;
6693
						case "edit":
6694
							$out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
6695
							break;
6696
					}
6697
6698
					$out .= '</td>';
6699
6700
					/*for($ii = 0; $ii < ($colspan - 1); $ii++)
6701
					{
6702
						$out .='<td class="'.$this->element.'_extras_'.$key.'"></td>';
6703
					}*/
6704
6705
					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>';
6706
					else $out .= '</tr>';
6707
					$e++;
6708
				}
6709
			}
6710
			$out .= "\n";
6711
			// Add code to manage list depending on others
6712
			if (! empty($conf->use_javascript_ajax)) {
6713
				$out .= '
6714
				<script>
6715
				    jQuery(document).ready(function() {
6716
				    	function showOptions(child_list, parent_list)
6717
				    	{
6718
				    		var val = $("select[name=\""+parent_list+"\"]").val();
6719
				    		var parentVal = parent_list + ":" + val;
6720
							if(val > 0) {
6721
					    		$("select[name=\""+child_list+"\"] option[parent]").hide();
6722
					    		$("select[name=\""+child_list+"\"] option[parent=\""+parentVal+"\"]").show();
6723
							} else {
6724
								$("select[name=\""+child_list+"\"] option").show();
6725
							}
6726
				    	}
6727
						function setListDependencies() {
6728
					    	jQuery("select option[parent]").parent().each(function() {
6729
					    		var child_list = $(this).attr("name");
6730
								var parent = $(this).find("option[parent]:first").attr("parent");
6731
								var infos = parent.split(":");
6732
								var parent_list = infos[0];
6733
								$("select[name=\""+parent_list+"\"]").change(function() {
6734
									showOptions(child_list, parent_list);
6735
								});
6736
					    	});
6737
						}
6738
6739
						setListDependencies();
6740
				    });
6741
				</script>'."\n";
6742
				$out .= '<!-- /showOptionalsInput --> '."\n";
6743
			}
6744
		}
6745
		return $out;
6746
	}
6747
6748
6749
	/**
6750
	 * Returns the rights used for this class
6751
	 * @return stdClass
6752
	 */
6753
	public function getRights()
6754
	{
6755
		global $user;
6756
6757
		$element = $this->element;
6758
		if ($element == 'facturerec') $element='facture';
6759
6760
		return $user->rights->{$element};
6761
	}
6762
6763
	/**
6764
	 * Function used to replace a thirdparty id with another one.
6765
	 * This function is meant to be called from replaceThirdparty with the appropiate tables
6766
	 * Column name fk_soc MUST be used to identify thirdparties
6767
	 *
6768
	 * @param  DoliDB 	   $db 			  Database handler
6769
	 * @param  int 		   $origin_id     Old thirdparty id (the thirdparty to delete)
6770
	 * @param  int 		   $dest_id       New thirdparty id (the thirdparty that will received element of the other)
6771
	 * @param  string[]    $tables        Tables that need to be changed
6772
	 * @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)
6773
	 * @return bool						  True if success, False if error
6774
	 */
6775
	public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
6776
	{
6777
		foreach ($tables as $table)
6778
		{
6779
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id;
6780
6781
			if (! $db->query($sql))
6782
			{
6783
				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.
6784
				//$this->errors = $db->lasterror();
6785
				return false;
6786
			}
6787
		}
6788
6789
		return true;
6790
	}
6791
6792
	/**
6793
	 * Get buy price to use for margin calculation. This function is called when buy price is unknown.
6794
	 *	 Set buy price = sell price if ForceBuyingPriceIfNull configured,
6795
	 *   else if calculation MARGIN_TYPE = 'costprice' and costprice is defined, use costprice as buyprice
6796
	 *	 else if calculation MARGIN_TYPE = 'pmp' and pmp is calculated, use pmp as buyprice
6797
	 *	 else set min buy price as buy price
6798
	 *
6799
	 * @param float		$unitPrice		 Product unit price
6800
	 * @param float		$discountPercent Line discount percent
6801
	 * @param int		$fk_product		 Product id
6802
	 * @return	float                    <0 if KO, buyprice if OK
6803
	 */
6804
	public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
6805
	{
6806
		global $conf;
6807
6808
		$buyPrice = 0;
6809
6810
		if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
6811
		{
6812
			$buyPrice = $unitPrice * (1 - $discountPercent / 100);
6813
		}
6814
		else
6815
		{
6816
			// Get cost price for margin calculation
6817
			if (! empty($fk_product))
6818
			{
6819
				if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice')
6820
				{
6821
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6822
					$product = new Product($this->db);
6823
					$result = $product->fetch($fk_product);
6824
					if ($result <= 0)
6825
					{
6826
						$this->errors[] = 'ErrorProductIdDoesNotExists';
6827
						return -1;
6828
					}
6829
					if ($product->cost_price > 0)
6830
					{
6831
						$buyPrice = $product->cost_price;
6832
					}
6833
					elseif ($product->pmp > 0)
6834
					{
6835
						$buyPrice = $product->pmp;
6836
					}
6837
				}
6838
				elseif (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')
6839
				{
6840
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6841
					$product = new Product($this->db);
6842
					$result = $product->fetch($fk_product);
6843
					if ($result <= 0)
6844
					{
6845
						$this->errors[] = 'ErrorProductIdDoesNotExists';
6846
						return -1;
6847
					}
6848
					if ($product->pmp > 0)
6849
					{
6850
						$buyPrice = $product->pmp;
6851
					}
6852
				}
6853
6854
				if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1','pmp','costprice')))
6855
				{
6856
					require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6857
					$productFournisseur = new ProductFournisseur($this->db);
6858
					if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0)
6859
					{
6860
						$buyPrice = $productFournisseur->fourn_unitprice;
6861
					}
6862
					elseif ($result < 0)
6863
					{
6864
						$this->errors[] = $productFournisseur->error;
6865
						return -2;
6866
					}
6867
				}
6868
			}
6869
		}
6870
		return $buyPrice;
6871
	}
6872
6873
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6874
	/**
6875
	 *  Show photos of an object (nbmax maximum), into several columns
6876
	 *
6877
	 *  @param		string	$modulepart		'product', 'ticket', ...
6878
	 *  @param      string	$sdir        	Directory to scan (full absolute path)
6879
	 *  @param      int		$size        	0=original size, 1='small' use thumbnail if possible
6880
	 *  @param      int		$nbmax       	Nombre maximum de photos (0=pas de max)
6881
	 *  @param      int		$nbbyrow     	Number of image per line or -1 to use div. Used only if size=1.
6882
	 * 	@param		int		$showfilename	1=Show filename
6883
	 * 	@param		int		$showaction		1=Show icon with action links (resize, delete)
6884
	 * 	@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.
6885
	 * 	@param		int		$maxWidth		Max width of original image when size='small'
6886
	 *  @param      int     $nolink         Do not add a href link to view enlarged imaged into a new tab
6887
	 *  @param      int     $notitle        Do not add title tag on image
6888
	 *  @param		int		$usesharelink	Use the public shared link of image (if not available, the 'nophoto' image will be shown instead)
6889
	 *  @return     string					Html code to show photo. Number of photos shown is saved in this->nbphoto
6890
	 */
6891
	public function show_photos($modulepart, $sdir, $size = 0, $nbmax = 0, $nbbyrow = 5, $showfilename = 0, $showaction = 0, $maxHeight = 120, $maxWidth = 160, $nolink = 0, $notitle = 0, $usesharelink = 0)
6892
	{
6893
        // phpcs:enable
6894
		global $conf,$user,$langs;
6895
6896
		include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php';
6897
		include_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';
6898
6899
		$sortfield='position_name';
6900
		$sortorder='asc';
6901
6902
		$dir = $sdir . '/';
6903
		$pdir = '/';
6904
6905
		$dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6906
		$pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6907
6908
		// For backward compatibility
6909
		if ($modulepart == 'product' && ! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))
6910
		{
6911
			$dir = $sdir . '/'. get_exdir($this->id, 2, 0, 0, $this, $modulepart) . $this->id ."/photos/";
6912
			$pdir = '/' . get_exdir($this->id, 2, 0, 0, $this, $modulepart) . $this->id ."/photos/";
6913
		}
6914
6915
		// Defined relative dir to DOL_DATA_ROOT
6916
		$relativedir = '';
6917
		if ($dir)
6918
		{
6919
			$relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
6920
			$relativedir = preg_replace('/^[\\/]/', '', $relativedir);
6921
			$relativedir = preg_replace('/[\\/]$/', '', $relativedir);
6922
		}
6923
6924
		$dirthumb = $dir.'thumbs/';
6925
		$pdirthumb = $pdir.'thumbs/';
6926
6927
		$return ='<!-- Photo -->'."\n";
6928
		$nbphoto=0;
6929
6930
		$filearray=dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC), 1);
0 ignored issues
show
introduced by
The condition strtolower($sortorder) == 'desc' is always false.
Loading history...
6931
6932
		/*if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))    // For backward compatiblity, we scan also old dirs
6933
		 {
6934
		 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
6935
		 $filearray=array_merge($filearray, $filearrayold);
6936
		 }*/
6937
6938
		completeFileArrayWithDatabaseInfo($filearray, $relativedir);
6939
6940
		if (count($filearray))
6941
		{
6942
			if ($sortfield && $sortorder)
6943
			{
6944
				$filearray=dol_sort_array($filearray, $sortfield, $sortorder);
6945
			}
6946
6947
			foreach($filearray as $key => $val)
6948
			{
6949
				$photo='';
6950
				$file = $val['name'];
6951
6952
				//if (! utf8_check($file)) $file=utf8_encode($file);	// To be sure file is stored in UTF8 in memory
6953
6954
				//if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
6955
				if (image_format_supported($file) >= 0)
6956
				{
6957
					$nbphoto++;
6958
					$photo = $file;
6959
					$viewfilename = $file;
6960
6961
					if ($size == 1 || $size == 'small') {   // Format vignette
6962
6963
						// Find name of thumb file
6964
						$photo_vignette=basename(getImageFileNameForSize($dir.$file, '_small'));
6965
						if (! dol_is_file($dirthumb.$photo_vignette)) $photo_vignette='';
6966
6967
						// Get filesize of original file
6968
						$imgarray=dol_getImageSize($dir.$photo);
6969
6970
						if ($nbbyrow > 0)
6971
						{
6972
							if ($nbphoto == 1) $return.= '<table class="valigntop center centpercent" style="border: 0; padding: 2; border-spacing: 2px; border-collapse: separate;">';
6973
6974
							if ($nbphoto % $nbbyrow == 1) $return.= '<tr class="center valignmiddle" style="border: 1px">';
6975
							$return.= '<td style="width: '.ceil(100/$nbbyrow).'%" class="photo">';
6976
						}
6977
						elseif ($nbbyrow < 0) $return .= '<div class="inline-block">';
6978
6979
						$return.= "\n";
6980
6981
						$relativefile=preg_replace('/^\//', '', $pdir.$photo);
6982
						if (empty($nolink))
6983
						{
6984
							$urladvanced=getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
6985
							if ($urladvanced) $return.='<a href="'.$urladvanced.'">';
6986
							else $return.= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank">';
6987
						}
6988
6989
						// Show image (width height=$maxHeight)
6990
						// Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
6991
						$alt=$langs->transnoentitiesnoconv('File').': '.$relativefile;
6992
						$alt.=' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
6993
						if ($notitle) $alt='';
6994
6995
						if ($usesharelink)
6996
						{
6997
							if ($val['share'])
6998
							{
6999
								if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
7000
								{
7001
									$return.= '<!-- Show original file (thumb not yet available with shared links) -->';
7002
									$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
7003
								}
7004
								else {
7005
									$return.= '<!-- Show original file -->';
7006
									$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
7007
								}
7008
							}
7009
							else
7010
							{
7011
								$return.= '<!-- Show nophoto file (because file is not shared) -->';
7012
								$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
7013
							}
7014
						}
7015
						else
7016
						{
7017
							if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
7018
							{
7019
								$return.= '<!-- Show thumb -->';
7020
								$return.= '<img class="photo photowithmargin"  height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdirthumb.$photo_vignette).'" title="'.dol_escape_htmltag($alt).'">';
7021
							}
7022
							else {
7023
								$return.= '<!-- Show original file -->';
7024
								$return.= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" title="'.dol_escape_htmltag($alt).'">';
7025
							}
7026
						}
7027
7028
						if (empty($nolink)) $return.= '</a>';
7029
						$return.="\n";
7030
7031
						if ($showfilename) $return.= '<br>'.$viewfilename;
7032
						if ($showaction)
7033
						{
7034
							$return.= '<br>';
7035
							// On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
7036
							if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight))
7037
							{
7038
								$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>';
7039
							}
7040
							// Special cas for product
7041
							if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
7042
							{
7043
								// Link to resize
7044
								$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; ';
7045
7046
								// Link to delete
7047
								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
7048
								$return.= img_delete().'</a>';
7049
							}
7050
						}
7051
						$return.= "\n";
7052
7053
						if ($nbbyrow > 0)
7054
						{
7055
							$return.= '</td>';
7056
							if (($nbphoto % $nbbyrow) == 0) $return.= '</tr>';
7057
						}
7058
						elseif ($nbbyrow < 0) $return.='</div>';
7059
					}
7060
7061
					if (empty($size)) {     // Format origine
7062
						$return.= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
7063
7064
						if ($showfilename) $return.= '<br>'.$viewfilename;
7065
						if ($showaction)
7066
						{
7067
							// Special case for product
7068
							if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
7069
							{
7070
								// Link to resize
7071
								$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; ';
7072
7073
								// Link to delete
7074
								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
7075
								$return.= img_delete().'</a>';
7076
							}
7077
						}
7078
					}
7079
7080
					// On continue ou on arrete de boucler ?
7081
					if ($nbmax && $nbphoto >= $nbmax) break;
7082
				}
7083
			}
7084
7085
			if ($size==1 || $size=='small')
7086
			{
7087
				if ($nbbyrow > 0)
7088
				{
7089
					// Ferme tableau
7090
					while ($nbphoto % $nbbyrow)
7091
					{
7092
						$return.= '<td style="width: '.ceil(100/$nbbyrow).'%">&nbsp;</td>';
7093
						$nbphoto++;
7094
					}
7095
7096
					if ($nbphoto) $return.= '</table>';
7097
				}
7098
			}
7099
		}
7100
7101
		$this->nbphoto = $nbphoto;
7102
7103
		return $return;
7104
	}
7105
7106
7107
	/**
7108
	 * Function test if type is array
7109
	 *
7110
	 * @param   array   $info   content informations of field
7111
	 * @return  bool			true if array
7112
	 */
7113
	protected function isArray($info)
7114
	{
7115
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
7116
		{
7117
			if(isset($info['type']) && $info['type']=='array') return true;
7118
			else return false;
7119
		}
7120
		return false;
7121
	}
7122
7123
	/**
7124
	 * Function test if type is date
7125
	 *
7126
	 * @param   array   $info   content informations of field
7127
	 * @return  bool			true if date
7128
	 */
7129
	public function isDate($info)
7130
	{
7131
		if(isset($info['type']) && ($info['type']=='date' || $info['type']=='datetime' || $info['type']=='timestamp')) return true;
7132
		return false;
7133
	}
7134
7135
	/**
7136
	 * Function test if type is integer
7137
	 *
7138
	 * @param   array   $info   content informations of field
7139
	 * @return  bool			true if integer
7140
	 */
7141
	public function isInt($info)
7142
	{
7143
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
7144
		{
7145
			if(isset($info['type']) && ($info['type']=='int' || preg_match('/^integer/i', $info['type']) ) ) return true;
7146
			else return false;
7147
		}
7148
		else return false;
7149
	}
7150
7151
	/**
7152
	 * Function test if type is float
7153
	 *
7154
	 * @param   array   $info   content informations of field
7155
	 * @return  bool			true if float
7156
	 */
7157
	public function isFloat($info)
7158
	{
7159
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
7160
		{
7161
			if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) return true;
7162
			else return false;
7163
		}
7164
		return false;
7165
	}
7166
7167
	/**
7168
	 * Function test if type is text
7169
	 *
7170
	 * @param   array   $info   content informations of field
7171
	 * @return  bool			true if type text
7172
	 */
7173
	public function isText($info)
7174
	{
7175
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
7176
		{
7177
			if(isset($info['type']) && $info['type']=='text') return true;
7178
			else return false;
7179
		}
7180
		return false;
7181
	}
7182
7183
	/**
7184
	 * Function test if field can be null
7185
	 *
7186
	 * @param   array   $info   content informations of field
7187
	 * @return  bool			true if it can be null
7188
	 */
7189
	protected function canBeNull($info)
7190
	{
7191
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
7192
		{
7193
			if(isset($info['notnull']) && $info['notnull']!='1') return true;
7194
			else return false;
7195
		}
7196
		return true;
7197
	}
7198
7199
	/**
7200
	 * Function test if field is forced to null if zero or empty
7201
	 *
7202
	 * @param   array   $info   content informations of field
7203
	 * @return  bool			true if forced to null
7204
	 */
7205
	protected function isForcedToNullIfZero($info)
7206
	{
7207
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
7208
		{
7209
			if(isset($info['notnull']) && $info['notnull']=='-1') return true;
7210
			else return false;
7211
		}
7212
		return false;
7213
	}
7214
7215
	/**
7216
	 * Function test if is indexed
7217
	 *
7218
	 * @param   array   $info   content informations of field
7219
	 * @return                  bool
7220
	 */
7221
	protected function isIndex($info)
7222
	{
7223
		if(is_array($info))
1 ignored issue
show
introduced by
The condition is_array($info) is always true.
Loading history...
7224
		{
7225
			if(isset($info['index']) && $info['index']==true) return true;
7226
			else return false;
7227
		}
7228
		return false;
7229
	}
7230
7231
	/**
7232
	 * Function to prepare the values to insert.
7233
	 * Note $this->${field} are set by the page that make the createCommon or the updateCommon.
7234
	 *
7235
	 * @return array
7236
	 */
7237
	protected function setSaveQuery()
7238
	{
7239
		global $conf;
7240
7241
		$queryarray=array();
7242
		foreach ($this->fields as $field=>$info)	// Loop on definition of fields
7243
		{
7244
			// Depending on field type ('datetime', ...)
7245
			if($this->isDate($info))
7246
			{
7247
				if(empty($this->{$field}))
7248
				{
7249
					$queryarray[$field] = null;
7250
				}
7251
				else
7252
				{
7253
					$queryarray[$field] = $this->db->idate($this->{$field});
7254
				}
7255
			}
7256
			elseif($this->isArray($info))
7257
			{
7258
				if(! empty($this->{$field})) {
7259
					if(! is_array($this->{$field})) {
7260
						$this->{$field} = array($this->{$field});
7261
					}
7262
					$queryarray[$field] = serialize($this->{$field});
7263
				} else {
7264
					$queryarray[$field] = null;
7265
				}
7266
			}
7267
			elseif($this->isInt($info))
7268
			{
7269
				if ($field == 'entity' && is_null($this->{$field})) $queryarray[$field]=$conf->entity;
7270
				else
7271
				{
7272
					$queryarray[$field] = (int) price2num($this->{$field});
7273
					if (empty($queryarray[$field])) $queryarray[$field]=0;		// May be reset to null later if property 'notnull' is -1 for this field.
7274
				}
7275
			}
7276
			elseif($this->isFloat($info))
7277
			{
7278
				$queryarray[$field] = (double) price2num($this->{$field});
7279
				if (empty($queryarray[$field])) $queryarray[$field]=0;
7280
			}
7281
			else
7282
			{
7283
				$queryarray[$field] = $this->{$field};
7284
			}
7285
7286
			if ($info['type'] == 'timestamp' && empty($queryarray[$field])) unset($queryarray[$field]);
7287
			if (! empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) $queryarray[$field] = null;
7288
		}
7289
7290
		return $queryarray;
7291
	}
7292
7293
	/**
7294
	 * Function to load data from a SQL pointer into properties of current object $this
7295
	 *
7296
	 * @param   stdClass    $obj    Contain data of object from database
7297
     * @return void
7298
	 */
7299
	protected function setVarsFromFetchObj(&$obj)
7300
	{
7301
		foreach ($this->fields as $field => $info)
7302
		{
7303
			if($this->isDate($info))
7304
			{
7305
				if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
7306
				else $this->{$field} = strtotime($obj->{$field});
7307
			}
7308
			elseif($this->isArray($info))
7309
			{
7310
				if(! empty($obj->{$field})) {
7311
					$this->{$field} = @unserialize($obj->{$field});
7312
					// Hack for data not in UTF8
7313
					if($this->{$field } === false) @unserialize(utf8_decode($obj->{$field}));
7314
				} else {
7315
					$this->{$field} = array();
7316
				}
7317
			}
7318
			elseif($this->isInt($info))
7319
			{
7320
				if ($field == 'rowid') $this->id = (int) $obj->{$field};
7321
				else
7322
				{
7323
					if ($this->isForcedToNullIfZero($info))
7324
					{
7325
						if (empty($obj->{$field})) $this->{$field} = null;
7326
						else $this->{$field} = (double) $obj->{$field};
7327
					}
7328
					else
7329
					{
7330
						$this->{$field} = (int) $obj->{$field};
7331
					}
7332
				}
7333
			}
7334
			elseif($this->isFloat($info))
7335
			{
7336
				if ($this->isForcedToNullIfZero($info))
7337
				{
7338
					if (empty($obj->{$field})) $this->{$field} = null;
7339
					else $this->{$field} = (double) $obj->{$field};
7340
				}
7341
				else
7342
				{
7343
					$this->{$field} = (double) $obj->{$field};
7344
				}
7345
			}
7346
			else
7347
			{
7348
				$this->{$field} = $obj->{$field};
7349
			}
7350
		}
7351
7352
		// If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
7353
		if (! isset($this->fields['ref']) && isset($this->id)) $this->ref = $this->id;
7354
	}
7355
7356
	/**
7357
	 * Function to concat keys of fields
7358
	 *
7359
	 * @return string
7360
	 */
7361
	protected function getFieldList()
7362
	{
7363
		$keys = array_keys($this->fields);
7364
		return implode(',', $keys);
7365
	}
7366
7367
	/**
7368
	 * Add quote to field value if necessary
7369
	 *
7370
	 * @param 	string|int	$value			Value to protect
7371
	 * @param	array		$fieldsentry	Properties of field
7372
	 * @return 	string
7373
	 */
7374
	protected function quote($value, $fieldsentry)
7375
	{
7376
		if (is_null($value)) return 'NULL';
1 ignored issue
show
introduced by
The condition is_null($value) is always false.
Loading history...
7377
		elseif (preg_match('/^(int|double|real)/i', $fieldsentry['type'])) return $this->db->escape("$value");
7378
		else return "'".$this->db->escape($value)."'";
7379
	}
7380
7381
7382
	/**
7383
	 * Create object into database
7384
	 *
7385
	 * @param  User $user      User that creates
7386
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
7387
	 * @return int             <0 if KO, Id of created object if OK
7388
	 */
7389
	public function createCommon(User $user, $notrigger = false)
7390
	{
7391
		global $langs;
7392
7393
		$error = 0;
7394
7395
		$now=dol_now();
7396
7397
		$fieldvalues = $this->setSaveQuery();
7398
		if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) $fieldvalues['date_creation']=$this->db->idate($now);
7399
		if (array_key_exists('fk_user_creat', $fieldvalues) && ! ($fieldvalues['fk_user_creat'] > 0)) $fieldvalues['fk_user_creat']=$user->id;
7400
		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
7401
		if (array_key_exists('ref', $fieldvalues)) $fieldvalues['ref']=dol_string_nospecial($fieldvalues['ref']);					// If field is a ref,we sanitize data
7402
		
7403
		$keys=array();
7404
		$values = array();
7405
		foreach ($fieldvalues as $k => $v) {
7406
			$keys[$k] = $k;
7407
			$value = $this->fields[$k];
7408
			$values[$k] = $this->quote($v, $value);
7409
		}
7410
7411
		// Clean and check mandatory
7412
		foreach($keys as $key)
7413
		{
7414
			// If field is an implicit foreign key field
7415
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';
7416
			if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';
7417
7418
			//var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7419
			if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && ! isset($values[$key]) && is_null($val['default']))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $val does not exist. Did you maybe mean $value?
Loading history...
7420
			{
7421
				$error++;
7422
				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7423
			}
7424
7425
			// If field is an implicit foreign key field
7426
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) $values[$key]='null';
7427
			if (! empty($this->fields[$key]['foreignkey']) && empty($values[$key])) $values[$key]='null';
7428
		}
7429
7430
		if ($error) return -1;
7431
7432
		$this->db->begin();
7433
7434
		if (! $error)
7435
		{
7436
			$sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
7437
			$sql.= ' ('.implode(", ", $keys).')';
7438
			$sql.= ' VALUES ('.implode(", ", $values).')';
7439
7440
			$res = $this->db->query($sql);
7441
			if ($res===false) {
0 ignored issues
show
introduced by
The condition $res === false is always false.
Loading history...
7442
				$error++;
7443
				$this->errors[] = $this->db->lasterror();
7444
			}
7445
		}
7446
7447
		if (! $error)
7448
		{
7449
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
7450
		}
7451
7452
		// If we have a field ref with a default value of (PROV)
7453
		if (! $error)
7454
		{
7455
		    if (key_exists('ref', $this->fields) && $this->fields['ref']['notnull'] > 0 && ! is_null($this->fields['ref']['default']) && $this->fields['ref']['default'] == '(PROV)')
7456
		    {
7457
		        $sql="UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ref = '(PROV".$this->id.")' WHERE ref = '(PROV)' AND rowid = ".$this->id;
7458
		        $resqlupdate = $this->db->query($sql);
7459
		        if ($resqlupdate===false)
0 ignored issues
show
introduced by
The condition $resqlupdate === false is always false.
Loading history...
7460
		        {
7461
		            $error++;
7462
		            $this->errors[] = $this->db->lasterror();
7463
		        }
7464
		    }
7465
		}
7466
7467
		// Create extrafields
7468
		if (! $error)
7469
		{
7470
			$result=$this->insertExtraFields();
7471
			if ($result < 0) $error++;
7472
		}
7473
7474
		// Create lines
7475
		if (! empty($this->table_element_line) && ! empty($this->fk_element))
7476
		{
7477
			$num=(is_array($this->lines) ? count($this->lines) : 0);
1 ignored issue
show
introduced by
The condition is_array($this->lines) is always true.
Loading history...
7478
			for ($i = 0; $i < $num; $i++)
7479
			{
7480
				$line = $this->lines[$i];
7481
7482
				$keyforparent = $this->fk_element;
7483
				$line->$keyforparent = $this->id;
7484
7485
				// Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
7486
				//if (! is_object($line)) $line=json_decode(json_encode($line), false);  // convert recursively array into object.
7487
				if (! is_object($line)) $line = (object) $line;
7488
7489
				$result = $line->create($user, 1);
7490
				if ($result < 0)
7491
				{
7492
					$this->error=$this->db->lasterror();
7493
					$this->db->rollback();
7494
					return -1;
7495
				}
7496
			}
7497
		}
7498
7499
		// Triggers
7500
		if (! $error && ! $notrigger)
7501
		{
7502
			// Call triggers
7503
			$result=$this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
7504
			if ($result < 0) { $error++; }
7505
			// End call triggers
7506
		}
7507
7508
		// Commit or rollback
7509
		if ($error) {
7510
			$this->db->rollback();
7511
			return -1;
7512
		} else {
7513
			$this->db->commit();
7514
			return $this->id;
7515
		}
7516
	}
7517
7518
7519
	/**
7520
	 * Load object in memory from the database
7521
	 *
7522
	 * @param	int    $id				Id object
7523
	 * @param	string $ref				Ref
7524
	 * @param	string	$morewhere		More SQL filters (' AND ...')
7525
	 * @return 	int         			<0 if KO, 0 if not found, >0 if OK
7526
	 */
7527
	public function fetchCommon($id, $ref = null, $morewhere = '')
7528
	{
7529
		if (empty($id) && empty($ref) && empty($morewhere)) return -1;
7530
7531
		$sql = 'SELECT '.$this->getFieldList();
7532
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
7533
7534
		if (!empty($id))  $sql.= ' WHERE rowid = '.$id;
7535
		elseif (!empty($ref)) $sql.= " WHERE ref = ".$this->quote($ref, $this->fields['ref']);
7536
		else $sql.=' WHERE 1 = 1';	// usage with empty id and empty ref is very rare
7537
		if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql.=' AND entity IN ('.getEntity($this->table_element).')';
7538
		if ($morewhere)   $sql.= $morewhere;
7539
		$sql.=' LIMIT 1';	// This is a fetch, to be sure to get only one record
7540
7541
		$res = $this->db->query($sql);
7542
		if ($res)
7543
		{
7544
			$obj = $this->db->fetch_object($res);
7545
			if ($obj)
7546
			{
7547
				$this->setVarsFromFetchObj($obj);
7548
				return $this->id;
7549
			}
7550
			else
7551
			{
7552
				return 0;
7553
			}
7554
		}
7555
		else
7556
		{
7557
			$this->error = $this->db->lasterror();
7558
			$this->errors[] = $this->error;
7559
			return -1;
7560
		}
7561
	}
7562
7563
	/**
7564
	 * Load object in memory from the database
7565
	 *
7566
	 * @param	string	$morewhere		More SQL filters (' AND ...')
7567
	 * @return 	int         			<0 if KO, 0 if not found, >0 if OK
7568
	 */
7569
	public function fetchLinesCommon($morewhere = '')
7570
	{
7571
		$objectlineclassname = get_class($this).'Line';
7572
		if (! class_exists($objectlineclassname))
7573
		{
7574
			$this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
7575
			return -1;
7576
		}
7577
7578
		$objectline = new $objectlineclassname($this->db);
7579
7580
		$sql = 'SELECT '.$objectline->getFieldList();
7581
		$sql.= ' FROM '.MAIN_DB_PREFIX.$objectline->table_element;
7582
		$sql.=' WHERE fk_'.$this->element.' = '.$this->id;
7583
		if ($morewhere)   $sql.= $morewhere;
7584
7585
		$resql = $this->db->query($sql);
7586
		if ($resql)
7587
		{
7588
			$num_rows = $this->db->num_rows($resql);
7589
			$i = 0;
7590
			while ($i < $num_rows)
7591
			{
7592
				$obj = $this->db->fetch_object($resql);
7593
				if ($obj)
7594
				{
7595
					$newline = new $objectlineclassname($this->db);
7596
					$newline->setVarsFromFetchObj($obj);
7597
7598
					$this->lines[] = $newline;
7599
				}
7600
				$i++;
7601
			}
7602
7603
			return 1;
7604
		}
7605
		else
7606
		{
7607
			$this->error = $this->db->lasterror();
7608
			$this->errors[] = $this->error;
7609
			return -1;
7610
		}
7611
	}
7612
7613
	/**
7614
	 * Update object into database
7615
	 *
7616
	 * @param  User $user      	User that modifies
7617
	 * @param  bool $notrigger 	false=launch triggers after, true=disable triggers
7618
	 * @return int             	<0 if KO, >0 if OK
7619
	 */
7620
	public function updateCommon(User $user, $notrigger = false)
7621
	{
7622
		global $conf, $langs;
7623
7624
		$error = 0;
7625
7626
		$now=dol_now();
7627
7628
		$fieldvalues = $this->setSaveQuery();
7629
		if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) $fieldvalues['date_modification']=$this->db->idate($now);
7630
		if (array_key_exists('fk_user_modif', $fieldvalues) && ! ($fieldvalues['fk_user_modif'] > 0)) $fieldvalues['fk_user_modif']=$user->id;
7631
		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
7632
7633
		$keys=array();
7634
		$values = array();
7635
		foreach ($fieldvalues as $k => $v) {
7636
			$keys[$k] = $k;
7637
			$value = $this->fields[$k];
7638
			$values[$k] = $this->quote($v, $value);
7639
			$tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
7640
		}
7641
7642
		// Clean and check mandatory
7643
		foreach($keys as $key)
7644
		{
7645
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';		// This is an implicit foreign key field
7646
			if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';					// This is an explicit foreign key field
7647
7648
			//var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7649
			/*
7650
			if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
7651
			{
7652
				$error++;
7653
				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7654
			}*/
7655
		}
7656
7657
		$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 7635. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
7658
7659
		$this->db->begin();
7660
		if (! $error)
7661
		{
7662
			$res = $this->db->query($sql);
7663
			if ($res===false)
0 ignored issues
show
introduced by
The condition $res === false is always false.
Loading history...
7664
			{
7665
				$error++;
7666
				$this->errors[] = $this->db->lasterror();
7667
			}
7668
		}
7669
7670
		// Update extrafield
7671
		if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
7672
		{
7673
			$result=$this->insertExtraFields();
7674
			if ($result < 0)
7675
			{
7676
				$error++;
7677
			}
7678
		}
7679
7680
		// Triggers
7681
		if (! $error && ! $notrigger)
7682
		{
7683
			// Call triggers
7684
			$result=$this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
7685
			if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail
7686
			// End call triggers
7687
		}
7688
7689
		// Commit or rollback
7690
		if ($error) {
7691
			$this->db->rollback();
7692
			return -1;
7693
		} else {
7694
			$this->db->commit();
7695
			return $this->id;
7696
		}
7697
	}
7698
7699
	/**
7700
	 * Delete object in database
7701
	 *
7702
	 * @param 	User 	$user       			User that deletes
7703
	 * @param 	bool 	$notrigger  			false=launch triggers after, true=disable triggers
7704
	 * @param	int		$forcechilddeletion		0=no, 1=Force deletion of children
7705
	 * @return 	int             				<=0 if KO, >0 if OK
7706
	 */
7707
	public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0)
7708
	{
7709
		$error=0;
7710
7711
		$this->db->begin();
7712
7713
		if ($forcechilddeletion)	// Force also delete of childtables that should lock deletion in standard case when option force is off
7714
		{
7715
			foreach($this->childtables as $table)
7716
			{
7717
				$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$table.' WHERE '.$this->fk_element.' = '.$this->id;
7718
				$resql = $this->db->query($sql);
7719
				if (! $resql)
7720
				{
7721
					$this->error=$this->db->lasterror();
7722
					$this->errors[]=$this->error;
7723
					$this->db->rollback();
7724
					return -1;
7725
				}
7726
			}
7727
		}
7728
		elseif (! empty($this->fk_element) && ! empty($this->childtables))	// If object has childs linked with a foreign key field, we check all child tables.
7729
		{
7730
			$objectisused = $this->isObjectUsed($this->id);
7731
			if (! empty($objectisused))
7732
			{
7733
				dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
7734
				$this->error='ErrorRecordHasChildren';
7735
				$this->errors[]=$this->error;
7736
				$this->db->rollback();
7737
				return 0;
7738
			}
7739
		}
7740
7741
		// Delete cascade first
7742
		if (! empty($this->childtablesoncascade)) {
7743
            foreach($this->childtablesoncascade as $table)
7744
            {
7745
                $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$table.' WHERE '.$this->fk_element.' = '.$this->id;
7746
                $resql = $this->db->query($sql);
7747
                if (! $resql)
7748
                {
7749
                    $this->error=$this->db->lasterror();
7750
                    $this->errors[]=$this->error;
7751
                    $this->db->rollback();
7752
                    return -1;
7753
                }
7754
            }
7755
        }
7756
7757
		if (! $error) {
7758
			if (! $notrigger) {
7759
				// Call triggers
7760
				$result=$this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
7761
				if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail
7762
				// End call triggers
7763
			}
7764
		}
7765
7766
		if (! $error && ! empty($this->isextrafieldmanaged))
7767
		{
7768
			$sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element."_extrafields";
7769
			$sql.= " WHERE fk_object=" . $this->id;
7770
7771
			$resql = $this->db->query($sql);
7772
			if (! $resql)
7773
			{
7774
				$this->errors[] = $this->db->lasterror();
7775
				$error++;
7776
			}
7777
		}
7778
7779
		if (! $error)
7780
		{
7781
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
7782
7783
			$res = $this->db->query($sql);
7784
			if($res===false) {
0 ignored issues
show
introduced by
The condition $res === false is always false.
Loading history...
7785
				$error++;
7786
				$this->errors[] = $this->db->lasterror();
7787
			}
7788
		}
7789
7790
		// Commit or rollback
7791
		if ($error) {
7792
			$this->db->rollback();
7793
			return -1;
7794
		} else {
7795
			$this->db->commit();
7796
			return 1;
7797
		}
7798
	}
7799
7800
	/**
7801
	 *  Delete a line of object in database
7802
	 *
7803
	 *	@param  User	$user       User that delete
7804
	 *  @param	int		$idline		Id of line to delete
7805
	 *  @param 	bool 	$notrigger  false=launch triggers after, true=disable triggers
7806
	 *  @return int         		>0 if OK, <0 if KO
7807
	 */
7808
	public function deleteLineCommon(User $user, $idline, $notrigger = false)
7809
	{
7810
		global $conf;
7811
7812
		$error=0;
7813
7814
		$tmpforobjectclass = get_class($this);
7815
		$tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
7816
7817
		// Call trigger
7818
		$result=$this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
7819
		if ($result < 0) return -1;
7820
		// End call triggers
7821
7822
		$this->db->begin();
7823
7824
		$sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element_line;
7825
		$sql.= " WHERE rowid=".$idline;
7826
7827
		dol_syslog(get_class($this)."::deleteLineCommon", LOG_DEBUG);
7828
		$resql = $this->db->query($sql);
7829
		if (! $resql)
7830
		{
7831
			$this->error="Error ".$this->db->lasterror();
7832
			$error++;
7833
		}
7834
7835
		if (empty($error)) {
7836
			// Remove extrafields
7837
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
7838
			{
7839
				$tmpobjectline = new $tmpforobjectlineclass($this->db);
7840
				$tmpobjectline->id= $idline;
7841
				$result=$tmpobjectline->deleteExtraFields();
7842
				if ($result < 0)
7843
				{
7844
					$error++;
7845
					$this->error="Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
7846
				}
7847
			}
7848
		}
7849
7850
		if (empty($error)) {
7851
			$this->db->commit();
7852
			return 1;
7853
		} else {
7854
			dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
7855
			$this->db->rollback();
7856
			return -1;
7857
		}
7858
	}
7859
7860
	/**
7861
	 * Initialise object with example values
7862
	 * Id must be 0 if object instance is a specimen
7863
	 *
7864
	 * @return void
7865
	 */
7866
	public function initAsSpecimenCommon()
7867
	{
7868
	    global $user;
7869
7870
		$this->id = 0;
7871
		if (array_key_exists('label', $this->fields)) $this->label='This is label';
7872
		if (array_key_exists('note_public', $this->fields)) $this->note_public='Public note';
7873
		if (array_key_exists('note_private', $this->fields)) $this->note_private='Private note';
7874
		if (array_key_exists('date_creation', $this->fields)) $this->date_creation=(dol_now()-3600*24);
7875
		if (array_key_exists('date_modification', $this->fields)) $this->date_modification=(dol_now()-3600*24);
7876
		if (array_key_exists('fk_user_creat', $this->fields)) $this->fk_user_creat=$user->id;
7877
		if (array_key_exists('fk_user_modif', $this->fields)) $this->fk_user_modif=$user->id;
7878
		if (array_key_exists('date', $this->fields)) $this->date=dol_now();
7879
		// ...
7880
	}
7881
7882
7883
	/* Part for comments */
7884
7885
	/**
7886
	 * Load comments linked with current task
7887
	 *	@return boolean	1 if ok
7888
	 */
7889
	public function fetchComments()
7890
	{
7891
		require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
7892
7893
		$comment = new Comment($this->db);
7894
		$result=$comment->fetchAllFor($this->element, $this->id);
7895
		if ($result<0) {
7896
			$this->errors=array_merge($this->errors, $comment->errors);
7897
			return -1;
7898
		} else {
7899
			$this->comments = $comment->comments;
7900
		}
7901
		return count($this->comments);
7902
	}
7903
7904
    /**
7905
     * Return nb comments already posted
7906
     *
7907
     * @return int
7908
     */
7909
    public function getNbComments()
7910
    {
7911
        return count($this->comments);
7912
    }
7913
7914
    /**
7915
     * Trim object parameters
7916
     * @param string[] $parameters array of parameters to trim
7917
     *
7918
     * @return void
7919
     */
7920
    public function trimParameters($parameters)
7921
    {
7922
        if (!is_array($parameters)) return;
1 ignored issue
show
introduced by
The condition is_array($parameters) is always true.
Loading history...
7923
        foreach ($parameters as $parameter) {
7924
            if (isset($this->$parameter)) {
7925
                $this->$parameter = trim($this->$parameter);
7926
            }
7927
        }
7928
    }
7929
7930
    /**
7931
     *  copy related categories to another object
7932
     *
7933
     * @param  int		$fromId	Id object source
7934
     * @param  int		$toId	Id object cible
7935
     * @param  string	$type	Type of category ('product', ...)
7936
     * @return int      < 0 si erreur, > 0 si ok
7937
     */
7938
	public function cloneCategories($fromId, $toId, $type = '')
7939
	{
7940
		$this->db->begin();
7941
7942
		if (empty($type)) $type = $this->table_element;
7943
7944
		require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7945
		$categorystatic = new Categorie($this->db);
7946
7947
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie_" . $categorystatic->MAP_CAT_TABLE[$type] . " (fk_categorie, fk_product)";
0 ignored issues
show
Bug introduced by
The property MAP_CAT_TABLE is declared protected in Categorie and cannot be accessed from this context.
Loading history...
7948
		$sql.= " SELECT fk_categorie, $toId FROM ".MAIN_DB_PREFIX."categorie_" . $categorystatic->MAP_CAT_TABLE[$type];
7949
		$sql.= " WHERE fk_product = '".$fromId."'";
7950
7951
		if (! $this->db->query($sql))
7952
		{
7953
			$this->db->rollback();die($sql);
7954
			return -1;
0 ignored issues
show
Unused Code introduced by
return -1 is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
7955
		}
7956
7957
		$this->db->commit();
7958
		return 1;
7959
	}
7960
}
7961