Passed
Branch develop (944e2a)
by
unknown
27:19
created

CommonObject::fetchObjectLinked()   F

Complexity

Conditions 61
Paths 14688

Size

Total Lines 172
Code Lines 108

Duplication

Lines 0
Ratio 0 %

Importance

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

1553
		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...
1554
	}
1555
1556
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1557
	/**
1558
	 *	Load data for barcode into properties ->barcode_type*
1559
	 *	Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
1560
	 *  if it is not defined, ->element must be defined to know default barcode type.
1561
	 *
1562
	 *	@return		int			<0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
1563
	 */
1564
	public function fetch_barcode()
1565
	{
1566
		// phpcs:enable
1567
		global $conf;
1568
1569
		dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1570
1571
		$idtype = $this->barcode_type;
1572
		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
1573
		{
1574
			if ($this->element == 'product')      $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1575
			elseif ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1576
			else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1577
		}
1578
1579
		if ($idtype > 0)
1580
		{
1581
			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
1582
			{
1583
				$sql = "SELECT rowid, code, libelle as label, coder";
1584
				$sql .= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1585
				$sql .= " WHERE rowid = ".$idtype;
1586
				dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1587
				$resql = $this->db->query($sql);
1588
				if ($resql)
1589
				{
1590
					$obj = $this->db->fetch_object($resql);
1591
					$this->barcode_type       = $obj->rowid;
1592
					$this->barcode_type_code  = $obj->code;
1593
					$this->barcode_type_label = $obj->label;
1594
					$this->barcode_type_coder = $obj->coder;
1595
					return 1;
1596
				} else {
1597
					dol_print_error($this->db);
1598
					return -1;
1599
				}
1600
			}
1601
		}
1602
		return 0;
1603
	}
1604
1605
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1606
	/**
1607
	 *		Load the project with id $this->fk_project into this->project
1608
	 *
1609
	 *		@return		int			<0 if KO, >=0 if OK
1610
	 */
1611
	public function fetch_projet()
1612
	{
1613
		// phpcs:enable
1614
		include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1615
1616
		if (empty($this->fk_project) && !empty($this->fk_projet)) $this->fk_project = $this->fk_projet; // For backward compatibility
1617
		if (empty($this->fk_project)) return 0;
1618
1619
		$project = new Project($this->db);
1620
		$result = $project->fetch($this->fk_project);
1621
1622
		$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

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

2147
								$this->/** @scrutinizer ignore-call */ 
2148
               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...
2148
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
2149
									($line->description ? $line->description : $line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
2150
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start,
2151
									$line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2152
								);
2153
								break;
2154
							case 'commande':
2155
								$this->updateline(
2156
									$line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent,
2157
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end,
2158
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
2159
									$line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2160
								);
2161
								break;
2162
							case 'facture':
2163
								$this->updateline(
2164
									$line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent,
2165
									$line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,
2166
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
2167
									$line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice
2168
								);
2169
								break;
2170
							case 'supplier_proposal':
2171
								$this->updateline(
2172
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
2173
									($line->description ? $line->description : $line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
2174
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options,
2175
									$line->ref_fourn, $line->multicurrency_subprice
2176
								);
2177
								break;
2178
							case 'order_supplier':
2179
								$this->updateline(
2180
									$line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent,
2181
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false,
2182
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2183
								);
2184
								break;
2185
							case 'invoice_supplier':
2186
								$this->updateline(
2187
									$line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx,
2188
									$line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false,
2189
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2190
								);
2191
								break;
2192
							default:
2193
								dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2194
								break;
2195
						}
2196
					}
2197
				}
2198
2199
				return 1;
2200
			} else {
2201
				dol_syslog(get_class($this).'::setMulticurrencyRate Error '.$sql.' - '.$this->db->error());
2202
				$this->error = $this->db->error();
2203
				return -1;
2204
			}
2205
		} else {
2206
			dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2207
			$this->error = 'Status of the object is incompatible '.$this->statut;
2208
			return -2;
2209
		}
2210
	}
2211
2212
	/**
2213
	 *  Change the payments terms
2214
	 *
2215
	 *  @param		int		$id		Id of new payment terms
2216
	 *  @return		int				>0 if OK, <0 if KO
2217
	 */
2218
	public function setPaymentTerms($id)
2219
	{
2220
		dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
2221
		if ($this->statut >= 0 || $this->element == 'societe')
2222
		{
2223
			// TODO uniformize field name
2224
			$fieldname = 'fk_cond_reglement';
2225
			if ($this->element == 'societe') $fieldname = 'cond_reglement';
2226
			if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
2227
2228
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2229
			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
2230
			$sql .= ' WHERE rowid='.$this->id;
2231
2232
			if ($this->db->query($sql))
2233
			{
2234
				$this->cond_reglement_id = $id;
2235
				// for supplier
2236
				if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
2237
				$this->cond_reglement = $id; // for compatibility
2238
				return 1;
2239
			} else {
2240
				dol_syslog(get_class($this).'::setPaymentTerms Error '.$sql.' - '.$this->db->error());
2241
				$this->error = $this->db->error();
2242
				return -1;
2243
			}
2244
		} else {
2245
			dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2246
			$this->error = 'Status of the object is incompatible '.$this->statut;
2247
			return -2;
2248
		}
2249
	}
2250
2251
	/**
2252
	 *  Change the transport mode methods
2253
	 *
2254
	 *  @param		int		$id		Id of new payment method
2255
	 *  @return		int				>0 if OK, <0 if KO
2256
	 */
2257
	public function setTransportMode($id)
2258
	{
2259
		dol_syslog(get_class($this).'::setTransportMode('.$id.')');
2260
		if ($this->statut >= 0 || $this->element == 'societe')
2261
		{
2262
			$fieldname = 'fk_transport_mode';
2263
			if ($this->element == 'societe') $fieldname = 'transport_mode';
2264
			if (get_class($this) == 'Fournisseur') $fieldname = 'transport_mode_supplier';
2265
2266
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2267
			$sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
2268
			$sql .= ' WHERE rowid='.$this->id;
2269
2270
			if ($this->db->query($sql))
2271
			{
2272
				$this->transport_mode_id = $id;
2273
				// for supplier
2274
				if (get_class($this) == 'Fournisseur') $this->transport_mode_supplier_id = $id;
2275
				return 1;
2276
			} else {
2277
				dol_syslog(get_class($this).'::setTransportMode Error '.$sql.' - '.$this->db->error());
2278
				$this->error = $this->db->error();
2279
				return -1;
2280
			}
2281
		} else {
2282
			dol_syslog(get_class($this).'::setTransportMode, status of the object is incompatible');
2283
			$this->error = 'Status of the object is incompatible '.$this->statut;
2284
			return -2;
2285
		}
2286
	}
2287
2288
	/**
2289
	 *  Change the retained warranty payments terms
2290
	 *
2291
	 *  @param		int		$id		Id of new payment terms
2292
	 *  @return		int				>0 if OK, <0 if KO
2293
	 */
2294
	public function setRetainedWarrantyPaymentTerms($id)
2295
	{
2296
		dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
2297
		if ($this->statut >= 0 || $this->element == 'societe')
2298
		{
2299
			$fieldname = 'retained_warranty_fk_cond_reglement';
2300
2301
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2302
			$sql .= ' SET '.$fieldname.' = '.$id;
2303
			$sql .= ' WHERE rowid='.$this->id;
2304
2305
			if ($this->db->query($sql))
2306
			{
2307
				$this->retained_warranty_fk_cond_reglement = $id;
2308
				return 1;
2309
			} else {
2310
				dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Error '.$sql.' - '.$this->db->error());
2311
				$this->error = $this->db->error();
2312
				return -1;
2313
			}
2314
		} else {
2315
			dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
2316
			$this->error = 'Status of the object is incompatible '.$this->statut;
2317
			return -2;
2318
		}
2319
	}
2320
2321
	/**
2322
	 *	Define delivery address
2323
	 *  @deprecated
2324
	 *
2325
	 *	@param      int		$id		Address id
2326
	 *	@return     int				<0 si ko, >0 si ok
2327
	 */
2328
	public function setDeliveryAddress($id)
2329
	{
2330
		$fieldname = 'fk_delivery_address';
2331
		if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
2332
2333
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
2334
		$sql .= " WHERE rowid = ".$this->id." AND fk_statut = 0";
2335
2336
		if ($this->db->query($sql))
2337
		{
2338
			$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

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