Passed
Branch develop (ad461a)
by
unknown
27:24
created

CommonObject::deleteObjectLinked()   F

Complexity

Conditions 17
Paths 384

Size

Total Lines 42
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
eloc 30
nc 384
nop 5
dl 0
loc 42
rs 2.0833
c 0
b 0
f 0

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

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