Passed
Branch develop (7b8a20)
by
unknown
32:48
created

CommonObject::getIncotermsForPDF()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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

1495
		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...
1496
	}
1497
1498
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1499
	/**
1500
	 *	Load data for barcode into properties ->barcode_type*
1501
	 *	Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
1502
	 *  if it is not defined, ->element must be defined to know default barcode type.
1503
	 *
1504
	 *	@return		int			<0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
1505
	 */
1506
	public function fetch_barcode()
1507
	{
1508
		// phpcs:enable
1509
		global $conf;
1510
1511
		dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1512
1513
		$idtype = $this->barcode_type;
1514
		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
1515
		{
1516
			if ($this->element == 'product')      $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1517
			elseif ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1518
			else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1519
		}
1520
1521
		if ($idtype > 0)
1522
		{
1523
			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
1524
			{
1525
				$sql = "SELECT rowid, code, libelle as label, coder";
1526
				$sql .= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1527
				$sql .= " WHERE rowid = ".$idtype;
1528
				dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1529
				$resql = $this->db->query($sql);
1530
				if ($resql)
1531
				{
1532
					$obj = $this->db->fetch_object($resql);
1533
					$this->barcode_type       = $obj->rowid;
1534
					$this->barcode_type_code  = $obj->code;
1535
					$this->barcode_type_label = $obj->label;
1536
					$this->barcode_type_coder = $obj->coder;
1537
					return 1;
1538
				} else {
1539
					dol_print_error($this->db);
1540
					return -1;
1541
				}
1542
			}
1543
		}
1544
		return 0;
1545
	}
1546
1547
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1548
	/**
1549
	 *		Load the project with id $this->fk_project into this->project
1550
	 *
1551
	 *		@return		int			<0 if KO, >=0 if OK
1552
	 */
1553
	public function fetch_projet()
1554
	{
1555
		// phpcs:enable
1556
		include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1557
1558
		if (empty($this->fk_project) && !empty($this->fk_projet)) $this->fk_project = $this->fk_projet; // For backward compatibility
1559
		if (empty($this->fk_project)) return 0;
1560
1561
		$project = new Project($this->db);
1562
		$result = $project->fetch($this->fk_project);
1563
1564
		$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

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

2089
								$this->/** @scrutinizer ignore-call */ 
2090
               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...
2090
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
2091
									($line->description ? $line->description : $line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
2092
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start,
2093
									$line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2094
								);
2095
								break;
2096
							case 'commande':
2097
								$this->updateline(
2098
									$line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent,
2099
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end,
2100
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
2101
									$line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2102
								);
2103
								break;
2104
							case 'facture':
2105
								$this->updateline(
2106
									$line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent,
2107
									$line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,
2108
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
2109
									$line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice
2110
								);
2111
								break;
2112
							case 'supplier_proposal':
2113
								$this->updateline(
2114
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
2115
									($line->description ? $line->description : $line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
2116
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options,
2117
									$line->ref_fourn, $line->multicurrency_subprice
2118
								);
2119
								break;
2120
							case 'order_supplier':
2121
								$this->updateline(
2122
									$line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent,
2123
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false,
2124
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2125
								);
2126
								break;
2127
							case 'invoice_supplier':
2128
								$this->updateline(
2129
									$line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx,
2130
									$line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false,
2131
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2132
								);
2133
								break;
2134
							default:
2135
								dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2136
								break;
2137
						}
2138
					}
2139
				}
2140
2141
				return 1;
2142
			} else {
2143
				dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
2144
				$this->error = $this->db->error();
2145
				return -1;
2146
			}
2147
		} else {
2148
			dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2149
			$this->error = 'Status of the object is incompatible '.$this->statut;
2150
			return -2;
2151
		}
2152
	}
2153
2154
	/**
2155
	 *  Change the payments terms
2156
	 *
2157
	 *  @param		int		$id		Id of new payment terms
2158
	 *  @return		int				>0 if OK, <0 if KO
2159
	 */
2160
	public function setPaymentTerms($id)
2161
	{
2162
		dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
2163
		if ($this->statut >= 0 || $this->element == 'societe')
2164
		{
2165
			// TODO uniformize field name
2166
			$fieldname = 'fk_cond_reglement';
2167
			if ($this->element == 'societe') $fieldname = 'cond_reglement';
2168
			if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
2169
2170
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2171
			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
2172
			$sql .= ' WHERE rowid='.$this->id;
2173
2174
			if ($this->db->query($sql))
2175
			{
2176
				$this->cond_reglement_id = $id;
2177
				// for supplier
2178
				if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
2179
				$this->cond_reglement = $id; // for compatibility
2180
				return 1;
2181
			} else {
2182
				dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
2183
				$this->error = $this->db->error();
2184
				return -1;
2185
			}
2186
		} else {
2187
			dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2188
			$this->error = 'Status of the object is incompatible '.$this->statut;
2189
			return -2;
2190
		}
2191
	}
2192
2193
2194
	/**
2195
	 *  Change the retained warranty payments terms
2196
	 *
2197
	 *  @param		int		$id		Id of new payment terms
2198
	 *  @return		int				>0 if OK, <0 if KO
2199
	 */
2200
	public function setRetainedWarrantyPaymentTerms($id)
2201
	{
2202
		dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
2203
		if ($this->statut >= 0 || $this->element == 'societe')
2204
		{
2205
			$fieldname = 'retained_warranty_fk_cond_reglement';
2206
2207
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2208
			$sql .= ' SET '.$fieldname.' = '.$id;
2209
			$sql .= ' WHERE rowid='.$this->id;
2210
2211
			if ($this->db->query($sql))
2212
			{
2213
				$this->retained_warranty_fk_cond_reglement = $id;
2214
				return 1;
2215
			} else {
2216
				dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Erreur '.$sql.' - '.$this->db->error());
2217
				$this->error = $this->db->error();
2218
				return -1;
2219
			}
2220
		} else {
2221
			dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
2222
			$this->error = 'Status of the object is incompatible '.$this->statut;
2223
			return -2;
2224
		}
2225
	}
2226
2227
	/**
2228
	 *	Define delivery address
2229
	 *  @deprecated
2230
	 *
2231
	 *	@param      int		$id		Address id
2232
	 *	@return     int				<0 si ko, >0 si ok
2233
	 */
2234
	public function setDeliveryAddress($id)
2235
	{
2236
		$fieldname = 'fk_delivery_address';
2237
		if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
2238
2239
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
2240
		$sql .= " WHERE rowid = ".$this->id." AND fk_statut = 0";
2241
2242
		if ($this->db->query($sql))
2243
		{
2244
			$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

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