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

CommonObject::printObjectLine()   F

Complexity

Conditions 29
Paths > 20000

Size

Total Lines 107
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
/* Copyright (C) 2006-2015 Laurent Destailleur  <[email protected]>
3
 * Copyright (C) 2005-2013 Regis Houssin        <[email protected]>
4
 * Copyright (C) 2010-2020 Juanjo Menent        <[email protected]>
5
 * Copyright (C) 2012-2013 Christophe Battarel  <[email protected]>
6
 * Copyright (C) 2011-2019 Philippe Grand       <[email protected]>
7
 * Copyright (C) 2012-2015 Marcos García        <[email protected]>
8
 * Copyright (C) 2012-2015 Raphaël Doursenaud   <[email protected]>
9
 * Copyright (C) 2012      Cedric Salvador      <[email protected]>
10
 * Copyright (C) 2015      Alexandre Spangaro   <[email protected]>
11
 * Copyright (C) 2016      Bahfir abbes         <[email protected]>
12
 * Copyright (C) 2017      ATM Consulting       <[email protected]>
13
 * Copyright (C) 2017-2019 Nicolas ZABOURI      <[email protected]>
14
 * Copyright (C) 2017      Rui Strecht		    <[email protected]>
15
 * Copyright (C) 2018-2019 Frédéric France      <[email protected]>
16
 * Copyright (C) 2018      Josep Lluís Amador   <[email protected]>
17
 *
18
 * This program is free software; you can redistribute it and/or modify
19
 * it under the terms of the GNU General Public License as published by
20
 * the Free Software Foundation; either version 3 of the License, or
21
 * (at your option) any later version.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26
 * GNU General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU General Public License
29
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
30
 */
31
32
/**
33
 *	\file       htdocs/core/class/commonobject.class.php
34
 *	\ingroup    core
35
 *	\brief      File of parent class of all other business classes (invoices, contracts, proposals, orders, ...)
36
 */
37
38
39
/**
40
 *	Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
41
 */
42
abstract class CommonObject
43
{
44
	/**
45
	 * @var DoliDb		Database handler (result of a new DoliDB)
46
	 */
47
	public $db;
48
49
	/**
50
	 * @var int The object identifier
51
	 */
52
	public $id;
53
54
	/**
55
	 * @var string 		Error string
56
	 * @see             $errors
57
	 */
58
	public $error;
59
60
	/**
61
	 * @var string[]	Array of error strings
62
	 */
63
	public $errors = array();
64
65
	/**
66
	 * @var string ID to identify managed object
67
	 */
68
	public $element;
69
70
	/**
71
	 * @var string Name of table without prefix where object is stored
72
	 */
73
	public $table_element;
74
75
	/**
76
	 * @var string    Name of subtable line
77
	 */
78
	public $table_element_line = '';
79
80
	/**
81
	 * @var string		Key value used to track if data is coming from import wizard
82
	 */
83
	public $import_key;
84
85
	/**
86
	 * @var mixed		Contains data to manage extrafields
87
	 */
88
	public $array_options = array();
89
90
	/**
91
	 * @var mixed		Array to store alternative languages values of object
92
	 */
93
	public $array_languages = null; // Value is array() when load already tried
94
95
	/**
96
	 * @var int[][]		Array of linked objects ids. Loaded by ->fetchObjectLinked
97
	 */
98
	public $linkedObjectsIds;
99
100
	/**
101
	 * @var mixed		Array of linked objects. Loaded by ->fetchObjectLinked
102
	 */
103
	public $linkedObjects;
104
105
	/**
106
	 * @var Object      To store a cloned copy of object before to edit it and keep track of old properties
107
	 */
108
	public $oldcopy;
109
110
	/**
111
	 * @var string		Column name of the ref field.
112
	 */
113
	protected $table_ref_field = '';
114
115
116
117
	// Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
118
119
	/**
120
	 * @var array<string,mixed>		Can be used to pass information when only object is provided to method
121
	 */
122
	public $context = array();
123
124
	/**
125
	 * @var string		Contains canvas name if record is an alternative canvas record
126
	 */
127
	public $canvas;
128
129
	/**
130
	 * @var Project The related project
131
	 * @see fetch_projet()
132
	 */
133
	public $project;
134
135
	/**
136
	 * @var int The related project ID
137
	 * @see setProject(), project
138
	 */
139
	public $fk_project;
140
141
	/**
142
	 * @deprecated
143
	 * @see project
144
	 */
145
	public $projet;
146
147
	/**
148
	 * @var Contact a related contact
149
	 * @see fetch_contact()
150
	 */
151
	public $contact;
152
153
	/**
154
	 * @var int The related contact ID
155
	 * @see fetch_contact()
156
	 */
157
	public $contact_id;
158
159
	/**
160
	 * @var Societe A related thirdparty
161
	 * @see fetch_thirdparty()
162
	 */
163
	public $thirdparty;
164
165
	/**
166
	 * @var User A related user
167
	 * @see fetch_user()
168
	 */
169
	public $user;
170
171
	/**
172
	 * @var string 	The type of originating object ('commande', 'facture', ...)
173
	 * @see fetch_origin()
174
	 */
175
	public $origin;
176
177
	/**
178
	 * @var int 	The id of originating object
179
	 * @see fetch_origin()
180
	 */
181
	public $origin_id;
182
183
	/**
184
	 * @var string The object's reference
185
	 */
186
	public $ref;
187
188
	/**
189
	 * @var string The object's previous reference
190
	 */
191
	public $ref_previous;
192
193
	/**
194
	 * @var string The object's next reference
195
	 */
196
	public $ref_next;
197
198
	/**
199
	 * @var string An external reference for the object
200
	 */
201
	public $ref_ext;
202
203
	/**
204
	 * @var int The object's status
205
	 * @see setStatut()
206
	 */
207
	public $statut;
208
209
	/**
210
	 * @var string
211
	 * @see getFullAddress()
212
	 */
213
	public $country;
214
215
	/**
216
	 * @var int
217
	 * @see getFullAddress(), country
218
	 */
219
	public $country_id;
220
221
	/**
222
	 * @var string
223
	 * @see getFullAddress(), isInEEC(), country
224
	 */
225
	public $country_code;
226
227
	/**
228
	 * @var string
229
	 * @see getFullAddress()
230
	 */
231
	public $state;
232
233
	/**
234
	 * @var int
235
	 * @see getFullAddress(), state
236
	 */
237
	public $state_id;
238
239
	/**
240
	 * @var string
241
	 * @see getFullAddress(), $state
242
	 */
243
	public $state_code;
244
245
	/**
246
	 * @var int
247
	 * @see getFullAddress(), $region_code, $region
248
	 */
249
	public $region_id;
250
251
	/**
252
	 * @var string
253
	 * @see getFullAddress(), $region_id, $region
254
	 */
255
	public $region_code;
256
257
	/**
258
	 * @var string
259
	 * @see getFullAddress(), $region_id, $region_code
260
	 */
261
	public $region;
262
263
	/**
264
	 * @var int
265
	 * @see fetch_barcode()
266
	 */
267
	public $barcode_type;
268
269
	/**
270
	 * @var string
271
	 * @see fetch_barcode(), barcode_type
272
	 */
273
	public $barcode_type_code;
274
275
	/**
276
	 * @var string
277
	 * @see fetch_barcode(), barcode_type
278
	 */
279
	public $barcode_type_label;
280
281
	/**
282
	 * @var string
283
	 * @see fetch_barcode(), barcode_type
284
	 */
285
	public $barcode_type_coder;
286
287
	/**
288
	 * @var int Payment method ID (cheque, cash, ...)
289
	 * @see setPaymentMethods()
290
	 */
291
	public $mode_reglement_id;
292
293
	/**
294
	 * @var int Payment terms ID
295
	 * @see setPaymentTerms()
296
	 */
297
	public $cond_reglement_id;
298
299
    /**
300
     * @var int 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