Passed
Branch develop (8fd327)
by
unknown
32:43
created

CommonObject::printOriginLine()   F

Complexity

Conditions 37
Paths > 20000

Size

Total Lines 141
Code Lines 80

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 37
eloc 80
c 0
b 0
f 0
nc 2052864
nop 5
dl 0
loc 141
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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-2019 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
	/**
425
     * @var integer|string date_creation
426
     */
427
	public $date_creation;
428
429
	/**
430
	 * @var integer|string $date_validation;
431
	 */
432
	public $date_validation; // Date validation
433
434
	/**
435
	 * @var integer|string $date_modification;
436
	 */
437
	public $date_modification; // Date last change (tms field)
438
439
	public $next_prev_filter;
440
441
442
443
	// No constructor as it is an abstract class
444
445
	/**
446
	 * Check an object id/ref exists
447
	 * If you don't need/want to instantiate object and just need to know if object exists, use this method instead of fetch
448
	 *
449
	 *  @param	string	$element   	String of element ('product', 'facture', ...)
450
	 *  @param	int		$id      	Id of object
451
	 *  @param  string	$ref     	Ref of object to check
452
	 *  @param	string	$ref_ext	Ref ext of object to check
453
	 *  @return int     			<0 if KO, 0 if OK but not found, >0 if OK and exists
454
	 */
455
	public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
456
	{
457
		global $db, $conf;
458
459
		$sql = "SELECT rowid, ref, ref_ext";
460
		$sql .= " FROM ".MAIN_DB_PREFIX.$element;
461
		$sql .= " WHERE entity IN (".getEntity($element).")";
462
463
		if ($id > 0) $sql .= " AND rowid = ".$db->escape($id);
464
		elseif ($ref) $sql .= " AND ref = '".$db->escape($ref)."'";
465
		elseif ($ref_ext) $sql .= " AND ref_ext = '".$db->escape($ref_ext)."'";
466
		else {
467
			$error = 'ErrorWrongParameters';
468
			dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
469
			return -1;
470
		}
471
		if ($ref || $ref_ext) $sql .= " AND entity = ".$conf->entity;
472
473
		dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
474
		$resql = $db->query($sql);
475
		if ($resql)
476
		{
477
			$num = $db->num_rows($resql);
478
			if ($num > 0) return 1;
479
			else return 0;
480
		}
481
		return -1;
482
	}
483
484
	/**
485
	 * Method to output saved errors
486
	 *
487
	 * @return	string		String with errors
488
	 */
489
	public function errorsToString()
490
	{
491
		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...
492
	}
493
494
	/**
495
	 * Return customer ref for screen output.
496
	 *
497
	 * @param  string      $objref        Customer ref
498
	 * @return string                     Customer ref formated
499
	 */
500
	public function getFormatedCustomerRef($objref)
501
	{
502
	    global $hookmanager;
503
504
	    $parameters = array('objref'=>$objref);
505
	    $action = '';
506
	    $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
507
	    if ($reshook > 0)
508
	    {
509
	        return $hookmanager->resArray['objref'];
510
	    }
511
	    return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
512
	}
513
514
	/**
515
	 * Return supplier ref for screen output.
516
	 *
517
	 * @param  string      $objref        Supplier ref
518
	 * @return string                     Supplier ref formated
519
	 */
520
	public function getFormatedSupplierRef($objref)
521
	{
522
	    global $hookmanager;
523
524
	    $parameters = array('objref'=>$objref);
525
	    $action = '';
526
	    $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
527
	    if ($reshook > 0)
528
	    {
529
	        return $hookmanager->resArray['objref'];
530
	    }
531
	    return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
532
	}
533
534
	/**
535
	 *	Return full name (civility+' '+name+' '+lastname)
536
	 *
537
	 *	@param	Translate	$langs			Language object for translation of civility (used only if option is 1)
538
	 *	@param	int			$option			0=No option, 1=Add civility
539
	 * 	@param	int			$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname, 3=Firstname if defined else lastname
540
	 * 	@param	int			$maxlen			Maximum length
541
	 * 	@return	string						String with full name
542
	 */
543
	public function getFullName($langs, $option = 0, $nameorder = -1, $maxlen = 0)
544
	{
545
		//print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n";
546
		$lastname = $this->lastname;
547
		$firstname = $this->firstname;
548
		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 : '')))));
549
550
		$ret = '';
551
		if ($option && $this->civility_id)
552
		{
553
			if ($langs->transnoentitiesnoconv("Civility".$this->civility_id) != "Civility".$this->civility_id) $ret .= $langs->transnoentitiesnoconv("Civility".$this->civility_id).' ';
554
			else $ret .= $this->civility_id.' ';
555
		}
556
557
		$ret .= dolGetFirstLastname($firstname, $lastname, $nameorder);
558
559
		return dol_trunc($ret, $maxlen);
560
	}
561
562
	/**
563
	 * 	Return full address of contact
564
	 *
565
	 * 	@param		int			$withcountry		1=Add country into address string
566
	 *  @param		string		$sep				Separator to use to build string
567
	 *  @param		int		    $withregion			1=Add region into address string
568
	 *	@return		string							Full address string
569
	 */
570
	public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0)
571
	{
572
		if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country)))
573
		{
574
			require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
575
			$tmparray = getCountry($this->country_id, 'all');
576
			$this->country_code = $tmparray['code'];
577
			$this->country = $tmparray['label'];
578
		}
579
580
        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...
581
    	{
582
    		require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
583
    		$tmparray = getState($this->state_id, 'all', 0, 1);
584
			$this->state_code   = $tmparray['code'];
585
			$this->state        = $tmparray['label'];
586
			$this->region_code  = $tmparray['region_code'];
587
			$this->region       = $tmparray['region'];
588
        }
589
590
		return dol_format_address($this, $withcountry, $sep);
591
	}
592
593
594
	/**
595
	 * 	Return full address for banner
596
	 *
597
	 * 	@param		string		$htmlkey            HTML id to make banner content unique
598
	 *  @param      Object      $object				Object (thirdparty, thirdparty of contact for contact, null for a member)
599
	 *	@return		string							Full address string
600
	 */
601
	public function getBannerAddress($htmlkey, $object)
602
	{
603
		global $conf, $langs;
604
605
		$countriesusingstate = array('AU', 'US', 'IN', 'GB', 'ES', 'UK', 'TR'); // See also option MAIN_FORCE_STATE_INTO_ADDRESS
606
607
		$contactid = 0;
608
		$thirdpartyid = 0;
609
		if ($this->element == 'societe')
610
		{
611
			$thirdpartyid = $this->id;
612
		}
613
		if ($this->element == 'contact')
614
		{
615
			$contactid = $this->id;
616
			$thirdpartyid = $object->fk_soc;
617
		}
618
		if ($this->element == 'user')
619
		{
620
			$contactid = $this->contact_id;
621
			$thirdpartyid = $object->fk_soc;
622
		}
623
624
		$out = '<!-- BEGIN part to show address block -->';
625
626
		$outdone = 0;
627
		$coords = $this->getFullAddress(1, ', ', $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT);
628
		if ($coords)
629
		{
630
			if (!empty($conf->use_javascript_ajax))
631
			{
632
				$namecoords = '';
633
				if ($this->element == 'contact' && !empty($conf->global->MAIN_SHOW_COMPANY_NAME_IN_BANNER_ADDRESS))
634
				{
635
					$namecoords .= $object->name.'<br>';
636
				}
637
				$namecoords .= $this->getFullName($langs, 1).'<br>'.$coords;
638
				// hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile
639
				$out .= '<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">';
640
				$out .= img_picto($langs->trans("Address"), 'object_address.png');
641
				$out .= '</a> ';
642
			}
643
			$out .= dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); $outdone++;
644
			$outdone++;
645
		}
646
647
		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
648
				&& empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state)
649
		{
650
            if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) {
651
                $out .= ($outdone ? ' - ' : '').$this->region.' - '.$this->state;
652
            }
653
            else {
654
                $out .= ($outdone ? ' - ' : '').$this->state;
655
            }
656
			$outdone++;
657
		}
658
659
		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>' : '');
660
		if (!empty($this->phone) && empty($this->phone_pro)) {		// For objects that store pro phone into ->phone
661
			$out .= dol_print_phone($this->phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro")); $outdone++;
662
		}
663
		if (!empty($this->phone_pro)) {
664
			$out .= dol_print_phone($this->phone_pro, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro")); $outdone++;
665
		}
666
		if (!empty($this->phone_mobile)) {
667
			$out .= dol_print_phone($this->phone_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'mobile', $langs->trans("PhoneMobile")); $outdone++;
668
		}
669
		if (!empty($this->phone_perso)) {
670
			$out .= dol_print_phone($this->phone_perso, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePerso")); $outdone++;
671
		}
672
		if (!empty($this->office_phone)) {
673
			$out .= dol_print_phone($this->office_phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro")); $outdone++;
674
		}
675
		if (!empty($this->user_mobile)) {
676
			$out .= dol_print_phone($this->user_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'mobile', $langs->trans("PhoneMobile")); $outdone++;
677
		}
678
		if (!empty($this->fax)) {
679
			$out .= dol_print_phone($this->fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', '&nbsp;', 'fax', $langs->trans("Fax")); $outdone++;
680
		}
681
		if (!empty($this->office_fax)) {
682
			$out .= dol_print_phone($this->office_fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', '&nbsp;', 'fax', $langs->trans("Fax")); $outdone++;
683
		}
684
685
		$out .= '<div style="clear: both;"></div>';
686
		$outdone = 0;
687
		if (!empty($this->email))
688
		{
689
			$out .= dol_print_email($this->email, $this->id, $object->id, 'AC_EMAIL', 0, 0, 1);
690
			$outdone++;
691
		}
692
		if (!empty($this->url))
693
		{
694
            //$out.=dol_print_url($this->url,'_goout',0,1);//steve changed to blank
695
		    $out .= dol_print_url($this->url, '_blank', 0, 1);
696
			$outdone++;
697
		}
698
		$out .= '<div style="clear: both;">';
699
		if (!empty($conf->socialnetworks->enabled))
700
		{
701
			if (is_array($this->socialnetworks) && count($this->socialnetworks) > 0) {
702
				foreach ($this->socialnetworks as $key => $value) {
703
					$out .= dol_print_socialnetworks($value, $this->id, $object->id, $key);
704
					$outdone++;
705
				}
706
			} else {
707
				if ($this->skype) $out .= dol_print_socialnetworks($this->skype, $this->id, $object->id, 'skype');
708
				$outdone++;
709
				if ($this->jabberid) $out .= dol_print_socialnetworks($this->jabberid, $this->id, $object->id, 'jabber');
710
				$outdone++;
711
				if ($this->twitter) $out .= dol_print_socialnetworks($this->twitter, $this->id, $object->id, 'twitter');
712
				$outdone++;
713
				if ($this->facebook) $out .= dol_print_socialnetworks($this->facebook, $this->id, $object->id, 'facebook');
714
				$outdone++;
715
				if ($this->linkedin) $out .= dol_print_socialnetworks($this->linkedin, $this->id, $object->id, 'linkedin');
716
				$outdone++;
717
			}
718
		}
719
		$out .= '</div>';
720
721
		$out .= '<!-- END Part to show address block -->';
722
723
		return $out;
724
	}
725
726
	/**
727
	 * Return the link of last main doc file for direct public download.
728
	 *
729
	 * @param	string	$modulepart			Module related to document
730
	 * @param	int		$initsharekey		Init the share key if it was not yet defined
731
	 * @param	int		$relativelink		0=Return full external link, 1=Return link relative to root of file
732
	 * @return	string						Link or empty string if there is no download link
733
	 */
734
	public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
735
	{
736
		global $user, $dolibarr_main_url_root;
737
738
		if (empty($this->last_main_doc))
739
		{
740
			return ''; // No way to known which document name to use
741
		}
742
743
		include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
744
		$ecmfile = new EcmFiles($this->db);
745
		$result = $ecmfile->fetch(0, '', $this->last_main_doc);
746
		if ($result < 0)
747
		{
748
			$this->error = $ecmfile->error;
749
			$this->errors = $ecmfile->errors;
750
			return -1;
751
		}
752
753
		if (empty($ecmfile->id))
754
		{
755
			// Add entry into index
756
			if ($initsharekey)
757
			{
758
				require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
759
				// TODO We can't, we dont' have full path of file, only last_main_doc adn ->element, so we must rebuild full path first
760
				/*
761
				$ecmfile->filepath = $rel_dir;
762
				$ecmfile->filename = $filename;
763
				$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
764
				$ecmfile->fullpath_orig = '';
765
				$ecmfile->gen_or_uploaded = 'generated';
766
				$ecmfile->description = '';    // indexed content
767
				$ecmfile->keyword = '';        // keyword content
768
				$ecmfile->share = getRandomPassword(true);
769
				$result = $ecmfile->create($user);
770
				if ($result < 0)
771
				{
772
					$this->error = $ecmfile->error;
773
					$this->errors = $ecmfile->errors;
774
				}
775
				*/
776
			}
777
			else return '';
778
		}
779
		elseif (empty($ecmfile->share))
780
		{
781
			// Add entry into index
782
			if ($initsharekey)
783
			{
784
				require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
785
				$ecmfile->share = getRandomPassword(true);
786
				$ecmfile->update($user);
787
			}
788
			else return '';
789
		}
790
		// Define $urlwithroot
791
		$urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
792
		// This is to use external domain name found into config file
793
		//if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
794
		//else
795
		$urlwithroot = $urlwithouturlroot.DOL_URL_ROOT;
796
		//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
797
798
		$forcedownload = 0;
799
800
		$paramlink = '';
801
		//if (! empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart;		// For sharing with hash (so public files), modulepart is not required.
802
		//if (! empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; 					// For sharing with hash (so public files), entity is not required.
803
		//$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath);								// No need of name of file for public link, we will use the hash
804
		if (!empty($ecmfile->share)) $paramlink .= ($paramlink ? '&' : '').'hashp='.$ecmfile->share; // Hash for public share
805
		if ($forcedownload) $paramlink .= ($paramlink ? '&' : '').'attachment=1';
806
807
		if ($relativelink)
808
		{
809
			$linktoreturn = 'document.php'.($paramlink ? '?'.$paramlink : '');
810
		}
811
		else
812
		{
813
			$linktoreturn = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : '');
814
		}
815
816
		// Here $ecmfile->share is defined
817
		return $linktoreturn;
818
	}
819
820
821
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
822
	/**
823
	 *  Add a link between element $this->element and a contact
824
	 *
825
	 *  @param	int		$fk_socpeople       Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
826
	 *  @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
827
	 *  @param  string	$source             external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
828
	 *  @param  int		$notrigger			Disable all triggers
829
	 *  @return int                 		<0 if KO, >0 if OK
830
	 */
831
	public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
832
	{
833
        // phpcs:enable
834
		global $user, $langs;
835
836
837
		dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
838
839
		// Check parameters
840
		if ($fk_socpeople <= 0)
841
		{
842
			$langs->load("errors");
843
			$this->error = $langs->trans("ErrorWrongValueForParameterX", "1");
844
			dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
845
			return -1;
846
		}
847
		if (!$type_contact)
848
		{
849
			$langs->load("errors");
850
			$this->error = $langs->trans("ErrorWrongValueForParameterX", "2");
851
			dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
852
			return -2;
853
		}
854
855
		$id_type_contact = 0;
856
		if (is_numeric($type_contact))
857
		{
858
			$id_type_contact = $type_contact;
859
		}
860
		else
861
		{
862
			// We look for id type_contact
863
			$sql = "SELECT tc.rowid";
864
			$sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
865
			$sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
866
			$sql .= " AND tc.source='".$this->db->escape($source)."'";
867
			$sql .= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
868
			//print $sql;
869
			$resql = $this->db->query($sql);
870
			if ($resql)
871
			{
872
				$obj = $this->db->fetch_object($resql);
873
				if ($obj) $id_type_contact = $obj->rowid;
874
			}
875
		}
876
877
		if ($id_type_contact == 0)
878
		{
879
			$this->error = 'CODE_NOT_VALID_FOR_THIS_ELEMENT';
880
			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");
881
			return -3;
882
		}
883
884
		$datecreate = dol_now();
885
886
		// Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
887
		$TListeContacts = $this->liste_contact(-1, $source);
888
		$already_added = false;
889
		if (is_array($TListeContacts) && !empty($TListeContacts)) {
0 ignored issues
show
introduced by
The condition is_array($TListeContacts) is always false.
Loading history...
890
			foreach ($TListeContacts as $array_contact) {
891
				if ($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
892
					$already_added = true;
893
					break;
894
				}
895
			}
896
		}
897
898
		if (!$already_added) {
1 ignored issue
show
introduced by
The condition $already_added is always false.
Loading history...
899
			$this->db->begin();
900
901
			// Insert into database
902
			$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact";
903
			$sql .= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
904
			$sql .= " VALUES (".$this->id.", ".$fk_socpeople." , ";
905
			$sql .= "'".$this->db->idate($datecreate)."'";
906
			$sql .= ", 4, ".$id_type_contact;
907
			$sql .= ")";
908
909
			$resql = $this->db->query($sql);
910
			if ($resql)
911
			{
912
				if (!$notrigger)
913
				{
914
					$result = $this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
915
					if ($result < 0)
916
					{
917
						$this->db->rollback();
918
						return -1;
919
					}
920
				}
921
922
				$this->db->commit();
923
				return 1;
924
			}
925
			else
926
			{
927
				if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
928
				{
929
					$this->error = $this->db->errno();
930
					$this->db->rollback();
931
					echo 'err rollback';
932
					return -2;
933
				}
934
				else
935
				{
936
					$this->error = $this->db->error();
937
					$this->db->rollback();
938
					return -1;
939
				}
940
			}
941
		} else return 0;
942
	}
943
944
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
945
	/**
946
	 *    Copy contact from one element to current
947
	 *
948
	 *    @param    CommonObject    $objFrom    Source element
949
	 *    @param    string          $source     Nature of contact ('internal' or 'external')
950
	 *    @return   int                         >0 if OK, <0 if KO
951
	 */
952
	public function copy_linked_contact($objFrom, $source = 'internal')
953
	{
954
        // phpcs:enable
955
		$contacts = $objFrom->liste_contact(-1, $source);
956
		foreach ($contacts as $contact)
0 ignored issues
show
Bug introduced by
The expression $contacts of type integer is not traversable.
Loading history...
957
		{
958
			if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0)
959
			{
960
				$this->error = $this->db->lasterror();
961
				return -1;
962
			}
963
		}
964
		return 1;
965
	}
966
967
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
968
	/**
969
	 *      Update a link to contact line
970
	 *
971
	 *      @param	int		$rowid              Id of line contact-element
972
	 * 		@param	int		$statut	            New status of link
973
	 *      @param  int		$type_contact_id    Id of contact type (not modified if 0)
974
	 *      @param  int		$fk_socpeople	    Id of soc_people to update (not modified if 0)
975
	 *      @return int                 		<0 if KO, >= 0 if OK
976
	 */
977
	public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
978
	{
979
        // phpcs:enable
980
		// Insert into database
981
		$sql = "UPDATE ".MAIN_DB_PREFIX."element_contact set";
982
		$sql .= " statut = ".$statut;
983
		if ($type_contact_id) $sql .= ", fk_c_type_contact = '".$type_contact_id."'";
984
		if ($fk_socpeople) $sql .= ", fk_socpeople = '".$fk_socpeople."'";
985
		$sql .= " where rowid = ".$rowid;
986
		$resql = $this->db->query($sql);
987
		if ($resql)
988
		{
989
			return 0;
990
		}
991
		else
992
		{
993
			$this->error = $this->db->lasterror();
994
			return -1;
995
		}
996
	}
997
998
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
999
	/**
1000
	 *    Delete a link to contact line
1001
	 *
1002
	 *    @param	int		$rowid			Id of contact link line to delete
1003
	 *    @param	int		$notrigger		Disable all triggers
1004
	 *    @return   int						>0 if OK, <0 if KO
1005
	 */
1006
	public function delete_contact($rowid, $notrigger = 0)
1007
	{
1008
        // phpcs:enable
1009
		global $user;
1010
1011
1012
		$this->db->begin();
1013
1014
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1015
		$sql .= " WHERE rowid =".$rowid;
1016
1017
		dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
1018
		if ($this->db->query($sql))
1019
		{
1020
			if (!$notrigger)
1021
			{
1022
				$result = $this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
1023
				if ($result < 0) { $this->db->rollback(); return -1; }
1024
			}
1025
1026
			$this->db->commit();
1027
			return 1;
1028
		}
1029
		else
1030
		{
1031
			$this->error = $this->db->lasterror();
1032
			$this->db->rollback();
1033
			return -1;
1034
		}
1035
	}
1036
1037
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1038
	/**
1039
	 *    Delete all links between an object $this and all its contacts
1040
	 *
1041
	 *	  @param	string	$source		'' or 'internal' or 'external'
1042
	 *	  @param	string	$code		Type of contact (code or id)
1043
	 *    @return   int					>0 if OK, <0 if KO
1044
	 */
1045
	public function delete_linked_contact($source = '', $code = '')
1046
	{
1047
        // phpcs:enable
1048
		$temp = array();
1049
		$typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1050
1051
		foreach ($typeContact as $key => $value)
1052
		{
1053
			array_push($temp, $key);
1054
		}
1055
		$listId = implode(",", $temp);
1056
1057
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1058
		$sql .= " WHERE element_id = ".$this->id;
1059
		if ($listId)
1060
			$sql .= " AND fk_c_type_contact IN (".$listId.")";
1061
1062
		dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1063
		if ($this->db->query($sql))
1064
		{
1065
			return 1;
1066
		}
1067
		else
1068
		{
1069
			$this->error = $this->db->lasterror();
1070
			return -1;
1071
		}
1072
	}
1073
1074
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1075
	/**
1076
	 *    Get array of all contacts for an object
1077
	 *
1078
	 *    @param	int			$status		Status of links to get (-1=all)
1079
	 *    @param	string		$source		Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)
1080
	 *    @param	int         $list       0:Return array contains all properties, 1:Return array contains just id
1081
	 *    @param    string      $code       Filter on this code of contact type ('SHIPPING', 'BILLING', ...)
1082
	 *    @return	array|int		        Array of contacts, -1 if error
1083
	 */
1084
	public function liste_contact($status = -1, $source = 'external', $list = 0, $code = '')
1085
	{
1086
        // phpcs:enable
1087
		global $langs;
1088
1089
		$tab = array();
1090
1091
		$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
1092
		if ($source == 'internal') $sql .= ", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
1093
		if ($source == 'external' || $source == 'thirdparty') $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
1094
		$sql .= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1095
		$sql .= ", tc.source, tc.element, tc.code, tc.libelle";
1096
		$sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
1097
		$sql .= ", ".MAIN_DB_PREFIX."element_contact ec";
1098
		if ($source == 'internal') $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
1099
		if ($source == 'external' || $source == 'thirdparty') $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
1100
		$sql .= " WHERE ec.element_id =".$this->id;
1101
		$sql .= " AND ec.fk_c_type_contact=tc.rowid";
1102
		$sql .= " AND tc.element='".$this->db->escape($this->element)."'";
1103
		if ($code) $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1104
		if ($source == 'internal') $sql .= " AND tc.source = 'internal'";
1105
		if ($source == 'external' || $source == 'thirdparty') $sql .= " AND tc.source = 'external'";
1106
		$sql .= " AND tc.active=1";
1107
		if ($status >= 0) $sql .= " AND ec.statut = ".$status;
1108
		$sql .= " ORDER BY t.lastname ASC";
1109
1110
		dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1111
		$resql = $this->db->query($sql);
1112
		if ($resql)
1113
		{
1114
			$num = $this->db->num_rows($resql);
1115
			$i = 0;
1116
			while ($i < $num)
1117
			{
1118
				$obj = $this->db->fetch_object($resql);
1119
1120
				if (!$list)
1121
				{
1122
					$transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1123
					$libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1124
					$tab[$i] = array('source'=>$obj->source, 'socid'=>$obj->socid, 'id'=>$obj->id,
1125
								   'nom'=>$obj->lastname, // For backward compatibility
1126
								   'civility'=>$obj->civility, 'lastname'=>$obj->lastname, 'firstname'=>$obj->firstname, 'email'=>$obj->email, 'login'=>$obj->login, 'photo'=>$obj->photo, 'statuscontact'=>$obj->statuscontact,
1127
								   'rowid'=>$obj->rowid, 'code'=>$obj->code, 'libelle'=>$libelle_type, 'status'=>$obj->statuslink, 'fk_c_type_contact'=>$obj->fk_c_type_contact);
1128
				}
1129
				else
1130
				{
1131
					$tab[$i] = $obj->id;
1132
				}
1133
1134
				$i++;
1135
			}
1136
1137
			return $tab;
1138
		}
1139
		else
1140
		{
1141
			$this->error = $this->db->lasterror();
1142
			dol_print_error($this->db);
1143
			return -1;
1144
		}
1145
	}
1146
1147
1148
	/**
1149
	 * 		Update status of a contact linked to object
1150
	 *
1151
	 * 		@param	int		$rowid		Id of link between object and contact
1152
	 * 		@return	int					<0 if KO, >=0 if OK
1153
	 */
1154
	public function swapContactStatus($rowid)
1155
	{
1156
		$sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1157
		$sql .= " tc.code, tc.libelle";
1158
		//$sql.= ", s.fk_soc";
1159
		$sql .= " FROM (".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as tc)";
1160
		//$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
1161
		$sql .= " WHERE ec.rowid =".$rowid;
1162
		$sql .= " AND ec.fk_c_type_contact=tc.rowid";
1163
		$sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1164
1165
		dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1166
		$resql = $this->db->query($sql);
1167
		if ($resql)
1168
		{
1169
			$obj = $this->db->fetch_object($resql);
1170
			$newstatut = ($obj->statut == 4) ? 5 : 4;
1171
			$result = $this->update_contact($rowid, $newstatut);
1172
			$this->db->free($resql);
1173
			return $result;
1174
		}
1175
		else
1176
		{
1177
			$this->error = $this->db->error();
1178
			dol_print_error($this->db);
1179
			return -1;
1180
		}
1181
	}
1182
1183
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1184
	/**
1185
	 *      Return array with list of possible values for type of contacts
1186
	 *
1187
	 *      @param	string	$source     'internal', 'external' or 'all'
1188
	 *      @param	string	$order		Sort order by : 'position', 'code', 'rowid'...
1189
	 *      @param  int		$option     0=Return array id->label, 1=Return array code->label
1190
	 *      @param  int		$activeonly 0=all status of contact, 1=only the active
1191
	 *		@param	string	$code		Type of contact (Example: 'CUSTOMER', 'SERVICE')
1192
	 *      @return array       		Array list of type of contacts (id->label if option=0, code->label if option=1)
1193
	 */
1194
	public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1195
	{
1196
        // phpcs:enable
1197
		global $langs;
1198
1199
		if (empty($order)) $order = 'position';
1200
		if ($order == 'position') $order .= ',code';
1201
1202
		$tab = array();
1203
		$sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1204
		$sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
1205
		$sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1206
		if ($activeonly == 1) $sql .= " AND tc.active=1"; // only the active types
1207
		if (!empty($source) && $source != 'all') $sql .= " AND tc.source='".$this->db->escape($source)."'";
1208
		if (!empty($code)) $sql .= " AND tc.code='".$this->db->escape($code)."'";
1209
		$sql .= $this->db->order($order, 'ASC');
1210
1211
		//print "sql=".$sql;
1212
		$resql = $this->db->query($sql);
1213
		if ($resql)
1214
		{
1215
			$num = $this->db->num_rows($resql);
1216
			$i = 0;
1217
			while ($i < $num)
1218
			{
1219
				$obj = $this->db->fetch_object($resql);
1220
1221
				$transkey = "TypeContact_".$this->element."_".$source."_".$obj->code;
1222
				$libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1223
				if (empty($option)) $tab[$obj->rowid] = $libelle_type;
1224
				else $tab[$obj->code] = $libelle_type;
1225
				$i++;
1226
			}
1227
			return $tab;
1228
		}
1229
		else
1230
		{
1231
			$this->error = $this->db->lasterror();
1232
			//dol_print_error($this->db);
1233
			return null;
1234
		}
1235
	}
1236
1237
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1238
	/**
1239
	 *      Return array with list of possible values for type of contacts
1240
	 *
1241
	 *      @param	string	$source     'internal', 'external' or 'all'
1242
	 *      @param  int		$option     0=Return array id->label, 1=Return array code->label
1243
	 *      @param  int		$activeonly 0=all status of contact, 1=only the active
1244
	 *		@param	string	$code		Type of contact (Example: 'CUSTOMER', 'SERVICE')
1245
	 *		@param	string	$element	Filter Element Type
1246
	 *      @return array       		Array list of type of contacts (id->label if option=0, code->label if option=1)
1247
	 */
1248
	public function listeTypeContacts($source = 'internal', $option = 0, $activeonly = 0, $code = '', $element = '')
1249
	{
1250
		// phpcs:enable
1251
		global $langs, $conf;
1252
1253
		$tab = array();
1254
1255
		$sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position, tc.element";
1256
		$sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
1257
1258
		$sqlWhere = array();
1259
		if (!empty($element))
1260
			$sqlWhere[] = " tc.element='".$this->db->escape($element)."'";
1261
1262
		if ($activeonly == 1)
1263
			$sqlWhere[] = " tc.active=1"; // only the active types
1264
1265
		if (!empty($source) && $source != 'all')
1266
			$sqlWhere[] = " tc.source='".$this->db->escape($source)."'";
1267
1268
		if (!empty($code))
1269
			$sqlWhere[] = " tc.code='".$this->db->escape($code)."'";
1270
1271
		if (count($sqlWhere) > 0) {
1272
			$sql .= " WHERE ".implode(' AND ', $sqlWhere);
1273
		}
1274
1275
		$sql .= $this->db->order('tc.element, tc.position', 'ASC');
1276
1277
		dol_syslog(get_class($this)."::".__METHOD__, LOG_DEBUG);
1278
		$resql = $this->db->query($sql);
1279
		if ($resql) {
1280
			$num = $this->db->num_rows($resql);
1281
			if ($num > 0) {
1282
				while ($obj = $this->db->fetch_object($resql)) {
1283
					if (strpos($obj->element, 'project') !== false) {
1284
						$element = 'projet';
1285
					} elseif ($obj->element == 'contrat') {
1286
						$element = 'contract';
1287
					} elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1288
						$element = 'fournisseur';
1289
					} elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1290
						$element = 'fournisseur';
1291
					} else {
1292
						$element = $obj->element;
1293
					}
1294
					if ($conf->{$element}->enabled) {
1295
						$libelle_element = $langs->trans('ContactDefault_'.$obj->element);
1296
						$transkey = "TypeContact_".$this->element."_".$source."_".$obj->code;
1297
						$libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1298
						if (empty($option))
1299
							$tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1300
						else $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1301
					}
1302
				}
1303
			}
1304
			return $tab;
1305
		}
1306
		else
1307
		{
1308
			$this->error = $this->db->lasterror();
1309
			return null;
1310
		}
1311
	}
1312
1313
	/**
1314
	 *      Return id of contacts for a source and a contact code.
1315
	 *      Example: contact client de facturation ('external', 'BILLING')
1316
	 *      Example: contact client de livraison ('external', 'SHIPPING')
1317
	 *      Example: contact interne suivi paiement ('internal', 'SALESREPFOLL')
1318
	 *
1319
	 *		@param	string	$source		'external' or 'internal'
1320
	 *		@param	string	$code		'BILLING', 'SHIPPING', 'SALESREPFOLL', ...
1321
	 *		@param	int		$status		limited to a certain status
1322
	 *      @return array       		List of id for such contacts
1323
	 */
1324
	public function getIdContact($source, $code, $status = 0)
1325
	{
1326
		global $conf;
1327
1328
		$result = array();
1329
		$i = 0;
1330
		//cas particulier pour les expeditions
1331
		if ($this->element == 'shipping' && $this->origin_id != 0) {
1332
			$id = $this->origin_id;
1333
			$element = 'commande';
1334
		} elseif ($this->element == 'reception' && $this->origin_id != 0) {
1335
			$id = $this->origin_id;
1336
			$element = 'order_supplier';
1337
		} else {
1338
			$id = $this->id;
1339
			$element = $this->element;
1340
		}
1341
1342
		$sql = "SELECT ec.fk_socpeople";
1343
		$sql .= " FROM ".MAIN_DB_PREFIX."element_contact as ec,";
1344
		if ($source == 'internal') $sql .= " ".MAIN_DB_PREFIX."user as c,";
1345
		if ($source == 'external') $sql .= " ".MAIN_DB_PREFIX."socpeople as c,";
1346
		$sql .= " ".MAIN_DB_PREFIX."c_type_contact as tc";
1347
		$sql .= " WHERE ec.element_id = ".$id;
1348
		$sql .= " AND ec.fk_socpeople = c.rowid";
1349
		if ($source == 'internal') $sql .= " AND c.entity IN (".getEntity('user').")";
1350
		if ($source == 'external') $sql .= " AND c.entity IN (".getEntity('societe').")";
1351
		$sql .= " AND ec.fk_c_type_contact = tc.rowid";
1352
		$sql .= " AND tc.element = '".$element."'";
1353
		$sql .= " AND tc.source = '".$source."'";
1354
		if ($code) $sql .= " AND tc.code = '".$code."'";
1355
		$sql .= " AND tc.active = 1";
1356
		if ($status) $sql .= " AND ec.statut = ".$status;
1357
1358
		dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1359
		$resql = $this->db->query($sql);
1360
		if ($resql)
1361
		{
1362
			while ($obj = $this->db->fetch_object($resql))
1363
			{
1364
				$result[$i] = $obj->fk_socpeople;
1365
				$i++;
1366
			}
1367
		}
1368
		else
1369
		{
1370
			$this->error = $this->db->error();
1371
			return null;
1372
		}
1373
1374
		return $result;
1375
	}
1376
1377
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1378
	/**
1379
	 *		Load object contact with id=$this->contactid into $this->contact
1380
	 *
1381
	 *		@param	int		$contactid      Id du contact. Use this->contactid if empty.
1382
	 *		@return	int						<0 if KO, >0 if OK
1383
	 */
1384
	public function fetch_contact($contactid = null)
1385
	{
1386
        // phpcs:enable
1387
		if (empty($contactid)) $contactid = $this->contactid;
1388
1389
		if (empty($contactid)) return 0;
1390
1391
		require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1392
		$contact = new Contact($this->db);
1393
		$result = $contact->fetch($contactid);
1394
		$this->contact = $contact;
1395
		return $result;
1396
	}
1397
1398
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1399
	/**
1400
	 *    	Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty
1401
	 *
1402
	 *		@param		int		$force_thirdparty_id	Force thirdparty id
1403
	 *		@return		int								<0 if KO, >0 if OK
1404
	 */
1405
	public function fetch_thirdparty($force_thirdparty_id = 0)
1406
	{
1407
        // phpcs:enable
1408
		global $conf;
1409
1410
		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...
1411
			return 0;
1412
1413
		require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1414
1415
		$idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : $this->fk_thirdparty);
1416
		if ($force_thirdparty_id)
1417
			$idtofetch = $force_thirdparty_id;
1418
1419
		if ($idtofetch) {
1420
			$thirdparty = new Societe($this->db);
1421
			$result = $thirdparty->fetch($idtofetch);
1422
			$this->thirdparty = $thirdparty;
1423
1424
			// Use first price level if level not defined for third party
1425
			if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1426
				$this->thirdparty->price_level = 1;
1427
			}
1428
1429
			return $result;
1430
		} else
1431
			return -1;
1432
	}
1433
1434
1435
	/**
1436
	 * Looks for an object with ref matching the wildcard provided
1437
	 * It does only work when $this->table_ref_field is set
1438
	 *
1439
	 * @param string $ref Wildcard
1440
	 * @return int >1 = OK, 0 = Not found or table_ref_field not defined, <0 = KO
1441
	 */
1442
	public function fetchOneLike($ref)
1443
	{
1444
		if (!$this->table_ref_field) {
1445
			return 0;
1446
		}
1447
1448
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE '.$this->table_ref_field.' LIKE "'.$this->db->escape($ref).'" LIMIT 1';
1449
1450
		$query = $this->db->query($sql);
1451
1452
		if (!$this->db->num_rows($query)) {
1453
			return 0;
1454
		}
1455
1456
		$result = $this->db->fetch_object($query);
1457
1458
		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

1458
		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...
1459
	}
1460
1461
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1462
	/**
1463
	 *	Load data for barcode into properties ->barcode_type*
1464
	 *	Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
1465
	 *  if it is not defined, ->element must be defined to know default barcode type.
1466
	 *
1467
	 *	@return		int			<0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
1468
	 */
1469
	public function fetch_barcode()
1470
	{
1471
        // phpcs:enable
1472
		global $conf;
1473
1474
		dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1475
1476
		$idtype = $this->barcode_type;
1477
		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
1478
		{
1479
			if ($this->element == 'product')      $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1480
			elseif ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1481
			else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1482
		}
1483
1484
		if ($idtype > 0)
1485
		{
1486
			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
1487
			{
1488
				$sql = "SELECT rowid, code, libelle as label, coder";
1489
				$sql .= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1490
				$sql .= " WHERE rowid = ".$idtype;
1491
				dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1492
				$resql = $this->db->query($sql);
1493
				if ($resql)
1494
				{
1495
					$obj = $this->db->fetch_object($resql);
1496
					$this->barcode_type       = $obj->rowid;
1497
					$this->barcode_type_code  = $obj->code;
1498
					$this->barcode_type_label = $obj->label;
1499
					$this->barcode_type_coder = $obj->coder;
1500
					return 1;
1501
				}
1502
				else
1503
				{
1504
					dol_print_error($this->db);
1505
					return -1;
1506
				}
1507
			}
1508
		}
1509
		return 0;
1510
	}
1511
1512
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1513
	/**
1514
	 *		Load the project with id $this->fk_project into this->project
1515
	 *
1516
	 *		@return		int			<0 if KO, >=0 if OK
1517
	 */
1518
	public function fetch_projet()
1519
	{
1520
        // phpcs:enable
1521
		include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1522
1523
		if (empty($this->fk_project) && !empty($this->fk_projet)) $this->fk_project = $this->fk_projet; // For backward compatibility
1524
		if (empty($this->fk_project)) return 0;
1525
1526
		$project = new Project($this->db);
1527
		$result = $project->fetch($this->fk_project);
1528
1529
		$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

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

2057
								$this->/** @scrutinizer ignore-call */ 
2058
               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...
2058
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
2059
									($line->description ? $line->description : $line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
2060
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start,
2061
									$line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2062
								);
2063
								break;
2064
							case 'commande':
2065
								$this->updateline(
2066
									$line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent,
2067
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end,
2068
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
2069
									$line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2070
								);
2071
								break;
2072
							case 'facture':
2073
								$this->updateline(
2074
									$line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent,
2075
									$line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,
2076
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
2077
									$line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice
2078
								);
2079
								break;
2080
							case 'supplier_proposal':
2081
								$this->updateline(
2082
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
2083
									($line->description ? $line->description : $line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
2084
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options,
2085
									$line->ref_fourn, $line->multicurrency_subprice
2086
								);
2087
								break;
2088
							case 'order_supplier':
2089
								$this->updateline(
2090
									$line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent,
2091
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false,
2092
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2093
								);
2094
								break;
2095
							case 'invoice_supplier':
2096
								$this->updateline(
2097
									$line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx,
2098
									$line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false,
2099
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2100
								);
2101
								break;
2102
							default:
2103
								dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2104
								break;
2105
						}
2106
					}
2107
				}
2108
2109
				return 1;
2110
			}
2111
			else
2112
			{
2113
				dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
2114
				$this->error = $this->db->error();
2115
				return -1;
2116
			}
2117
		}
2118
		else
2119
		{
2120
			dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2121
			$this->error = 'Status of the object is incompatible '.$this->statut;
2122
			return -2;
2123
		}
2124
	}
2125
2126
	/**
2127
	 *  Change the payments terms
2128
	 *
2129
	 *  @param		int		$id		Id of new payment terms
2130
	 *  @return		int				>0 if OK, <0 if KO
2131
	 */
2132
	public function setPaymentTerms($id)
2133
	{
2134
		dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
2135
		if ($this->statut >= 0 || $this->element == 'societe')
2136
		{
2137
			// TODO uniformize field name
2138
			$fieldname = 'fk_cond_reglement';
2139
			if ($this->element == 'societe') $fieldname = 'cond_reglement';
2140
			if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
2141
2142
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2143
			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
2144
			$sql .= ' WHERE rowid='.$this->id;
2145
2146
			if ($this->db->query($sql))
2147
			{
2148
				$this->cond_reglement_id = $id;
2149
				// for supplier
2150
				if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
2151
				$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

2151
				/** @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...
2152
				return 1;
2153
			}
2154
			else
2155
			{
2156
				dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
2157
				$this->error = $this->db->error();
2158
				return -1;
2159
			}
2160
		}
2161
		else
2162
		{
2163
			dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2164
			$this->error = 'Status of the object is incompatible '.$this->statut;
2165
			return -2;
2166
		}
2167
	}
2168
2169
2170
	/**
2171
	 *  Change the retained warranty payments terms
2172
	 *
2173
	 *  @param		int		$id		Id of new payment terms
2174
	 *  @return		int				>0 if OK, <0 if KO
2175
	 */
2176
	public function setRetainedWarrantyPaymentTerms($id)
2177
	{
2178
	    dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
2179
	    if ($this->statut >= 0 || $this->element == 'societe')
2180
	    {
2181
	        $fieldname = 'retained_warranty_fk_cond_reglement';
2182
2183
	        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2184
	        $sql .= ' SET '.$fieldname.' = '.$id;
2185
	        $sql .= ' WHERE rowid='.$this->id;
2186
2187
	        if ($this->db->query($sql))
2188
	        {
2189
	            $this->retained_warranty_fk_cond_reglement = $id;
2190
	            return 1;
2191
	        }
2192
	        else
2193
	        {
2194
	            dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Erreur '.$sql.' - '.$this->db->error());
2195
	            $this->error = $this->db->error();
2196
	            return -1;
2197
	        }
2198
	    }
2199
	    else
2200
	    {
2201
	        dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
2202
	        $this->error = 'Status of the object is incompatible '.$this->statut;
2203
	        return -2;
2204
	    }
2205
	}
2206
2207
	/**
2208
	 *	Define delivery address
2209
	 *  @deprecated
2210
	 *
2211
	 *	@param      int		$id		Address id
2212
	 *	@return     int				<0 si ko, >0 si ok
2213
	 */
2214
	public function setDeliveryAddress($id)
2215
	{
2216
		$fieldname = 'fk_delivery_address';
2217
		if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
2218
2219
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
2220
		$sql .= " WHERE rowid = ".$this->id." AND fk_statut = 0";
2221
2222
		if ($this->db->query($sql))
2223
		{
2224
			$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

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