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

CommonObject::printObjectLine()   F

Complexity

Conditions 29
Paths > 20000

Size

Total Lines 107
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 29
eloc 57
nc 23015
nop 11
dl 0
loc 107
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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