Completed
Branch develop (a2b570)
by
unknown
43:44
created

CommonObject   F

Complexity

Total Complexity 1448

Size/Duplication

Total Lines 7109
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 0
Metric Value
dl 0
loc 7109
rs 0.8
c 0
b 0
f 0
wmc 1448
lcom 1
cbo 17

109 Methods

Rating   Name   Duplication   Size   Complexity  
B isExistingObject() 0 28 8
A errorsToString() 0 4 3
B getFullName() 0 18 10
B getFullAddress() 0 22 11
F getBannerAddress() 0 102 37
C getLastMainDocLink() 0 83 14
D add_contact() 0 112 17
A copy_linked_contact() 0 13 3
A update_contact() 0 19 4
A delete_contact() 0 29 4
A delete_linked_contact() 0 27 4
F liste_contact() 0 61 16
A swapContactStatus() 0 29 3
C liste_type_contact() 0 41 11
C getIdContact() 0 49 10
A fetch_contact() 0 12 3
B fetch_thirdparty() 0 27 11
A fetchOneLike() 0 18 3
B fetch_barcode() 0 41 11
A fetch_projet() 0 14 4
A fetch_product() 0 12 2
A fetch_user() 0 7 1
A fetch_origin() 0 11 3
A fetchObjectFrom() 0 27 4
A getValueFrom() 0 17 5
F setValueFrom() 0 69 27
F load_previous_next_ref() 0 86 85
A getListContactId() 0 14 3
B setProject() 0 34 6
B setPaymentMethods() 0 35 7
A setMulticurrencyCode() 0 34 5
F setMulticurrencyRate() 0 95 19
B setPaymentTerms() 0 36 7
A setDeliveryAddress() 0 20 4
B setShippingMethod() 0 47 9
A setWarehouse() 0 22 5
A setDocModel() 0 29 3
B setBankAccount() 0 51 9
C line_order() 0 77 14
A getChildrenOfLine() 0 25 3
A line_up() 0 10 1
A line_down() 0 13 1
A updateRangOfLine() 0 14 3
A line_ajaxorder() 0 8 2
A updateLineUp() 0 22 4
A updateLineDown() 0 22 4
A getRangOfLine() 0 13 2
A getIdOfLine() 0 12 2
B line_max() 0 39 5
A update_ref_ext() 0 24 4
B update_note() 0 43 9
A update_note_public() 0 4 1
F update_price() 0 224 53
B add_object_linked() 0 36 6
F fetchObjectLinked() 0 185 57
C updateObjectLinked() 0 35 12
F deleteObjectLinked() 0 51 17
F setStatut() 0 79 29
B getCanvas() 0 33 8
A getSpecialCode() 0 11 2
C isObjectUsed() 0 62 11
C hasProductsOrServices() 0 17 14
A getTotalDiscount() 0 32 3
F getTotalWeightVolume() 0 79 19
A setExtraParameters() 0 24 4
A display_incoterms() 0 19 5
A getIncotermsForPDF() 0 23 3
B setIncoterms() 0 32 7
A formAddObjectLine() 0 24 4
F printObjectLines() 0 136 31
F printObjectLine() 0 104 28
B printOriginLinesList() 0 42 10
F printOriginLine() 0 127 35
A add_element_resource() 0 33 2
A delete_resource() 0 29 4
A __clone() 0 12 4
F commonGenerateDocument() 0 239 46
A addThumbs() 0 18 2
B getDefaultCreateValueFor() 0 25 7
A call_trigger() 0 21 3
F fetch_optionals() 0 95 22
A deleteExtraFields() 0 22 3
F insertExtraFields() 0 218 45
F updateExtraField() 0 101 19
F showInputField() 0 563 134
F showOutputField() 0 357 80
F showOptionals() 0 177 51
A getRights() 0 9 2
A commonReplaceThirdparty() 0 16 4
D defineBuyPrice() 0 68 19
F show_photos() 0 220 55
A isArray() 0 9 4
A isNull() 0 9 4
A isDate() 0 5 5
A isInt() 0 9 5
A isFloat() 0 9 4
A isText() 0 9 4
A isIndex() 0 9 4
D setSaveQuery() 0 55 18
D setVarsFromFetchObj() 0 43 19
A getFieldList() 0 5 1
A quote() 0 5 3
F createCommon() 0 87 28
B fetchCommon() 0 34 9
F updateCommon() 0 81 20
B deleteCommon() 0 48 10
A initAsSpecimenCommon() 0 6 1
A fetchComments() 0 14 2
A getNbComments() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like CommonObject often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CommonObject, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* Copyright (C) 2006-2015 Laurent Destailleur  <[email protected]>
3
 * Copyright (C) 2005-2013 Regis Houssin        <[email protected]>
4
 * Copyright (C) 2010-2013 Juanjo Menent        <[email protected]>
5
 * Copyright (C) 2012      Christophe Battarel  <[email protected]>
6
 * Copyright (C) 2010-2015 Juanjo Menent        <[email protected]>
7
 * Copyright (C) 2012-2013 Christophe Battarel  <[email protected]>
8
 * Copyright (C) 2011-2018 Philippe Grand	    <[email protected]>
9
 * Copyright (C) 2012-2015 Marcos García        <[email protected]>
10
 * Copyright (C) 2012-2015 Raphaël Doursenaud   <[email protected]>
11
 * Copyright (C) 2012      Cedric Salvador      <[email protected]>
12
 * Copyright (C) 2015      Alexandre Spangaro   <[email protected]>
13
 * Copyright (C) 2016      Bahfir abbes         <[email protected]>
14
 * Copyright (C) 2017      ATM Consulting       <[email protected]>
15
 * Copyright (C) 2017      Nicolas ZABOURI      <[email protected]>
16
 * Copyright (C) 2017      Rui Strecht		    <[email protected]>
17
 * Copyright (C) 2018      Frederic France      <[email protected]>
18
 *
19
 * This program is free software; you can redistribute it and/or modify
20
 * it under the terms of the GNU General Public License as published by
21
 * the Free Software Foundation; either version 3 of the License, or
22
 * (at your option) any later version.
23
 *
24
 * This program is distributed in the hope that it will be useful,
25
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27
 * GNU General Public License for more details.
28
 *
29
 * You should have received a copy of the GNU General Public License
30
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31
 */
32
33
/**
34
 *	\file       htdocs/core/class/commonobject.class.php
35
 *	\ingroup    core
36
 *	\brief      File of parent class of all other business classes (invoices, contracts, proposals, orders, ...)
37
 */
38
39
40
/**
41
 *	Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
42
 */
43
abstract class CommonObject
44
{
45
	/**
46
	 * @var DoliDb		Database handler (result of a new DoliDB)
47
	 */
48
	public $db;
49
	/**
50
	 * @var int The object identifier
51
	 */
52
	public $id;
53
	/**
54
	 * @var string 		Error string
55
	 * @see             errors
56
	 */
57
	public $error;
58
	/**
59
	 * @var string[]	Array of error strings
60
	 */
61
	public $errors=array();
62
	/**
63
	 * @var string
64
	 */
65
	public $element;
66
	/**
67
	 * @var string
68
	 */
69
	public $table_element;
70
	/**
71
	 * @var
72
	 */
73
	public $table_element_line;
74
	/**
75
	 * @var string		Key value used to track if data is coming from import wizard
76
	 */
77
	public $import_key;
78
	/**
79
	 * @var mixed		Contains data to manage extrafields
80
	 */
81
	public $array_options=array();
82
	/**
83
	 * @var int[][]		Array of linked objects ids. Loaded by ->fetchObjectLinked
84
	 */
85
	public $linkedObjectsIds;
86
	/**
87
	 * @var mixed		Array of linked objects. Loaded by ->fetchObjectLinked
88
	 */
89
	public $linkedObjects;
90
	/**
91
	 * @var Object      To store a cloned copy of object before to edit it and keep track of old properties
92
	 */
93
	public $oldcopy;
94
95
	/**
96
	 * @var string		Column name of the ref field.
97
	 */
98
	protected $table_ref_field = '';
99
100
101
102
	// Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
103
104
	/**
105
	 * @var array<string,mixed>		Can be used to pass information when only object is provided to method
106
	 */
107
	public $context=array();
108
109
	/**
110
	 * @var string		Contains canvas name if record is an alternative canvas record
111
	 */
112
	public $canvas;
113
114
	/**
115
	 * @var Project The related project
116
	 * @see fetch_projet()
117
	 */
118
	public $project;
119
	/**
120
	 * @var int The related project ID
121
	 * @see setProject(), project
122
	 */
123
	public $fk_project;
124
	/**
125
	 * @deprecated
126
	 * @see project
127
	 */
128
	public $projet;
129
130
	/**
131
	 * @var Contact a related contact
132
	 * @see fetch_contact()
133
	 */
134
	public $contact;
135
	/**
136
	 * @var int The related contact ID
137
	 * @see fetch_contact()
138
	 */
139
	public $contact_id;
140
141
	/**
142
	 * @var Societe A related thirdparty
143
	 * @see fetch_thirdparty()
144
	 */
145
	public $thirdparty;
146
147
	/**
148
	 * @var User A related user
149
	 * @see fetch_user()
150
	 */
151
	public $user;
152
153
	/**
154
	 * @var string 	The type of originating object ('commande', 'facture', ...)
155
	 * @see fetch_origin()
156
	 */
157
	public $origin;
158
	/**
159
	 * @var int 	The id of originating object
160
	 * @see fetch_origin()
161
	 */
162
	public $origin_id;
163
164
	/**
165
	 * @var string The object's reference
166
	 */
167
	public $ref;
168
	/**
169
	 * @var string The object's previous reference
170
	 */
171
	public $ref_previous;
172
	/**
173
	 * @var string The object's next reference
174
	 */
175
	public $ref_next;
176
	/**
177
	 * @var string An external reference for the object
178
	 */
179
	public $ref_ext;
180
181
	/**
182
	 * @var int The object's status
183
	 * @see setStatut()
184
	 */
185
	public $statut;
186
187
	/**
188
	 * @var string
189
	 * @see getFullAddress()
190
	 */
191
	public $country;
192
	/**
193
	 * @var int
194
	 * @see getFullAddress(), country
195
	 */
196
	public $country_id;
197
	/**
198
	 * @var string
199
	 * @see getFullAddress(), isInEEC(), country
200
	 */
201
    public $country_code;
202
    /**
203
	 * @var string
204
	 * @see getFullAddress()
205
	 */
206
	public $state;
207
	/**
208
	 * @var int
209
	 * @see getFullAddress(), state
210
	 */
211
	public $state_id;
212
	/**
213
	 * @var string
214
	 * @see getFullAddress(), state
215
	 */
216
    public $state_code;
217
    /**
218
	 * @var string
219
	 * @see getFullAddress(), region
220
	 */
221
	public $region;
222
	/**
223
	 * @var string
224
	 * @see getFullAddress(), region
225
	 */
226
    public $region_code;
227
228
	/**
229
	 * @var int
230
	 * @see fetch_barcode()
231
	 */
232
	public $barcode_type;
233
	/**
234
	 * @var string
235
	 * @see fetch_barcode(), barcode_type
236
	 */
237
	public $barcode_type_code;
238
	/**
239
	 * @var string
240
	 * @see fetch_barcode(), barcode_type
241
	 */
242
	public $barcode_type_label;
243
	/**
244
	 * @var string
245
	 * @see fetch_barcode(), barcode_type
246
	 */
247
	public $barcode_type_coder;
248
249
	/**
250
	 * @var int Payment method ID (cheque, cash, ...)
251
	 * @see setPaymentMethods()
252
	 */
253
	public $mode_reglement_id;
254
255
	/**
256
	 * @var int Payment terms ID
257
	 * @see setPaymentTerms()
258
	 */
259
	public $cond_reglement_id;
260
	/**
261
	 * @var int Payment terms ID
262
	 * @deprecated Kept for compatibility
263
	 * @see cond_reglement_id;
264
	 */
265
	public $cond_reglement;
266
267
	/**
268
	 * @var int Delivery address ID
269
	 * @deprecated
270
	 * @see setDeliveryAddress()
271
	 */
272
	public $fk_delivery_address;
273
274
	/**
275
	 * @var int Shipping method ID
276
	 * @see setShippingMethod()
277
	 */
278
	public $shipping_method_id;
279
280
	/**
281
	 * @var string
282
	 * @see SetDocModel()
283
	 */
284
	public $modelpdf;
285
286
	/**
287
	 * @var int Bank account ID
288
	 * @see SetBankAccount()
289
	 */
290
	public $fk_account;
291
292
	/**
293
	 * @var string Public note
294
	 * @see update_note()
295
	 */
296
	public $note_public;
297
	/**
298
	 * @var string Private note
299
	 * @see update_note()
300
	 */
301
	public $note_private;
302
	/**
303
	 * @deprecated
304
	 * @see note_public
305
	 */
306
	public $note;
307
308
	/**
309
	 * @var float Total amount before taxes
310
	 * @see update_price()
311
	 */
312
	public $total_ht;
313
	/**
314
	 * @var float Total VAT amount
315
	 * @see update_price()
316
	 */
317
	public $total_tva;
318
	/**
319
	 * @var float Total local tax 1 amount
320
	 * @see update_price()
321
	 */
322
	public $total_localtax1;
323
	/**
324
	 * @var float Total local tax 2 amount
325
	 * @see update_price()
326
	 */
327
	public $total_localtax2;
328
	/**
329
	 * @var float Total amount with taxes
330
	 * @see update_price()
331
	 */
332
	public $total_ttc;
333
334
	/**
335
	 * @var CommonObjectLine[]
336
	 */
337
	public $lines;
338
339
	/**
340
	 * @var mixed		Contains comments
341
	 * @see fetchComments()
342
	 */
343
	public $comments=array();
344
345
	/**
346
	 * @var int
347
	 * @see setIncoterms()
348
	 */
349
	public $fk_incoterms;
350
	/**
351
	 * @var string
352
	 * @see SetIncoterms()
353
	 */
354
	public $libelle_incoterms;
355
	/**
356
	 * @var string
357
	 * @see display_incoterms()
358
	 */
359
	public $location_incoterms;
360
361
	public $name;
362
	public $lastname;
363
	public $firstname;
364
	public $civility_id;
365
366
367
	// No constructor as it is an abstract class
368
369
	/**
370
	 * Check an object id/ref exists
371
	 * If you don't need/want to instantiate object and just need to know if object exists, use this method instead of fetch
372
	 *
373
	 *  @param	string	$element   	String of element ('product', 'facture', ...)
374
	 *  @param	int		$id      	Id of object
375
	 *  @param  string	$ref     	Ref of object to check
376
	 *  @param	string	$ref_ext	Ref ext of object to check
377
	 *  @return int     			<0 if KO, 0 if OK but not found, >0 if OK and exists
378
	 */
379
	static function isExistingObject($element, $id, $ref='', $ref_ext='')
380
	{
381
		global $db,$conf;
382
383
		$sql = "SELECT rowid, ref, ref_ext";
384
		$sql.= " FROM ".MAIN_DB_PREFIX.$element;
385
		$sql.= " WHERE entity IN (".getEntity($element).")" ;
386
387
		if ($id > 0) $sql.= " AND rowid = ".$db->escape($id);
388
		else if ($ref) $sql.= " AND ref = '".$db->escape($ref)."'";
389
		else if ($ref_ext) $sql.= " AND ref_ext = '".$db->escape($ref_ext)."'";
390
		else {
391
			$error='ErrorWrongParameters';
392
			dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
393
			return -1;
394
		}
395
		if ($ref || $ref_ext) $sql.= " AND entity = ".$conf->entity;
396
397
		dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
398
		$resql = $db->query($sql);
399
		if ($resql)
400
		{
401
			$num=$db->num_rows($resql);
402
			if ($num > 0) return 1;
403
			else return 0;
404
		}
405
		return -1;
406
	}
407
408
	/**
409
	 * Method to output saved errors
410
	 *
411
	 * @return	string		String with errors
412
	 */
413
	function errorsToString()
414
	{
415
		return $this->error.(is_array($this->errors)?(($this->error!=''?', ':'').join(', ',$this->errors)):'');
416
	}
417
418
	/**
419
	 *	Return full name (civility+' '+name+' '+lastname)
420
	 *
421
	 *	@param	Translate	$langs			Language object for translation of civility (used only if option is 1)
422
	 *	@param	int			$option			0=No option, 1=Add civility
423
	 * 	@param	int			$nameorder		-1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname
424
	 * 	@param	int			$maxlen			Maximum length
425
	 * 	@return	string						String with full name
426
	 */
427
	function getFullName($langs,$option=0,$nameorder=-1,$maxlen=0)
428
	{
429
		//print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n";
430
		$lastname=$this->lastname;
431
		$firstname=$this->firstname;
432
		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:'')))));
433
434
		$ret='';
435
		if ($option && $this->civility_id)
436
		{
437
			if ($langs->transnoentitiesnoconv("Civility".$this->civility_id)!="Civility".$this->civility_id) $ret.=$langs->transnoentitiesnoconv("Civility".$this->civility_id).' ';
438
			else $ret.=$this->civility_id.' ';
439
		}
440
441
		$ret.=dolGetFirstLastname($firstname, $lastname, $nameorder);
442
443
		return dol_trunc($ret,$maxlen);
444
	}
445
446
	/**
447
	 * 	Return full address of contact
448
	 *
449
	 * 	@param		int			$withcountry		1=Add country into address string
450
	 *  @param		string		$sep				Separator to use to build string
451
	 *  @param		int		    $withregion			1=Add region into address string
452
	 *	@return		string							Full address string
453
	 */
454
	function getFullAddress($withcountry=0, $sep="\n", $withregion=0)
455
	{
456
		if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country)))
457
		{
458
			require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
459
			$tmparray=getCountry($this->country_id,'all');
460
			$this->country_code=$tmparray['code'];
461
			$this->country     =$tmparray['label'];
462
		}
463
464
        if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_cpde)))
465
    	{
466
    		require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
467
    		$tmparray=getState($this->state_id,'all',0,1);
468
			$this->state_code   =$tmparray['code'];
469
			$this->state        =$tmparray['label'];
470
			$this->region_code  =$tmparray['region_code'];
471
			$this->region       =$tmparray['region'];
472
        }
473
474
		return dol_format_address($this, $withcountry, $sep);
475
	}
476
477
478
	/**
479
	 * 	Return full address for banner
480
	 *
481
	 * 	@param		string		$htmlkey            HTML id to make banner content unique
482
	 *  @param      Object      $object				Object (thirdparty, thirdparty of contact for contact, null for a member)
483
	 *	@return		string							Full address string
484
	 */
485
	function getBannerAddress($htmlkey, $object)
486
	{
487
		global $conf, $langs;
488
489
		$countriesusingstate=array('AU','US','IN','GB','ES','UK','TR');    // See also option MAIN_FORCE_STATE_INTO_ADDRESS
490
491
		$contactid=0;
492
		$thirdpartyid=0;
493
		if ($this->element == 'societe')
494
		{
495
			$thirdpartyid=$this->id;
496
		}
497
		if ($this->element == 'contact')
498
		{
499
			$contactid=$this->id;
500
			$thirdpartyid=$object->fk_soc;
501
		}
502
		if ($this->element == 'user')
503
		{
504
			$contactid=$this->contact_id;
505
			$thirdpartyid=$object->fk_soc;
506
		}
507
508
		$out='<!-- BEGIN part to show address block -->';
509
510
		$outdone=0;
511
		$coords = $this->getFullAddress(1,', ',$conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT);
512
		if ($coords)
513
		{
514
			if (! empty($conf->use_javascript_ajax))
515
			{
516
				$namecoords = $this->getFullName($langs,1).'<br>'.$coords;
517
				// hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile
518
				$out.='<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">';
519
				$out.=img_picto($langs->trans("Address"), 'object_address.png');
520
				$out.='</a> ';
521
			}
522
			$out.=dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); $outdone++;
523
			$outdone++;
524
		}
525
526
		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
527
				&& empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state)
528
		{
529
            if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) {
530
                $out.=($outdone?' - ':'').$this->region.' - '.$this->state;
531
            }
532
            else {
533
                $out.=($outdone?' - ':'').$this->state;
534
            }
535
			$outdone++;
536
		}
537
538
		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>':'');
539
		if (! empty($this->phone) && empty($this->phone_pro)) {		// For objects that store pro phone into ->phone
540
			$out.=dol_print_phone($this->phone,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
541
		}
542
		if (! empty($this->phone_pro)) {
543
			$out.=dol_print_phone($this->phone_pro,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
544
		}
545
		if (! empty($this->phone_mobile)) {
546
			$out.=dol_print_phone($this->phone_mobile,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','mobile',$langs->trans("PhoneMobile")); $outdone++;
547
		}
548
		if (! empty($this->phone_perso)) {
549
			$out.=dol_print_phone($this->phone_perso,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePerso")); $outdone++;
550
		}
551
		if (! empty($this->office_phone)) {
552
			$out.=dol_print_phone($this->office_phone,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
553
		}
554
		if (! empty($this->user_mobile)) {
555
			$out.=dol_print_phone($this->user_mobile,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','mobile',$langs->trans("PhoneMobile")); $outdone++;
556
		}
557
		if (! empty($this->fax)) {
558
			$out.=dol_print_phone($this->fax,$this->country_code,$contactid,$thirdpartyid,'AC_FAX','&nbsp;','fax',$langs->trans("Fax")); $outdone++;
559
		}
560
		if (! empty($this->office_fax)) {
561
			$out.=dol_print_phone($this->office_fax,$this->country_code,$contactid,$thirdpartyid,'AC_FAX','&nbsp;','fax',$langs->trans("Fax")); $outdone++;
562
		}
563
564
		$out.='<div style="clear: both;"></div>';
565
		$outdone=0;
566
		if (! empty($this->email))
567
		{
568
			$out.=dol_print_email($this->email,$this->id,$object->id,'AC_EMAIL',0,0,1);
569
			$outdone++;
570
		}
571
		if (! empty($this->url))
572
		{
573
			$out.=dol_print_url($this->url,'_goout',0,1);
574
			$outdone++;
575
		}
576
		if (! empty($conf->skype->enabled))
577
		{
578
			$out.='<div style="clear: both;"></div>';
579
			if ($this->skype) $out.=dol_print_skype($this->skype,$this->id,$object->id,'AC_SKYPE');
580
			$outdone++;
581
		}
582
583
		$out.='<!-- END Part to show address block -->';
584
585
		return $out;
586
	}
587
588
	/**
589
	 * Return the link of last main doc file for direct public download.
590
	 *
591
	 * @param	string	$modulepart			Module related to document
592
	 * @param	int		$initsharekey		Init the share key if it was not yet defined
593
	 * @param	int		$relativelink		0=Return full external link, 1=Return link relative to root of file
594
	 * @return	string						Link or empty string if there is no download link
595
	 */
596
	function getLastMainDocLink($modulepart, $initsharekey=0, $relativelink=0)
597
	{
598
		global $user, $dolibarr_main_url_root;
599
600
		if (empty($this->last_main_doc))
601
		{
602
			return '';		// No way to known which document name to use
603
		}
604
605
		include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
606
		$ecmfile=new EcmFiles($this->db);
607
		$result = $ecmfile->fetch(0, '', $this->last_main_doc);
608
		if ($result < 0)
609
		{
610
			$this->error = $ecmfile->error;
611
			$this->errors = $ecmfile->errors;
612
			return -1;
613
		}
614
615
		if (empty($ecmfile->id))
616
		{
617
			// Add entry into index
618
			if ($initsharekey)
619
			{
620
				require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
621
				// TODO We can't, we dont' have full path of file, only last_main_doc adn ->element, so we must rebuild full path first
622
				/*
623
				$ecmfile->filepath = $rel_dir;
624
				$ecmfile->filename = $filename;
625
				$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
626
				$ecmfile->fullpath_orig = '';
627
				$ecmfile->gen_or_uploaded = 'generated';
628
				$ecmfile->description = '';    // indexed content
629
				$ecmfile->keyword = '';        // keyword content
630
				$ecmfile->share = getRandomPassword(true);
631
				$result = $ecmfile->create($user);
632
				if ($result < 0)
633
				{
634
					$this->error = $ecmfile->error;
635
					$this->errors = $ecmfile->errors;
636
				}
637
				*/
638
			}
639
			else return '';
640
		}
641
		elseif (empty($ecmfile->share))
642
		{
643
			// Add entry into index
644
			if ($initsharekey)
645
			{
646
				require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
647
				$ecmfile->share = getRandomPassword(true);
648
				$ecmfile->update($user);
649
			}
650
			else return '';
651
		}
652
653
		// Define $urlwithroot
654
		$urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
655
		$urlwithroot=$urlwithouturlroot.DOL_URL_ROOT;		// This is to use external domain name found into config file
656
		//$urlwithroot=DOL_MAIN_URL_ROOT;					// This is to use same domain name than current
657
658
		$forcedownload=0;
659
660
		$paramlink='';
661
		//if (! empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart;		// For sharing with hash (so public files), modulepart is not required.
662
		//if (! empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; 					// For sharing with hash (so public files), entity is not required.
663
		//$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath);								// No need of name of file for public link, we will use the hash
664
		if (! empty($ecmfile->share)) $paramlink.=($paramlink?'&':'').'hashp='.$ecmfile->share;			// Hash for public share
665
		if ($forcedownload) $paramlink.=($paramlink?'&':'').'attachment=1';
666
667
		if ($relativelink)
668
		{
669
			$linktoreturn='document.php'.($paramlink?'?'.$paramlink:'');
670
		}
671
		else
672
		{
673
			$linktoreturn=$urlwithroot.'/document.php'.($paramlink?'?'.$paramlink:'');
674
		}
675
676
		// Here $ecmfile->share is defined
677
		return $linktoreturn;
678
	}
679
680
681
	/**
682
	 *  Add a link between element $this->element and a contact
683
	 *
684
	 *  @param	int		$fk_socpeople       Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link
685
	 *  @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
686
	 *  @param  string	$source             external=Contact extern (llx_socpeople), internal=Contact intern (llx_user)
687
	 *  @param  int		$notrigger			Disable all triggers
688
	 *  @return int                 		<0 if KO, >0 if OK
689
	 */
690
	function add_contact($fk_socpeople, $type_contact, $source='external',$notrigger=0)
691
	{
692
		global $user,$langs;
693
694
695
		dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
696
697
		// Check parameters
698
		if ($fk_socpeople <= 0)
699
		{
700
			$langs->load("errors");
701
			$this->error=$langs->trans("ErrorWrongValueForParameterX","1");
702
			dol_syslog(get_class($this)."::add_contact ".$this->error,LOG_ERR);
703
			return -1;
704
		}
705
		if (! $type_contact)
706
		{
707
			$langs->load("errors");
708
			$this->error=$langs->trans("ErrorWrongValueForParameterX","2");
709
			dol_syslog(get_class($this)."::add_contact ".$this->error,LOG_ERR);
710
			return -2;
711
		}
712
713
		$id_type_contact=0;
714
		if (is_numeric($type_contact))
715
		{
716
			$id_type_contact=$type_contact;
717
		}
718
		else
719
		{
720
			// We look for id type_contact
721
			$sql = "SELECT tc.rowid";
722
			$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
723
			$sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
724
			$sql.= " AND tc.source='".$this->db->escape($source)."'";
725
			$sql.= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
726
			//print $sql;
727
			$resql=$this->db->query($sql);
728
			if ($resql)
729
			{
730
				$obj = $this->db->fetch_object($resql);
731
				if ($obj) $id_type_contact=$obj->rowid;
732
			}
733
		}
734
735
		if ($id_type_contact == 0)
736
		{
737
			$this->error='CODE_NOT_VALID_FOR_THIS_ELEMENT';
738
			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");
739
			return -3;
740
		}
741
742
		$datecreate = dol_now();
743
744
		// Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
745
		$TListeContacts=$this->liste_contact(-1, $source);
746
		$already_added=false;
747
		if(!empty($TListeContacts)) {
748
			foreach($TListeContacts as $array_contact) {
749
				if($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
750
					$already_added=true;
751
					break;
752
				}
753
			}
754
		}
755
756
		if(!$already_added) {
757
758
			$this->db->begin();
759
760
			// Insert into database
761
			$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact";
762
			$sql.= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
763
			$sql.= " VALUES (".$this->id.", ".$fk_socpeople." , " ;
764
			$sql.= "'".$this->db->idate($datecreate)."'";
765
			$sql.= ", 4, ". $id_type_contact;
766
			$sql.= ")";
767
768
			$resql=$this->db->query($sql);
769
			if ($resql)
770
			{
771
				if (! $notrigger)
772
				{
773
					$result=$this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
774
					if ($result < 0)
775
					{
776
						$this->db->rollback();
777
						return -1;
778
					}
779
				}
780
781
				$this->db->commit();
782
				return 1;
783
			}
784
			else
785
			{
786
				if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
787
				{
788
					$this->error=$this->db->errno();
789
					$this->db->rollback();
790
					echo 'err rollback';
791
					return -2;
792
				}
793
				else
794
				{
795
					$this->error=$this->db->error();
796
					$this->db->rollback();
797
					return -1;
798
				}
799
			}
800
		} else return 0;
801
	}
802
803
	/**
804
	 *    Copy contact from one element to current
805
	 *
806
	 *    @param    CommonObject    $objFrom    Source element
807
	 *    @param    string          $source     Nature of contact ('internal' or 'external')
808
	 *    @return   int                         >0 if OK, <0 if KO
809
	 */
810
	function copy_linked_contact($objFrom, $source='internal')
811
	{
812
		$contacts = $objFrom->liste_contact(-1, $source);
813
		foreach($contacts as $contact)
814
		{
815
			if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0)
816
			{
817
				$this->error=$this->db->lasterror();
818
				return -1;
819
			}
820
		}
821
		return 1;
822
	}
823
824
	/**
825
	 *      Update a link to contact line
826
	 *
827
	 *      @param	int		$rowid              Id of line contact-element
828
	 * 		@param	int		$statut	            New status of link
829
	 *      @param  int		$type_contact_id    Id of contact type (not modified if 0)
830
	 *      @param  int		$fk_socpeople	    Id of soc_people to update (not modified if 0)
831
	 *      @return int                 		<0 if KO, >= 0 if OK
832
	 */
833
	function update_contact($rowid, $statut, $type_contact_id=0, $fk_socpeople=0)
834
	{
835
		// Insert into database
836
		$sql = "UPDATE ".MAIN_DB_PREFIX."element_contact set";
837
		$sql.= " statut = ".$statut;
838
		if ($type_contact_id) $sql.= ", fk_c_type_contact = '".$type_contact_id ."'";
839
		if ($fk_socpeople) $sql.= ", fk_socpeople = '".$fk_socpeople ."'";
840
		$sql.= " where rowid = ".$rowid;
841
		$resql=$this->db->query($sql);
842
		if ($resql)
843
		{
844
			return 0;
845
		}
846
		else
847
		{
848
			$this->error=$this->db->lasterror();
849
			return -1;
850
		}
851
	}
852
853
	/**
854
	 *    Delete a link to contact line
855
	 *
856
	 *    @param	int		$rowid			Id of contact link line to delete
857
	 *    @param	int		$notrigger		Disable all triggers
858
	 *    @return   int						>0 if OK, <0 if KO
859
	 */
860
	function delete_contact($rowid, $notrigger=0)
861
	{
862
		global $user;
863
864
865
		$this->db->begin();
866
867
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
868
		$sql.= " WHERE rowid =".$rowid;
869
870
		dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
871
		if ($this->db->query($sql))
872
		{
873
			if (! $notrigger)
874
			{
875
				$result=$this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
876
				if ($result < 0) { $this->db->rollback(); return -1; }
877
			}
878
879
			$this->db->commit();
880
			return 1;
881
		}
882
		else
883
		{
884
			$this->error=$this->db->lasterror();
885
			$this->db->rollback();
886
			return -1;
887
		}
888
	}
889
890
	/**
891
	 *    Delete all links between an object $this and all its contacts
892
	 *
893
	 *	  @param	string	$source		'' or 'internal' or 'external'
894
	 *	  @param	string	$code		Type of contact (code or id)
895
	 *    @return   int					>0 if OK, <0 if KO
896
	 */
897
	function delete_linked_contact($source='',$code='')
898
	{
899
		$temp = array();
900
		$typeContact = $this->liste_type_contact($source,'',0,0,$code);
901
902
		foreach($typeContact as $key => $value)
903
		{
904
			array_push($temp,$key);
905
		}
906
		$listId = implode(",", $temp);
907
908
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
909
		$sql.= " WHERE element_id = ".$this->id;
910
		if ($listId)
911
			$sql.= " AND fk_c_type_contact IN (".$listId.")";
912
913
		dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
914
		if ($this->db->query($sql))
915
		{
916
			return 1;
917
		}
918
		else
919
		{
920
			$this->error=$this->db->lasterror();
921
			return -1;
922
		}
923
	}
924
925
	/**
926
	 *    Get array of all contacts for an object
927
	 *
928
	 *    @param	int			$statut		Status of links to get (-1=all)
929
	 *    @param	string		$source		Source of contact: external or thirdparty (llx_socpeople) or internal (llx_user)
930
	 *    @param	int         $list       0:Return array contains all properties, 1:Return array contains just id
931
	 *    @param    string      $code       Filter on this code of contact type ('SHIPPING', 'BILLING', ...)
932
	 *    @return	array		            Array of contacts
933
	 */
934
	function liste_contact($statut=-1,$source='external',$list=0,$code='')
935
	{
936
		global $langs;
937
938
		$tab=array();
939
940
		$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
941
		if ($source == 'internal') $sql.=", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
942
		if ($source == 'external' || $source == 'thirdparty') $sql.=", t.fk_soc as socid, t.statut as statuscontact";
943
		$sql.= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
944
		$sql.= ", tc.source, tc.element, tc.code, tc.libelle";
945
		$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
946
		$sql.= ", ".MAIN_DB_PREFIX."element_contact ec";
947
		if ($source == 'internal') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
948
		if ($source == 'external'|| $source == 'thirdparty') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
949
		$sql.= " WHERE ec.element_id =".$this->id;
950
		$sql.= " AND ec.fk_c_type_contact=tc.rowid";
951
		$sql.= " AND tc.element='".$this->db->escape($this->element)."'";
952
		if ($code) $sql.= " AND tc.code = '".$this->db->escape($code)."'";
953
		if ($source == 'internal') $sql.= " AND tc.source = 'internal'";
954
		if ($source == 'external' || $source == 'thirdparty') $sql.= " AND tc.source = 'external'";
955
		$sql.= " AND tc.active=1";
956
		if ($statut >= 0) $sql.= " AND ec.statut = '".$statut."'";
957
		$sql.=" ORDER BY t.lastname ASC";
958
959
		dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
960
		$resql=$this->db->query($sql);
961
		if ($resql)
962
		{
963
			$num=$this->db->num_rows($resql);
964
			$i=0;
965
			while ($i < $num)
966
			{
967
				$obj = $this->db->fetch_object($resql);
968
969
				if (! $list)
970
				{
971
					$transkey="TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
972
					$libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
973
					$tab[$i]=array('source'=>$obj->source,'socid'=>$obj->socid,'id'=>$obj->id,
974
								   'nom'=>$obj->lastname,      // For backward compatibility
975
								   'civility'=>$obj->civility, 'lastname'=>$obj->lastname, 'firstname'=>$obj->firstname, 'email'=>$obj->email, 'login'=>$obj->login, 'photo'=>$obj->photo, 'statuscontact'=>$obj->statuscontact,
976
								   'rowid'=>$obj->rowid, 'code'=>$obj->code, 'libelle'=>$libelle_type, 'status'=>$obj->statuslink, 'fk_c_type_contact'=>$obj->fk_c_type_contact);
977
				}
978
				else
979
				{
980
					$tab[$i]=$obj->id;
981
				}
982
983
				$i++;
984
			}
985
986
			return $tab;
987
		}
988
		else
989
		{
990
			$this->error=$this->db->lasterror();
991
			dol_print_error($this->db);
992
			return -1;
993
		}
994
	}
995
996
997
	/**
998
	 * 		Update status of a contact linked to object
999
	 *
1000
	 * 		@param	int		$rowid		Id of link between object and contact
1001
	 * 		@return	int					<0 if KO, >=0 if OK
1002
	 */
1003
	function swapContactStatus($rowid)
1004
	{
1005
		$sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1006
		$sql.= " tc.code, tc.libelle";
1007
		//$sql.= ", s.fk_soc";
1008
		$sql.= " FROM (".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as tc)";
1009
		//$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as s ON ec.fk_socpeople=s.rowid";	// Si contact de type external, alors il est lie a une societe
1010
		$sql.= " WHERE ec.rowid =".$rowid;
1011
		$sql.= " AND ec.fk_c_type_contact=tc.rowid";
1012
		$sql.= " AND tc.element = '".$this->db->escape($this->element)."'";
1013
1014
		dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1015
		$resql=$this->db->query($sql);
1016
		if ($resql)
1017
		{
1018
			$obj = $this->db->fetch_object($resql);
1019
			$newstatut = ($obj->statut == 4) ? 5 : 4;
1020
			$result = $this->update_contact($rowid, $newstatut);
1021
			$this->db->free($resql);
1022
			return $result;
1023
		}
1024
		else
1025
		{
1026
			$this->error=$this->db->error();
1027
			dol_print_error($this->db);
1028
			return -1;
1029
		}
1030
1031
	}
1032
1033
	/**
1034
	 *      Return array with list of possible values for type of contacts
1035
	 *
1036
	 *      @param	string	$source     'internal', 'external' or 'all'
1037
	 *      @param	string	$order		Sort order by : 'position', 'code', 'rowid'...
1038
	 *      @param  int		$option     0=Return array id->label, 1=Return array code->label
1039
	 *      @param  int		$activeonly 0=all status of contact, 1=only the active
1040
	 *		@param	string	$code		Type of contact (Example: 'CUSTOMER', 'SERVICE')
1041
	 *      @return array       		Array list of type of contacts (id->label if option=0, code->label if option=1)
1042
	 */
1043
	function liste_type_contact($source='internal', $order='position', $option=0, $activeonly=0, $code='')
1044
	{
1045
		global $langs;
1046
1047
		if (empty($order)) $order='position';
1048
		if ($order == 'position') $order.=',code';
1049
1050
		$tab = array();
1051
		$sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1052
		$sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
1053
		$sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
1054
		if ($activeonly == 1) $sql.= " AND tc.active=1"; // only the active types
1055
		if (! empty($source) && $source != 'all') $sql.= " AND tc.source='".$this->db->escape($source)."'";
1056
		if (! empty($code)) $sql.= " AND tc.code='".$this->db->escape($code)."'";
1057
		$sql.= $this->db->order($order,'ASC');
1058
1059
		//print "sql=".$sql;
1060
		$resql=$this->db->query($sql);
1061
		if ($resql)
1062
		{
1063
			$num=$this->db->num_rows($resql);
1064
			$i=0;
1065
			while ($i < $num)
1066
			{
1067
				$obj = $this->db->fetch_object($resql);
1068
1069
				$transkey="TypeContact_".$this->element."_".$source."_".$obj->code;
1070
				$libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1071
				if (empty($option)) $tab[$obj->rowid]=$libelle_type;
1072
				else $tab[$obj->code]=$libelle_type;
1073
				$i++;
1074
			}
1075
			return $tab;
1076
		}
1077
		else
1078
		{
1079
			$this->error=$this->db->lasterror();
1080
			//dol_print_error($this->db);
1081
			return null;
1082
		}
1083
	}
1084
1085
	/**
1086
	 *      Return id of contacts for a source and a contact code.
1087
	 *      Example: contact client de facturation ('external', 'BILLING')
1088
	 *      Example: contact client de livraison ('external', 'SHIPPING')
1089
	 *      Example: contact interne suivi paiement ('internal', 'SALESREPFOLL')
1090
	 *
1091
	 *		@param	string	$source		'external' or 'internal'
1092
	 *		@param	string	$code		'BILLING', 'SHIPPING', 'SALESREPFOLL', ...
1093
	 *		@param	int		$status		limited to a certain status
1094
	 *      @return array       		List of id for such contacts
1095
	 */
1096
	function getIdContact($source,$code,$status=0)
1097
	{
1098
		global $conf;
1099
1100
		$result=array();
1101
		$i=0;
1102
		//cas particulier pour les expeditions
1103
		if($this->element=='shipping' && $this->origin_id != 0) {
1104
			$id=$this->origin_id;
1105
			$element='commande';
1106
		} else {
1107
			$id=$this->id;
1108
			$element=$this->element;
1109
		}
1110
1111
		$sql = "SELECT ec.fk_socpeople";
1112
		$sql.= " FROM ".MAIN_DB_PREFIX."element_contact as ec,";
1113
		if ($source == 'internal') $sql.= " ".MAIN_DB_PREFIX."user as c,";
1114
		if ($source == 'external') $sql.= " ".MAIN_DB_PREFIX."socpeople as c,";
1115
		$sql.= " ".MAIN_DB_PREFIX."c_type_contact as tc";
1116
		$sql.= " WHERE ec.element_id = ".$id;
1117
		$sql.= " AND ec.fk_socpeople = c.rowid";
1118
		if ($source == 'internal') $sql.= " AND c.entity IN (0,".$conf->entity.")";
1119
		if ($source == 'external') $sql.= " AND c.entity IN (".getEntity('socpeople').")";
1120
		$sql.= " AND ec.fk_c_type_contact = tc.rowid";
1121
		$sql.= " AND tc.element = '".$element."'";
1122
		$sql.= " AND tc.source = '".$source."'";
1123
		$sql.= " AND tc.code = '".$code."'";
1124
		$sql.= " AND tc.active = 1";
1125
		if ($status) $sql.= " AND ec.statut = ".$status;
1126
1127
		dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1128
		$resql=$this->db->query($sql);
1129
		if ($resql)
1130
		{
1131
			while ($obj = $this->db->fetch_object($resql))
1132
			{
1133
				$result[$i]=$obj->fk_socpeople;
1134
				$i++;
1135
			}
1136
		}
1137
		else
1138
		{
1139
			$this->error=$this->db->error();
1140
			return null;
1141
		}
1142
1143
		return $result;
1144
	}
1145
1146
	/**
1147
	 *		Load object contact with id=$this->contactid into $this->contact
1148
	 *
1149
	 *		@param	int		$contactid      Id du contact. Use this->contactid if empty.
1150
	 *		@return	int						<0 if KO, >0 if OK
1151
	 */
1152
	function fetch_contact($contactid=null)
1153
	{
1154
		if (empty($contactid)) $contactid=$this->contactid;
1155
1156
		if (empty($contactid)) return 0;
1157
1158
		require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1159
		$contact = new Contact($this->db);
1160
		$result=$contact->fetch($contactid);
1161
		$this->contact = $contact;
1162
		return $result;
1163
	}
1164
1165
	/**
1166
	 *    	Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty
1167
	 *
1168
	 *		@param		int		$force_thirdparty_id	Force thirdparty id
1169
	 *		@return		int								<0 if KO, >0 if OK
1170
	 */
1171
	function fetch_thirdparty($force_thirdparty_id=0)
1172
	{
1173
		global $conf;
1174
1175
		if (empty($this->socid) && empty($this->fk_soc) && empty($this->fk_thirdparty) && empty($force_thirdparty_id))
1176
			return 0;
1177
1178
		require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1179
1180
		$idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : $this->fk_thirdparty);
1181
		if ($force_thirdparty_id)
1182
			$idtofetch = $force_thirdparty_id;
1183
1184
		if ($idtofetch) {
1185
			$thirdparty = new Societe($this->db);
1186
			$result = $thirdparty->fetch($idtofetch);
1187
			$this->thirdparty = $thirdparty;
1188
1189
			// Use first price level if level not defined for third party
1190
			if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1191
				$this->thirdparty->price_level = 1;
1192
			}
1193
1194
			return $result;
1195
		} else
1196
			return -1;
1197
	}
1198
1199
1200
	/**
1201
	 * Looks for an object with ref matching the wildcard provided
1202
	 * It does only work when $this->table_ref_field is set
1203
	 *
1204
	 * @param string $ref Wildcard
1205
	 * @return int >1 = OK, 0 = Not found or table_ref_field not defined, <0 = KO
1206
	 */
1207
	public function fetchOneLike($ref)
1208
	{
1209
		if (!$this->table_ref_field) {
1210
			return 0;
1211
		}
1212
1213
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE '.$this->table_ref_field.' LIKE "'.$this->db->escape($ref).'" LIMIT 1';
1214
1215
		$query = $this->db->query($sql);
1216
1217
		if (!$this->db->num_rows($query)) {
1218
			return 0;
1219
		}
1220
1221
		$result = $this->db->fetch_object($query);
1222
1223
		return $this->fetch($result->rowid);
1224
	}
1225
1226
	/**
1227
	 *	Load data for barcode into properties ->barcode_type*
1228
	 *	Properties ->barcode_type that is id of barcode. Type is used to find other properties, but
1229
	 *  if it is not defined, ->element must be defined to know default barcode type.
1230
	 *
1231
	 *	@return		int			<0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded)
1232
	 */
1233
	function fetch_barcode()
1234
	{
1235
		global $conf;
1236
1237
		dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1238
1239
		$idtype=$this->barcode_type;
1240
		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
1241
		{
1242
			if ($this->element == 'product')      $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1243
			else if ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1244
			else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1245
		}
1246
1247
		if ($idtype > 0)
1248
		{
1249
			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
1250
			{
1251
				$sql = "SELECT rowid, code, libelle as label, coder";
1252
				$sql.= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1253
				$sql.= " WHERE rowid = ".$idtype;
1254
				dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1255
				$resql = $this->db->query($sql);
1256
				if ($resql)
1257
				{
1258
					$obj = $this->db->fetch_object($resql);
1259
					$this->barcode_type       = $obj->rowid;
1260
					$this->barcode_type_code  = $obj->code;
1261
					$this->barcode_type_label = $obj->label;
1262
					$this->barcode_type_coder = $obj->coder;
1263
					return 1;
1264
				}
1265
				else
1266
				{
1267
					dol_print_error($this->db);
1268
					return -1;
1269
				}
1270
			}
1271
		}
1272
		return 0;
1273
	}
1274
1275
	/**
1276
	 *		Load the project with id $this->fk_project into this->project
1277
	 *
1278
	 *		@return		int			<0 if KO, >=0 if OK
1279
	 */
1280
	function fetch_projet()
1281
	{
1282
		include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1283
1284
		if (empty($this->fk_project) && ! empty($this->fk_projet)) $this->fk_project = $this->fk_projet;	// For backward compatibility
1285
		if (empty($this->fk_project)) return 0;
1286
1287
		$project = new Project($this->db);
1288
		$result = $project->fetch($this->fk_project);
1289
1290
		$this->projet = $project;	// deprecated
1291
		$this->project = $project;
1292
		return $result;
1293
	}
1294
1295
	/**
1296
	 *		Load the product with id $this->fk_product into this->product
1297
	 *
1298
	 *		@return		int			<0 if KO, >=0 if OK
1299
	 */
1300
	function fetch_product()
1301
	{
1302
		include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1303
1304
		if (empty($this->fk_product)) return 0;
1305
1306
		$product = new Product($this->db);
1307
		$result = $product->fetch($this->fk_product);
1308
1309
		$this->product = $product;
1310
		return $result;
1311
	}
1312
1313
	/**
1314
	 *		Load the user with id $userid into this->user
1315
	 *
1316
	 *		@param	int		$userid 		Id du contact
1317
	 *		@return	int						<0 if KO, >0 if OK
1318
	 */
1319
	function fetch_user($userid)
1320
	{
1321
		$user = new User($this->db);
1322
		$result=$user->fetch($userid);
1323
		$this->user = $user;
1324
		return $result;
1325
	}
1326
1327
	/**
1328
	 *	Read linked origin object
1329
	 *
1330
	 *	@return		void
1331
	 */
1332
	function fetch_origin()
1333
	{
1334
		if ($this->origin == 'shipping') $this->origin = 'expedition';
1335
		if ($this->origin == 'delivery') $this->origin = 'livraison';
1336
1337
		$origin = $this->origin;
1338
1339
		$classname = ucfirst($origin);
1340
		$this->$origin = new $classname($this->db);
1341
		$this->$origin->fetch($this->origin_id);
1342
	}
1343
1344
	/**
1345
     *  Load object from specific field
1346
     *
1347
     *  @param	string	$table		Table element or element line
1348
     *  @param	string	$field		Field selected
1349
     *  @param	string	$key		Import key
1350
     *  @param	string	$element	Element name
1351
     *	@return	int					<0 if KO, >0 if OK
1352
     */
1353
	function fetchObjectFrom($table, $field, $key, $element = null)
1354
	{
1355
		global $conf;
1356
1357
		$result=false;
1358
1359
		$sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$table;
1360
		$sql.= " WHERE ".$field." = '".$key."'";
1361
		if (! empty($element)) {
1362
			$sql.= " AND entity IN (".getEntity($element).")";
1363
		} else {
1364
			$sql.= " AND entity = ".$conf->entity;
1365
		}
1366
1367
		dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1368
		$resql = $this->db->query($sql);
1369
		if ($resql)
1370
		{
1371
			$row = $this->db->fetch_row($resql);
1372
			// Test for avoid error -1
1373
			if ($row[0] > 0) {
1374
				$result = $this->fetch($row[0]);
1375
			}
1376
		}
1377
1378
		return $result;
1379
	}
1380
1381
	/**
1382
	 *	Getter generic. Load value from a specific field
1383
	 *
1384
	 *	@param	string	$table		Table of element or element line
1385
	 *	@param	int		$id			Element id
1386
	 *	@param	string	$field		Field selected
1387
	 *	@return	int					<0 if KO, >0 if OK
1388
	 */
1389
	function getValueFrom($table, $id, $field)
1390
	{
1391
		$result=false;
1392
		if (!empty($id) && !empty($field) && !empty($table)) {
1393
			$sql = "SELECT ".$field." FROM ".MAIN_DB_PREFIX.$table;
1394
			$sql.= " WHERE rowid = ".$id;
1395
1396
			dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1397
			$resql = $this->db->query($sql);
1398
			if ($resql)
1399
			{
1400
				$row = $this->db->fetch_row($resql);
1401
				$result = $row[0];
1402
			}
1403
		}
1404
		return $result;
1405
	}
1406
1407
	/**
1408
	 *	Setter generic. Update a specific field into database.
1409
	 *  Warning: Trigger is run only if param trigkey is provided.
1410
	 *
1411
	 *	@param	string		$field			Field to update
1412
	 *	@param	mixed		$value			New value
1413
	 *	@param	string		$table			To force other table element or element line (should not be used)
1414
	 *	@param	int			$id				To force other object id (should not be used)
1415
	 *	@param	string		$format			Data format ('text', 'date'). 'text' is used if not defined
1416
	 *	@param	string		$id_field		To force rowid field name. 'rowid' is used if not defined
1417
	 *	@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'
1418
	 *  @param  string      $trigkey    	Trigger key to run (in most cases something like 'XXX_MODIFY')
1419
	 *  @param	string		$fk_user_field	Name of field to save user id making change
1420
	 *	@return	int							<0 if KO, >0 if OK
1421
	 *  @see updateExtraField
1422
	 */
1423
	function setValueFrom($field, $value, $table='', $id=null, $format='', $id_field='', $fuser=null, $trigkey='', $fk_user_field='fk_user_modif')
1424
	{
1425
		global $user,$langs,$conf;
1426
1427
		if (empty($table)) 	  $table=$this->table_element;
1428
		if (empty($id))    	  $id=$this->id;
1429
		if (empty($format))   $format='text';
1430
		if (empty($id_field)) $id_field='rowid';
1431
1432
		$error=0;
1433
1434
		$this->db->begin();
1435
1436
		// Special case
1437
		if ($table == 'product' && $field == 'note_private') $field='note';
1438
		if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) $fk_user_field = 'fk_user_mod';
1439
1440
		$sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET ";
1441
1442
		if ($format == 'text') $sql.= $field." = '".$this->db->escape($value)."'";
1443
		else if ($format == 'int') $sql.= $field." = ".$this->db->escape($value);
1444
		else if ($format == 'date') $sql.= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
1445
1446
		if ($fk_user_field)
1447
		{
1448
			if (! empty($fuser) && is_object($fuser)) $sql.=", ".$fk_user_field." = ".$fuser->id;
1449
			elseif (empty($fuser) || $fuser != 'none') $sql.=", ".$fk_user_field." = ".$user->id;
1450
		}
1451
1452
		$sql.= " WHERE ".$id_field." = ".$id;
1453
1454
		dol_syslog(get_class($this)."::".__FUNCTION__."", LOG_DEBUG);
1455
		$resql = $this->db->query($sql);
1456
		if ($resql)
1457
		{
1458
			if ($trigkey)
1459
			{
1460
				// call trigger with updated object values
1461
				if (empty($this->fields) && method_exists($this, 'fetch'))
1462
				{
1463
					$result = $this->fetch($id);
1464
				}
1465
				else
1466
				{
1467
					$result = $this->fetchCommon($id);
1468
				}
1469
				if ($result >= 0) $result=$this->call_trigger($trigkey, (! empty($fuser) && is_object($fuser)) ? $fuser : $user);   // This may set this->errors
1470
				if ($result < 0) $error++;
1471
			}
1472
1473
			if (! $error)
1474
			{
1475
				if (property_exists($this, $field)) $this->$field = $value;
1476
				$this->db->commit();
1477
				return 1;
1478
			}
1479
			else
1480
			{
1481
				$this->db->rollback();
1482
				return -2;
1483
			}
1484
		}
1485
		else
1486
		{
1487
			$this->error=$this->db->lasterror();
1488
			$this->db->rollback();
1489
			return -1;
1490
		}
1491
	}
1492
1493
	/**
1494
	 *      Load properties id_previous and id_next by comparing $fieldid with $this->ref
1495
	 *
1496
	 *      @param	string	$filter		Optional filter. Example: " AND (t.field1 = 'aa' OR t.field2 = 'bb')"
1497
	 *	 	@param  string	$fieldid   	Name of field to use for the select MAX and MIN
1498
	 *		@param	int		$nodbprefix	Do not include DB prefix to forge table name
1499
	 *      @return int         		<0 if KO, >0 if OK
1500
	 */
1501
	function load_previous_next_ref($filter, $fieldid, $nodbprefix=0)
1502
	{
1503
		global $user;
1504
1505
		if (! $this->table_element)
1506
		{
1507
			dol_print_error('',get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
1508
			return -1;
1509
		}
1510
		if ($fieldid == 'none') return 1;
1511
1512
		// Security on socid
1513
		$socid = 0;
1514
		if ($user->societe_id > 0) $socid = $user->societe_id;
1515
1516
		// this->ismultientitymanaged contains
1517
		// 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
1518
		$alias = 's';
1519
		if ($this->element == 'societe') $alias = 'te';
1520
1521
		$sql = "SELECT MAX(te.".$fieldid.")";
1522
		$sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1523
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to entity
1524
		else if ($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
1525
		else if ($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
1526
		if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid)  $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1527
		$sql.= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1528
		if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1529
		if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1530
		if (! empty($filter))
1531
		{
1532
			if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1533
			$sql.=$filter;
1534
		}
1535
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to entity
1536
		else if ($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
1537
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1538
		if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1539
		if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1540
		if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1541
		//print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1542
1543
		$result = $this->db->query($sql);
1544
		if (! $result)
1545
		{
1546
			$this->error=$this->db->lasterror();
1547
			return -1;
1548
		}
1549
		$row = $this->db->fetch_row($result);
1550
		$this->ref_previous = $row[0];
1551
1552
1553
		$sql = "SELECT MIN(te.".$fieldid.")";
1554
		$sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1555
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ", ".MAIN_DB_PREFIX."societe as s";	// If we need to link to societe to limit select to entity
1556
		else if ($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
1557
		else if ($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
1558
		if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1559
		$sql.= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'";  // ->ref must always be defined (set to id if field does not exists)
1560
		if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1561
		if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1562
		if (! empty($filter))
1563
		{
1564
			if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND ";   // For backward compatibility
1565
			$sql.=$filter;
1566
		}
1567
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ' AND te.fk_soc = s.rowid';			// If we need to link to societe to limit select to entity
1568
		else if ($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
1569
		if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1570
		if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1571
		if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1572
		if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1573
		//print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1574
		// 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
1575
1576
		$result = $this->db->query($sql);
1577
		if (! $result)
1578
		{
1579
			$this->error=$this->db->lasterror();
1580
			return -2;
1581
		}
1582
		$row = $this->db->fetch_row($result);
1583
		$this->ref_next = $row[0];
1584
1585
		return 1;
1586
	}
1587
1588
1589
	/**
1590
	 *      Return list of id of contacts of object
1591
	 *
1592
	 *      @param	string	$source     Source of contact: external (llx_socpeople) or internal (llx_user) or thirdparty (llx_societe)
1593
	 *      @return array				Array of id of contacts (if source=external or internal)
1594
	 * 									Array of id of third parties with at least one contact on object (if source=thirdparty)
1595
	 */
1596
	function getListContactId($source='external')
1597
	{
1598
		$contactAlreadySelected = array();
1599
		$tab = $this->liste_contact(-1,$source);
1600
		$num=count($tab);
1601
		$i = 0;
1602
		while ($i < $num)
1603
		{
1604
			if ($source == 'thirdparty') $contactAlreadySelected[$i] = $tab[$i]['socid'];
1605
			else  $contactAlreadySelected[$i] = $tab[$i]['id'];
1606
			$i++;
1607
		}
1608
		return $contactAlreadySelected;
1609
	}
1610
1611
1612
	/**
1613
	 *	Link element with a project
1614
	 *
1615
	 *	@param     	int		$projectid		Project id to link element to
1616
	 *	@return		int						<0 if KO, >0 if OK
1617
	 */
1618
	function setProject($projectid)
1619
	{
1620
		if (! $this->table_element)
1621
		{
1622
			dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined",LOG_ERR);
1623
			return -1;
1624
		}
1625
1626
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1627
		if ($this->table_element == 'actioncomm')
1628
		{
1629
			if ($projectid) $sql.= ' SET fk_project = '.$projectid;
1630
			else $sql.= ' SET fk_project = NULL';
1631
			$sql.= ' WHERE id = '.$this->id;
1632
		}
1633
		else
1634
		{
1635
			if ($projectid) $sql.= ' SET fk_projet = '.$projectid;
1636
			else $sql.= ' SET fk_projet = NULL';
1637
			$sql.= ' WHERE rowid = '.$this->id;
1638
		}
1639
1640
		dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
1641
		if ($this->db->query($sql))
1642
		{
1643
			$this->fk_project = $projectid;
1644
			return 1;
1645
		}
1646
		else
1647
		{
1648
			dol_print_error($this->db);
1649
			return -1;
1650
		}
1651
	}
1652
1653
	/**
1654
	 *  Change the payments methods
1655
	 *
1656
	 *  @param		int		$id		Id of new payment method
1657
	 *  @return		int				>0 if OK, <0 if KO
1658
	 */
1659
	function setPaymentMethods($id)
1660
	{
1661
		dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
1662
		if ($this->statut >= 0 || $this->element == 'societe')
1663
		{
1664
			// TODO uniformize field name
1665
			$fieldname = 'fk_mode_reglement';
1666
			if ($this->element == 'societe') $fieldname = 'mode_reglement';
1667
			if (get_class($this) == 'Fournisseur') $fieldname = 'mode_reglement_supplier';
1668
1669
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1670
			$sql .= ' SET '.$fieldname.' = '.$id;
1671
			$sql .= ' WHERE rowid='.$this->id;
1672
1673
			if ($this->db->query($sql))
1674
			{
1675
				$this->mode_reglement_id = $id;
1676
				// for supplier
1677
				if (get_class($this) == 'Fournisseur') $this->mode_reglement_supplier_id = $id;
1678
				return 1;
1679
			}
1680
			else
1681
			{
1682
				dol_syslog(get_class($this).'::setPaymentMethods Erreur '.$sql.' - '.$this->db->error());
1683
				$this->error=$this->db->error();
1684
				return -1;
1685
			}
1686
		}
1687
		else
1688
		{
1689
			dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
1690
			$this->error='Status of the object is incompatible '.$this->statut;
1691
			return -2;
1692
		}
1693
	}
1694
1695
	/**
1696
	 *  Change the multicurrency code
1697
	 *
1698
	 *  @param		string	$code	multicurrency code
1699
	 *  @return		int				>0 if OK, <0 if KO
1700
	 */
1701
	function setMulticurrencyCode($code)
1702
	{
1703
		dol_syslog(get_class($this).'::setMulticurrencyCode('.$id.')');
0 ignored issues
show
Bug introduced by
The variable $id does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1704
		if ($this->statut >= 0 || $this->element == 'societe')
1705
		{
1706
			$fieldname = 'multicurrency_code';
1707
1708
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1709
			$sql .= ' SET '.$fieldname." = '".$this->db->escape($code)."'";
1710
			$sql .= ' WHERE rowid='.$this->id;
1711
1712
			if ($this->db->query($sql))
1713
			{
1714
				$this->multicurrency_code = $code;
1715
1716
				list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
1717
				if ($rate) $this->setMulticurrencyRate($rate);
1718
1719
				return 1;
1720
			}
1721
			else
1722
			{
1723
				dol_syslog(get_class($this).'::setMulticurrencyCode Erreur '.$sql.' - '.$this->db->error());
1724
				$this->error=$this->db->error();
1725
				return -1;
1726
			}
1727
		}
1728
		else
1729
		{
1730
			dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
1731
			$this->error='Status of the object is incompatible '.$this->statut;
1732
			return -2;
1733
		}
1734
	}
1735
1736
	/**
1737
	 *  Change the multicurrency rate
1738
	 *
1739
	 *  @param		double	$rate	multicurrency rate
1740
	 *  @param		int		$mode	mode 1 : amounts in company currency will be recalculated, mode 2 : amounts in foreign currency
1741
	 *  @return		int				>0 if OK, <0 if KO
1742
	 */
1743
	function setMulticurrencyRate($rate, $mode=1)
1744
	{
1745
		dol_syslog(get_class($this).'::setMulticurrencyRate('.$id.')');
0 ignored issues
show
Bug introduced by
The variable $id does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1746
		if ($this->statut >= 0 || $this->element == 'societe')
1747
		{
1748
			$fieldname = 'multicurrency_tx';
1749
1750
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1751
			$sql .= ' SET '.$fieldname.' = '.$rate;
1752
			$sql .= ' WHERE rowid='.$this->id;
1753
1754
			if ($this->db->query($sql))
1755
			{
1756
				$this->multicurrency_tx = $rate;
1757
1758
				// Update line price
1759
				if (!empty($this->lines))
1760
				{
1761
					foreach ($this->lines as &$line)
1762
					{
1763
						if($mode == 1) {
1764
							$line->subprice = 0;
1765
						}
1766
1767
						switch ($this->element) {
1768
							case 'propal':
1769
								$this->updateline(
1770
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1771
									($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1772
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start,
1773
									$line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1774
								);
1775
								break;
1776
							case 'commande':
1777
								$this->updateline(
1778
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1779
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end,
1780
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1781
									$line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1782
								);
1783
								break;
1784
							case 'facture':
1785
								$this->updateline(
1786
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1787
									$line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,
1788
									$line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1789
									$line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice
1790
								);
1791
								break;
1792
							case 'supplier_proposal':
1793
								$this->updateline(
1794
									$line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1795
									($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1796
									$line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options,
1797
									$line->ref_fourn, $line->multicurrency_subprice
1798
								);
1799
								break;
1800
							case 'order_supplier':
1801
								$this->updateline(
1802
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1803
									$line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,  $line->product_type, false,
1804
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1805
								);
1806
								break;
1807
							case 'invoice_supplier':
1808
								$this->updateline(
1809
									$line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx,
1810
									$line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false,
1811
									$line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1812
								);
1813
								break;
1814
							default:
1815
								dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
1816
								break;
1817
						}
1818
1819
					}
1820
				}
1821
1822
				return 1;
1823
			}
1824
			else
1825
			{
1826
				dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
1827
				$this->error=$this->db->error();
1828
				return -1;
1829
			}
1830
		}
1831
		else
1832
		{
1833
			dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
1834
			$this->error='Status of the object is incompatible '.$this->statut;
1835
			return -2;
1836
		}
1837
	}
1838
1839
	/**
1840
	 *  Change the payments terms
1841
	 *
1842
	 *  @param		int		$id		Id of new payment terms
1843
	 *  @return		int				>0 if OK, <0 if KO
1844
	 */
1845
	function setPaymentTerms($id)
1846
	{
1847
		dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
1848
		if ($this->statut >= 0 || $this->element == 'societe')
1849
		{
1850
			// TODO uniformize field name
1851
			$fieldname = 'fk_cond_reglement';
1852
			if ($this->element == 'societe') $fieldname = 'cond_reglement';
1853
			if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
1854
1855
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1856
			$sql .= ' SET '.$fieldname.' = '.$id;
1857
			$sql .= ' WHERE rowid='.$this->id;
1858
1859
			if ($this->db->query($sql))
1860
			{
1861
				$this->cond_reglement_id = $id;
1862
				// for supplier
1863
				if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
1864
				$this->cond_reglement = $id;	// for compatibility
1865
				return 1;
1866
			}
1867
			else
1868
			{
1869
				dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
1870
				$this->error=$this->db->error();
1871
				return -1;
1872
			}
1873
		}
1874
		else
1875
		{
1876
			dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
1877
			$this->error='Status of the object is incompatible '.$this->statut;
1878
			return -2;
1879
		}
1880
	}
1881
1882
	/**
1883
	 *	Define delivery address
1884
	 *  @deprecated
1885
	 *
1886
	 *	@param      int		$id		Address id
1887
	 *	@return     int				<0 si ko, >0 si ok
1888
	 */
1889
	function setDeliveryAddress($id)
1890
	{
1891
		$fieldname = 'fk_delivery_address';
1892
		if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
1893
1894
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
1895
		$sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
1896
1897
		if ($this->db->query($sql))
1898
		{
1899
			$this->fk_delivery_address = $id;
1900
			return 1;
1901
		}
1902
		else
1903
		{
1904
			$this->error=$this->db->error();
1905
			dol_syslog(get_class($this).'::setDeliveryAddress Erreur '.$sql.' - '.$this->error);
1906
			return -1;
1907
		}
1908
	}
1909
1910
1911
	/**
1912
	 *  Change the shipping method
1913
	 *
1914
	 *  @param      int     $shipping_method_id     Id of shipping method
1915
     *  @param      bool    $notrigger              false=launch triggers after, true=disable triggers
1916
     *  @param      User	$userused               Object user
1917
	 *
1918
	 *  @return     int              1 if OK, 0 if KO
1919
	 */
1920
	function setShippingMethod($shipping_method_id, $notrigger=false, $userused=null)
1921
	{
1922
        global $user;
1923
1924
        if (empty($userused)) $userused=$user;
1925
1926
        $error = 0;
1927
1928
		if (! $this->table_element) {
1929
			dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined",LOG_ERR);
1930
			return -1;
1931
		}
1932
1933
        $this->db->begin();
1934
1935
		if ($shipping_method_id<0) $shipping_method_id='NULL';
1936
		dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
1937
1938
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1939
		$sql.= " SET fk_shipping_method = ".$shipping_method_id;
1940
		$sql.= " WHERE rowid=".$this->id;
1941
        $resql = $this->db->query($sql);
1942
		if (! $resql) {
1943
			dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
1944
			$this->error = $this->db->lasterror();
1945
			$error++;
1946
        } else {
1947
            if (!$notrigger)
1948
            {
1949
                // Call trigger
1950
                $this->context=array('shippingmethodupdate'=>1);
1951
                $result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
1952
                if ($result < 0) $error++;
1953
                // End call trigger
1954
            }
1955
        }
1956
        if ($error)
1957
        {
1958
            $this->db->rollback();
1959
            return -1;
1960
        } else {
1961
            $this->shipping_method_id = ($shipping_method_id=='NULL')?null:$shipping_method_id;
0 ignored issues
show
Documentation Bug introduced by
It seems like $shipping_method_id == '...l : $shipping_method_id can also be of type string. However, the property $shipping_method_id is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1962
            $this->db->commit();
1963
            return 1;
1964
        }
1965
1966
	}
1967
1968
1969
	/**
1970
	 *  Change the warehouse
1971
	 *
1972
	 *  @param      int     $warehouse_id     Id of warehouse
1973
	 *  @return     int              1 if OK, 0 if KO
1974
	 */
1975
	function setWarehouse($warehouse_id)
1976
	{
1977
		if (! $this->table_element) {
1978
			dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined",LOG_ERR);
1979
			return -1;
1980
		}
1981
		if ($warehouse_id<0) $warehouse_id='NULL';
1982
		dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
1983
1984
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1985
		$sql.= " SET fk_warehouse = ".$warehouse_id;
1986
		$sql.= " WHERE rowid=".$this->id;
1987
1988
		if ($this->db->query($sql)) {
1989
			$this->warehouse_id = ($warehouse_id=='NULL')?null:$warehouse_id;
1990
			return 1;
1991
		} else {
1992
			dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
1993
			$this->error=$this->db->error();
1994
			return 0;
1995
		}
1996
	}
1997
1998
1999
	/**
2000
	 *		Set last model used by doc generator
2001
	 *
2002
	 *		@param		User	$user		User object that make change
2003
	 *		@param		string	$modelpdf	Modele name
2004
	 *		@return		int					<0 if KO, >0 if OK
2005
	 */
2006
	function setDocModel($user, $modelpdf)
2007
	{
2008
		if (! $this->table_element)
2009
		{
2010
			dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined",LOG_ERR);
2011
			return -1;
2012
		}
2013
2014
		$newmodelpdf=dol_trunc($modelpdf,255);
2015
2016
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2017
		$sql.= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2018
		$sql.= " WHERE rowid = ".$this->id;
2019
		// if ($this->element == 'facture') $sql.= " AND fk_statut < 2";
2020
		// if ($this->element == 'propal')  $sql.= " AND fk_statut = 0";
2021
2022
		dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2023
		$resql=$this->db->query($sql);
2024
		if ($resql)
2025
		{
2026
			$this->modelpdf=$modelpdf;
2027
			return 1;
2028
		}
2029
		else
2030
		{
2031
			dol_print_error($this->db);
2032
			return 0;
2033
		}
2034
	}
2035
2036
2037
	/**
2038
	 *  Change the bank account
2039
	 *
2040
	 *  @param		int		$fk_account		Id of bank account
2041
	 *  @param      bool    $notrigger      false=launch triggers after, true=disable triggers
2042
	 *  @param      User	$userused		Object user
2043
	 *  @return		int				1 if OK, 0 if KO
2044
	 */
2045
	function setBankAccount($fk_account, $notrigger=false, $userused=null)
2046
	{
2047
        global $user;
2048
2049
        if (empty($userused)) $userused=$user;
2050
2051
        $error = 0;
2052
2053
		if (! $this->table_element) {
2054
			dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined",LOG_ERR);
2055
			return -1;
2056
		}
2057
        $this->db->begin();
2058
2059
		if ($fk_account<0) $fk_account='NULL';
2060
		dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2061
2062
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2063
		$sql.= " SET fk_account = ".$fk_account;
2064
		$sql.= " WHERE rowid=".$this->id;
2065
2066
        $resql = $this->db->query($sql);
2067
        if (! $resql)
2068
        {
2069
            dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2070
            $this->error = $this->db->lasterror();
2071
            $error++;
2072
        }
2073
        else
2074
        {
2075
            if (!$notrigger)
2076
            {
2077
                // Call trigger
2078
                $this->context=array('bankaccountupdate'=>1);
2079
                $result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2080
                if ($result < 0) $error++;
2081
                // End call trigger
2082
            }
2083
        }
2084
        if ($error)
2085
        {
2086
            $this->db->rollback();
2087
            return -1;
2088
        }
2089
        else
2090
        {
2091
            $this->fk_account = ($fk_account=='NULL')?null:$fk_account;
0 ignored issues
show
Documentation Bug introduced by
It seems like $fk_account == 'NULL' ? null : $fk_account can also be of type string. However, the property $fk_account is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2092
            $this->db->commit();
2093
            return 1;
2094
        }
2095
    }
2096
2097
2098
	// TODO: Move line related operations to CommonObjectLine?
2099
2100
	/**
2101
	 *  Save a new position (field rang) for details lines.
2102
	 *  You can choose to set position for lines with already a position or lines without any position defined.
2103
	 *
2104
	 * 	@param		boolean		$renum			   True to renum all already ordered lines, false to renum only not already ordered lines.
2105
	 * 	@param		string		$rowidorder		   ASC or DESC
2106
	 * 	@param		boolean		$fk_parent_line    Table with fk_parent_line field or not
2107
	 * 	@return		int                            <0 if KO, >0 if OK
2108
	 */
2109
	function line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
2110
	{
2111
		if (! $this->table_element_line)
2112
		{
2113
			dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined",LOG_ERR);
2114
			return -1;
2115
		}
2116
		if (! $this->fk_element)
2117
		{
2118
			dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined",LOG_ERR);
2119
			return -1;
2120
		}
2121
2122
		// Count number of lines to reorder (according to choice $renum)
2123
		$nl=0;
2124
		$sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2125
		$sql.= ' WHERE '.$this->fk_element.'='.$this->id;
2126
		if (! $renum) $sql.= ' AND rang = 0';
2127
		if ($renum) $sql.= ' AND rang <> 0';
2128
2129
		dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
2130
		$resql = $this->db->query($sql);
2131
		if ($resql)
2132
		{
2133
			$row = $this->db->fetch_row($resql);
2134
			$nl = $row[0];
2135
		}
2136
		else dol_print_error($this->db);
2137
		if ($nl > 0)
2138
		{
2139
			// The goal of this part is to reorder all lines, with all children lines sharing the same
2140
			// counter that parents.
2141
			$rows=array();
2142
2143
			// We first search all lines that are parent lines (for multilevel details lines)
2144
			$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2145
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2146
			if ($fk_parent_line) $sql.= ' AND fk_parent_line IS NULL';
2147
			$sql.= ' ORDER BY rang ASC, rowid '.$rowidorder;
2148
2149
			dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
2150
			$resql = $this->db->query($sql);
2151
			if ($resql)
2152
			{
2153
				$i=0;
2154
				$num = $this->db->num_rows($resql);
2155
				while ($i < $num)
2156
				{
2157
					$row = $this->db->fetch_row($resql);
2158
					$rows[] = $row[0];	// Add parent line into array rows
2159
					$childrens = $this->getChildrenOfLine($row[0]);
2160
					if (! empty($childrens))
2161
					{
2162
						foreach($childrens as $child)
2163
						{
2164
							array_push($rows, $child);
2165
						}
2166
					}
2167
					$i++;
2168
				}
2169
2170
				// Now we set a new number for each lines (parent and children with children included into parent tree)
2171
				if (! empty($rows))
2172
				{
2173
					foreach($rows as $key => $row)
2174
					{
2175
						$this->updateRangOfLine($row, ($key+1));
2176
					}
2177
				}
2178
			}
2179
			else
2180
			{
2181
				dol_print_error($this->db);
2182
			}
2183
		}
2184
		return 1;
2185
	}
2186
2187
	/**
2188
	 * 	Get children of line
2189
	 *
2190
	 * 	@param	int		$id		Id of parent line
2191
	 * 	@return	array			Array with list of children lines id
2192
	 */
2193
	function getChildrenOfLine($id)
2194
	{
2195
		$rows=array();
2196
2197
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2198
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2199
		$sql.= ' AND fk_parent_line = '.$id;
2200
		$sql.= ' ORDER BY rang ASC';
2201
2202
		dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG);
2203
		$resql = $this->db->query($sql);
2204
		if ($resql)
2205
		{
2206
			$i=0;
2207
			$num = $this->db->num_rows($resql);
2208
			while ($i < $num)
2209
			{
2210
				$row = $this->db->fetch_row($resql);
2211
				$rows[$i] = $row[0];
2212
				$i++;
2213
			}
2214
		}
2215
2216
		return $rows;
2217
	}
2218
2219
	/**
2220
	 * 	Update a line to have a lower rank
2221
	 *
2222
	 * 	@param 	int			$rowid				Id of line
2223
	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2224
	 * 	@return	void
2225
	 */
2226
	function line_up($rowid, $fk_parent_line=true)
2227
	{
2228
		$this->line_order(false, 'ASC', $fk_parent_line);
2229
2230
		// Get rang of line
2231
		$rang = $this->getRangOfLine($rowid);
2232
2233
		// Update position of line
2234
		$this->updateLineUp($rowid, $rang);
2235
	}
2236
2237
	/**
2238
	 * 	Update a line to have a higher rank
2239
	 *
2240
	 * 	@param	int			$rowid				Id of line
2241
	 * 	@param	boolean		$fk_parent_line		Table with fk_parent_line field or not
2242
	 * 	@return	void
2243
	 */
2244
	function line_down($rowid, $fk_parent_line=true)
2245
	{
2246
		$this->line_order(false, 'ASC', $fk_parent_line);
2247
2248
		// Get rang of line
2249
		$rang = $this->getRangOfLine($rowid);
2250
2251
		// Get max value for rang
2252
		$max = $this->line_max();
2253
2254
		// Update position of line
2255
		$this->updateLineDown($rowid, $rang, $max);
2256
	}
2257
2258
	/**
2259
	 * 	Update position of line (rang)
2260
	 *
2261
	 * 	@param	int		$rowid		Id of line
2262
	 * 	@param	int		$rang		Position
2263
	 * 	@return	void
2264
	 */
2265
	function updateRangOfLine($rowid,$rang)
2266
	{
2267
		$fieldposition = 'rang';
2268
		if ($this->table_element_line == 'ecm_files') $fieldposition = 'position';
2269
2270
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2271
		$sql.= ' WHERE rowid = '.$rowid;
2272
2273
		dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
2274
		if (! $this->db->query($sql))
2275
		{
2276
			dol_print_error($this->db);
2277
		}
2278
	}
2279
2280
	/**
2281
	 * 	Update position of line with ajax (rang)
2282
	 *
2283
	 * 	@param	array	$rows	Array of rows
2284
	 * 	@return	void
2285
	 */
2286
	function line_ajaxorder($rows)
2287
	{
2288
		$num = count($rows);
2289
		for ($i = 0 ; $i < $num ; $i++)
2290
		{
2291
			$this->updateRangOfLine($rows[$i], ($i+1));
2292
		}
2293
	}
2294
2295
	/**
2296
	 * 	Update position of line up (rang)
2297
	 *
2298
	 * 	@param	int		$rowid		Id of line
2299
	 * 	@param	int		$rang		Position
2300
	 * 	@return	void
2301
	 */
2302
	function updateLineUp($rowid,$rang)
2303
	{
2304
		if ($rang > 1 )
2305
		{
2306
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.$rang ;
2307
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2308
			$sql.= ' AND rang = '.($rang - 1);
2309
			if ($this->db->query($sql) )
2310
			{
2311
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang  = '.($rang - 1);
2312
				$sql.= ' WHERE rowid = '.$rowid;
2313
				if (! $this->db->query($sql) )
2314
				{
2315
					dol_print_error($this->db);
2316
				}
2317
			}
2318
			else
2319
			{
2320
				dol_print_error($this->db);
2321
			}
2322
		}
2323
	}
2324
2325
	/**
2326
	 * 	Update position of line down (rang)
2327
	 *
2328
	 * 	@param	int		$rowid		Id of line
2329
	 * 	@param	int		$rang		Position
2330
	 * 	@param	int		$max		Max
2331
	 * 	@return	void
2332
	 */
2333
	function updateLineDown($rowid,$rang,$max)
2334
	{
2335
		if ($rang < $max)
2336
		{
2337
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.$rang;
2338
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2339
			$sql.= ' AND rang = '.($rang+1);
2340
			if ($this->db->query($sql) )
2341
			{
2342
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET rang = '.($rang+1);
2343
				$sql.= ' WHERE rowid = '.$rowid;
2344
				if (! $this->db->query($sql) )
2345
				{
2346
					dol_print_error($this->db);
2347
				}
2348
			}
2349
			else
2350
			{
2351
				dol_print_error($this->db);
2352
			}
2353
		}
2354
	}
2355
2356
	/**
2357
	 * 	Get position of line (rang)
2358
	 *
2359
	 * 	@param		int		$rowid		Id of line
2360
	 *  @return		int     			Value of rang in table of lines
2361
	 */
2362
	function getRangOfLine($rowid)
2363
	{
2364
		$sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2365
		$sql.= ' WHERE rowid ='.$rowid;
2366
2367
		dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
2368
		$resql = $this->db->query($sql);
2369
		if ($resql)
2370
		{
2371
			$row = $this->db->fetch_row($resql);
2372
			return $row[0];
2373
		}
2374
	}
2375
2376
	/**
2377
	 * 	Get rowid of the line relative to its position
2378
	 *
2379
	 * 	@param		int		$rang		Rang value
2380
	 *  @return     int     			Rowid of the line
2381
	 */
2382
	function getIdOfLine($rang)
2383
	{
2384
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2385
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2386
		$sql.= ' AND rang = '.$rang;
2387
		$resql = $this->db->query($sql);
2388
		if ($resql)
2389
		{
2390
			$row = $this->db->fetch_row($resql);
2391
			return $row[0];
2392
		}
2393
	}
2394
2395
	/**
2396
	 * 	Get max value used for position of line (rang)
2397
	 *
2398
	 * 	@param		int		$fk_parent_line		Parent line id
2399
	 *  @return     int  			   			Max value of rang in table of lines
2400
	 */
2401
	function line_max($fk_parent_line=0)
2402
	{
2403
		// Search the last rang with fk_parent_line
2404
		if ($fk_parent_line)
2405
		{
2406
			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2407
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2408
			$sql.= ' AND fk_parent_line = '.$fk_parent_line;
2409
2410
			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2411
			$resql = $this->db->query($sql);
2412
			if ($resql)
2413
			{
2414
				$row = $this->db->fetch_row($resql);
2415
				if (! empty($row[0]))
2416
				{
2417
					return $row[0];
2418
				}
2419
				else
2420
				{
2421
					return $this->getRangOfLine($fk_parent_line);
2422
				}
2423
			}
2424
		}
2425
		// If not, search the last rang of element
2426
		else
2427
		{
2428
			$sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2429
			$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2430
2431
			dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2432
			$resql = $this->db->query($sql);
2433
			if ($resql)
2434
			{
2435
				$row = $this->db->fetch_row($resql);
2436
				return $row[0];
2437
			}
2438
		}
2439
	}
2440
2441
	/**
2442
	 *  Update external ref of element
2443
	 *
2444
	 *  @param      string		$ref_ext	Update field ref_ext
2445
	 *  @return     int      		   		<0 if KO, >0 if OK
2446
	 */
2447
	function update_ref_ext($ref_ext)
2448
	{
2449
		if (! $this->table_element)
2450
		{
2451
			dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
2452
			return -1;
2453
		}
2454
2455
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2456
		$sql.= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
2457
		$sql.= " WHERE ".(isset($this->table_rowid)?$this->table_rowid:'rowid')." = ". $this->id;
2458
2459
		dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
2460
		if ($this->db->query($sql))
2461
		{
2462
			$this->ref_ext = $ref_ext;
2463
			return 1;
2464
		}
2465
		else
2466
		{
2467
			$this->error=$this->db->error();
2468
			return -1;
2469
		}
2470
	}
2471
2472
	/**
2473
	 *  Update note of element
2474
	 *
2475
	 *  @param      string		$note		New value for note
2476
	 *  @param		string		$suffix		'', '_public' or '_private'
2477
	 *  @return     int      		   		<0 if KO, >0 if OK
2478
	 */
2479
	function update_note($note,$suffix='')
2480
	{
2481
		global $user;
2482
2483
		if (! $this->table_element)
2484
		{
2485
			$this->error='update_note was called on objet with property table_element not defined';
2486
			dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
2487
			return -1;
2488
		}
2489
		if (! in_array($suffix,array('','_public','_private')))
2490
		{
2491
			$this->error='update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
2492
			dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
2493
			return -2;
2494
		}
2495
		// Special cas
2496
		//var_dump($this->table_element);exit;
2497
		if ($this->table_element == 'product') $suffix='';
2498
2499
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2500
		$sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL");
2501
		$sql.= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))?"fk_user_mod":"fk_user_modif")." = ".$user->id;
2502
		$sql.= " WHERE rowid =". $this->id;
2503
2504
		dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
2505
		if ($this->db->query($sql))
2506
		{
2507
			if ($suffix == '_public') $this->note_public = $note;
2508
			else if ($suffix == '_private') $this->note_private = $note;
2509
			else
2510
			{
2511
				$this->note = $note;      // deprecated
2512
				$this->note_private = $note;
2513
			}
2514
			return 1;
2515
		}
2516
		else
2517
		{
2518
			$this->error=$this->db->lasterror();
2519
			return -1;
2520
		}
2521
	}
2522
2523
	/**
2524
	 * 	Update public note (kept for backward compatibility)
2525
	 *
2526
	 * @param      string		$note		New value for note
2527
	 * @return     int      		   		<0 if KO, >0 if OK
2528
	 * @deprecated
2529
	 * @see update_note()
2530
	 */
2531
	function update_note_public($note)
2532
	{
2533
		return $this->update_note($note,'_public');
2534
	}
2535
2536
	/**
2537
	 *	Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
2538
	 *  Must be called at end of methods addline or updateline.
2539
	 *
2540
	 *	@param	int		$exclspec          	>0 = Exclude special product (product_type=9)
2541
	 *  @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
2542
	 *  @param	int		$nodatabaseupdate	1=Do not update database. Update only properties of object.
2543
	 *  @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.
2544
	 *	@return	int    			           	<0 if KO, >0 if OK
2545
	 */
2546
	function update_price($exclspec=0,$roundingadjust='none',$nodatabaseupdate=0,$seller=null)
2547
	{
2548
		global $conf;
2549
2550
		// Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
2551
		$MODULE = "";
2552
		if ($this->element == 'propal')
2553
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
2554
		elseif ($this->element == 'order')
2555
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
2556
		elseif ($this->element == 'facture')
2557
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
2558
		elseif ($this->element == 'facture_fourn')
2559
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
2560
		elseif ($this->element == 'order_supplier')
2561
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
2562
		elseif ($this->element == 'supplier_proposal')
2563
			$MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
2564
2565
		if (! empty($MODULE)) {
2566
			if (! empty($conf->global->$MODULE)) {
2567
				$modsactivated = explode(',', $conf->global->$MODULE);
2568
				foreach ($modsactivated as $mod) {
2569
					if ($conf->$mod->enabled)
2570
						return 1; // update was disabled by specific setup
2571
				}
2572
			}
2573
		}
2574
2575
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2576
2577
		if ($roundingadjust == '-1') $roundingadjust='auto';	// For backward compatibility
2578
2579
		$forcedroundingmode=$roundingadjust;
2580
		if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode=$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
2581
		elseif ($forcedroundingmode == 'auto') $forcedroundingmode='0';
2582
2583
		$error=0;
2584
2585
		$multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
2586
2587
		// Define constants to find lines to sum
2588
		$fieldtva='total_tva';
2589
		$fieldlocaltax1='total_localtax1';
2590
		$fieldlocaltax2='total_localtax2';
2591
		$fieldup='subprice';
2592
		if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
2593
		{
2594
			$fieldtva='tva';
2595
			$fieldup='pu_ht';
2596
		}
2597
		if ($this->element == 'expensereport')
2598
		{
2599
			$fieldup='value_unit';
2600
		}
2601
2602
		$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,';
2603
		$sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
2604
			if ($this->table_element_line == 'facturedet') $sql.= ', situation_percent';
2605
			$sql.= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
2606
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2607
		$sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2608
		if ($exclspec)
2609
		{
2610
			$product_field='product_type';
2611
			if ($this->table_element_line == 'contratdet') $product_field='';    // contratdet table has no product_type field
2612
			if ($product_field) $sql.= ' AND '.$product_field.' <> 9';
2613
		}
2614
		$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
2615
2616
		dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2617
		$resql = $this->db->query($sql);
2618
		if ($resql)
2619
		{
2620
			$this->total_ht  = 0;
2621
			$this->total_tva = 0;
2622
			$this->total_localtax1 = 0;
2623
			$this->total_localtax2 = 0;
2624
			$this->total_ttc = 0;
2625
			$total_ht_by_vats  = array();
2626
			$total_tva_by_vats = array();
2627
			$total_ttc_by_vats = array();
2628
			$this->multicurrency_total_ht	= 0;
2629
			$this->multicurrency_total_tva	= 0;
2630
			$this->multicurrency_total_ttc	= 0;
2631
2632
			$num = $this->db->num_rows($resql);
2633
			$i = 0;
2634
			while ($i < $num)
2635
			{
2636
				$obj = $this->db->fetch_object($resql);
2637
2638
				// Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
2639
				if ($forcedroundingmode == '0')	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode='auto'
2640
				{
2641
					$localtax_array=array($obj->localtax1_type,$obj->localtax1_tx,$obj->localtax2_type,$obj->localtax2_tx);
2642
					$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);
0 ignored issues
show
Bug introduced by
It seems like $seller defined by parameter $seller on line 2546 can be null; however, calcul_price_total() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
2643
					$diff=price2num($tmpcal[1] - $obj->total_tva, 'MT', 1);
2644
					if ($diff)
2645
					{
2646
						$sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid;
2647
						dol_syslog('We found unconsistent data into detailed line (difference of '.$diff.') 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);
2648
								$resqlfix=$this->db->query($sqlfix);
2649
								if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
2650
								$obj->total_tva = $tmpcal[1];
2651
								$obj->total_ttc = $tmpcal[2];
2652
						//
2653
					}
2654
				}
2655
2656
				$this->total_ht        += $obj->total_ht;		// The field visible at end of line detail
2657
				$this->total_tva       += $obj->total_tva;
2658
				$this->total_localtax1 += $obj->total_localtax1;
2659
				$this->total_localtax2 += $obj->total_localtax2;
2660
				$this->total_ttc       += $obj->total_ttc;
2661
				$this->multicurrency_total_ht        += $obj->multicurrency_total_ht;		// The field visible at end of line detail
2662
				$this->multicurrency_total_tva       += $obj->multicurrency_total_tva;
2663
				$this->multicurrency_total_ttc       += $obj->multicurrency_total_ttc;
2664
2665
				if (! isset($total_ht_by_vats[$obj->vatrate]))  $total_ht_by_vats[$obj->vatrate]=0;
2666
				if (! isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate]=0;
2667
				if (! isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate]=0;
2668
				$total_ht_by_vats[$obj->vatrate]  += $obj->total_ht;
2669
				$total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
2670
				$total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
2671
2672
				if ($forcedroundingmode == '1')	// Check if we need adjustement onto line for vat. TODO This works on the company currency but not on multicurrency
2673
				{
2674
					$tmpvat=price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
2675
					$diff=price2num($total_tva_by_vats[$obj->vatrate]-$tmpvat, 'MT', 1);
2676
					//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";
2677
					if ($diff)
2678
					{
2679
						if (abs($diff) > 0.1) { dol_syslog('A rounding difference was detected into TOTAL but is too high to be corrected', LOG_WARNING); exit; }
2680
						$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;
2681
						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);
2682
								$resqlfix=$this->db->query($sqlfix);
2683
								if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
2684
								$this->total_tva -= $diff;
2685
								$this->total_ttc -= $diff;
2686
								$total_tva_by_vats[$obj->vatrate] -= $diff;
2687
								$total_ttc_by_vats[$obj->vatrate] -= $diff;
2688
2689
					}
2690
				}
2691
2692
				$i++;
2693
			}
2694
2695
			// Add revenue stamp to total
2696
			$this->total_ttc       			+= isset($this->revenuestamp)?$this->revenuestamp:0;
2697
			$this->multicurrency_total_ttc  += isset($this->revenuestamp)?($this->revenuestamp * $multicurrency_tx):0;
2698
2699
			// Situations totals
2700
			if ($this->situation_cycle_ref && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE )
2701
			{
2702
				$prev_sits = $this->get_prev_sits();
2703
2704
				foreach ($prev_sits as $sit) {				// $sit is an object Facture loaded with a fetch.
2705
					$this->total_ht -= $sit->total_ht;
2706
					$this->total_tva -= $sit->total_tva;
2707
					$this->total_localtax1 -= $sit->total_localtax1;
2708
					$this->total_localtax2 -= $sit->total_localtax2;
2709
					$this->total_ttc -= $sit->total_ttc;
2710
					$this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
2711
					$this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
2712
					$this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
2713
				}
2714
			}
2715
2716
			$this->db->free($resql);
2717
2718
			// Now update global field total_ht, total_ttc and tva
2719
			$fieldht='total_ht';
2720
			$fieldtva='tva';
2721
			$fieldlocaltax1='localtax1';
2722
			$fieldlocaltax2='localtax2';
2723
			$fieldttc='total_ttc';
2724
			// Specific code for backward compatibility with old field names
2725
			if ($this->element == 'facture' || $this->element == 'facturerec')             $fieldht='total';
2726
			if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='total_tva';
2727
			if ($this->element == 'propal')                                                $fieldttc='total';
2728
			if ($this->element == 'expensereport')                                         $fieldtva='total_tva';
2729
			if ($this->element == 'supplier_proposal')                                     $fieldttc='total';
2730
2731
			if (empty($nodatabaseupdate))
2732
			{
2733
				$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2734
				$sql .= " ".$fieldht."='".price2num($this->total_ht)."',";
2735
				$sql .= " ".$fieldtva."='".price2num($this->total_tva)."',";
2736
				$sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',";
2737
				$sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',";
2738
				$sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'";
2739
						$sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'";
2740
						$sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'";
2741
						$sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'";
2742
				$sql .= ' WHERE rowid = '.$this->id;
2743
2744
2745
				dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2746
				$resql=$this->db->query($sql);
2747
				if (! $resql)
2748
				{
2749
					$error++;
2750
					$this->error=$this->db->lasterror();
2751
					$this->errors[]=$this->db->lasterror();
2752
				}
2753
			}
2754
2755
			if (! $error)
2756
			{
2757
				return 1;
2758
			}
2759
			else
2760
			{
2761
				return -1;
2762
			}
2763
		}
2764
		else
2765
		{
2766
			dol_print_error($this->db,'Bad request in update_price');
2767
			return -1;
2768
		}
2769
	}
2770
2771
	/**
2772
	 *	Add objects linked in llx_element_element.
2773
	 *
2774
	 *	@param		string	$origin		Linked element type
2775
	 *	@param		int		$origin_id	Linked element id
2776
	 *	@return		int					<=0 if KO, >0 if OK
2777
	 *	@see		fetchObjectLinked, updateObjectLinked, deleteObjectLinked
2778
	 */
2779
	function add_object_linked($origin=null, $origin_id=null)
2780
	{
2781
		$origin = (! empty($origin) ? $origin : $this->origin);
2782
		$origin_id = (! empty($origin_id) ? $origin_id : $this->origin_id);
2783
2784
		// Special case
2785
		if ($origin == 'order') $origin='commande';
2786
		if ($origin == 'invoice') $origin='facture';
2787
2788
		$this->db->begin();
2789
2790
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
2791
		$sql.= "fk_source";
2792
		$sql.= ", sourcetype";
2793
		$sql.= ", fk_target";
2794
		$sql.= ", targettype";
2795
		$sql.= ") VALUES (";
2796
		$sql.= $origin_id;
2797
		$sql.= ", '".$this->db->escape($origin)."'";
2798
		$sql.= ", ".$this->id;
2799
		$sql.= ", '".$this->db->escape($this->element)."'";
2800
		$sql.= ")";
2801
2802
		dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG);
2803
		if ($this->db->query($sql))
2804
	  	{
2805
	  		$this->db->commit();
2806
	  		return 1;
2807
	  	}
2808
	  	else
2809
	  	{
2810
	  		$this->error=$this->db->lasterror();
2811
	  		$this->db->rollback();
2812
	  		return 0;
2813
	  	}
2814
	}
2815
2816
	/**
2817
	 *	Fetch array of objects linked to current object. Links are loaded into this->linkedObjects array and this->linkedObjectsIds
2818
	 *  Possible usage for parameters:
2819
	 *  - all parameters empty -> we look all link to current object (current object can be source or target)
2820
	 *  - source id+type -> will get target list linked to source
2821
	 *  - target id+type -> will get source list linked to target
2822
	 *  - source id+type + target type -> will get target list of the type
2823
	 *  - target id+type + target source -> will get source list of the type
2824
	 *
2825
	 *	@param	int		$sourceid		Object source id (if not defined, id of object)
2826
	 *	@param  string	$sourcetype		Object source type (if not defined, element name of object)
2827
	 *	@param  int		$targetid		Object target id (if not defined, id of object)
2828
	 *	@param  string	$targettype		Object target type (if not defined, elemennt name of object)
2829
	 *	@param  string	$clause			'OR' or 'AND' clause used when both source id and target id are provided
2830
	 *  @param  int		$alsosametype	0=Return only links to object that differs from source. 1=Include also link to objects of same type.
2831
	 *  @param  string	$orderby		SQL 'ORDER BY' clause
2832
	 *	@return int						<0 if KO, >0 if OK
2833
	 *  @see	add_object_linked, updateObjectLinked, deleteObjectLinked
2834
	 */
2835
	function fetchObjectLinked($sourceid=null,$sourcetype='',$targetid=null,$targettype='',$clause='OR',$alsosametype=1,$orderby='sourcetype')
2836
	{
2837
		global $conf;
2838
2839
		$this->linkedObjectsIds=array();
2840
		$this->linkedObjects=array();
2841
2842
		$justsource=false;
2843
		$justtarget=false;
2844
		$withtargettype=false;
2845
		$withsourcetype=false;
2846
2847
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid))
2848
		{
2849
			$justsource=true;  // the source (id and type) is a search criteria
2850
			if (! empty($targettype)) $withtargettype=true;
2851
		}
2852
		if (! empty($targetid) && ! empty($targettype) && empty($sourceid))
2853
		{
2854
			$justtarget=true;  // the target (id and type) is a search criteria
2855
			if (! empty($sourcetype)) $withsourcetype=true;
2856
		}
2857
2858
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
2859
		$targetid = (! empty($targetid) ? $targetid : $this->id);
2860
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
2861
		$targettype = (! empty($targettype) ? $targettype : $this->element);
2862
2863
		/*if (empty($sourceid) && empty($targetid))
2864
        {
2865
        	dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
2866
        	return -1;
2867
        }*/
2868
2869
		// Links between objects are stored in table element_element
2870
		$sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype';
2871
		$sql.= ' FROM '.MAIN_DB_PREFIX.'element_element';
2872
		$sql.= " WHERE ";
2873
		if ($justsource || $justtarget)
2874
		{
2875
			if ($justsource)
2876
			{
2877
				$sql.= "fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."'";
2878
				if ($withtargettype) $sql.= " AND targettype = '".$targettype."'";
2879
			}
2880
			else if ($justtarget)
2881
			{
2882
				$sql.= "fk_target = ".$targetid." AND targettype = '".$targettype."'";
2883
				if ($withsourcetype) $sql.= " AND sourcetype = '".$sourcetype."'";
2884
			}
2885
		}
2886
		else
2887
		{
2888
			$sql.= "(fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."')";
2889
			$sql.= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$targettype."')";
2890
		}
2891
		$sql .= ' ORDER BY '.$orderby;
2892
2893
		dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
2894
		$resql = $this->db->query($sql);
2895
		if ($resql)
2896
		{
2897
			$num = $this->db->num_rows($resql);
2898
			$i = 0;
2899
			while ($i < $num)
2900
			{
2901
				$obj = $this->db->fetch_object($resql);
2902
				if ($justsource || $justtarget)
2903
				{
2904
					if ($justsource)
2905
					{
2906
						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
2907
					}
2908
					else if ($justtarget)
2909
					{
2910
						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
2911
					}
2912
				}
2913
				else
2914
				{
2915
					if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype)
2916
					{
2917
						$this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
2918
					}
2919
					if ($obj->fk_target == $targetid && $obj->targettype == $targettype)
2920
					{
2921
						$this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
2922
					}
2923
				}
2924
				$i++;
2925
			}
2926
2927
			if (! empty($this->linkedObjectsIds))
2928
			{
2929
				foreach($this->linkedObjectsIds as $objecttype => $objectids)       // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
2930
				{
2931
					// Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
2932
					$module = $element = $subelement = $objecttype;
2933
					if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
2934
						&& preg_match('/^([^_]+)_([^_]+)/i',$objecttype,$regs))
2935
					{
2936
						$module = $element = $regs[1];
2937
						$subelement = $regs[2];
2938
					}
2939
2940
					$classpath = $element.'/class';
2941
					// To work with non standard classpath or module name
2942
					if ($objecttype == 'facture')			{
2943
						$classpath = 'compta/facture/class';
2944
					}
2945
					else if ($objecttype == 'facturerec')			{
2946
						$classpath = 'compta/facture/class'; $module = 'facture';
2947
					}
2948
					else if ($objecttype == 'propal')			{
2949
						$classpath = 'comm/propal/class';
2950
					}
2951
					else if ($objecttype == 'supplier_proposal')			{
2952
						$classpath = 'supplier_proposal/class';
2953
					}
2954
					else if ($objecttype == 'shipping')			{
2955
						$classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon';
2956
					}
2957
					else if ($objecttype == 'delivery')			{
2958
						$classpath = 'livraison/class'; $subelement = 'livraison'; $module = 'livraison_bon';
2959
					}
2960
					else if ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier')	{
2961
						$classpath = 'fourn/class'; $module = 'fournisseur';
2962
					}
2963
					else if ($objecttype == 'fichinter')			{
2964
						$classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter';
2965
					}
2966
					else if ($objecttype == 'subscription')			{
2967
						$classpath = 'adherents/class'; $module = 'adherent';
2968
					}
2969
2970
					// Set classfile
2971
					$classfile = strtolower($subelement); $classname = ucfirst($subelement);
2972
2973
					if ($objecttype == 'order') {
2974
						$classfile = 'commande'; $classname = 'Commande';
2975
					}
2976
					else if ($objecttype == 'invoice_supplier') {
2977
						$classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur';
2978
					}
2979
					else if ($objecttype == 'order_supplier')   {
2980
						$classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur';
2981
					}
2982
					else if ($objecttype == 'supplier_proposal')   {
2983
						$classfile = 'supplier_proposal'; $classname = 'SupplierProposal';
2984
					}
2985
					else if ($objecttype == 'facturerec')   {
2986
						$classfile = 'facture-rec'; $classname = 'FactureRec';
2987
					}
2988
					else if ($objecttype == 'subscription')   {
2989
						$classfile = 'subscription'; $classname = 'Subscription';
2990
					}
2991
2992
					// Here $module, $classfile and $classname are set
2993
					if ($conf->$module->enabled && (($element != $this->element) || $alsosametype))
2994
					{
2995
						dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
2996
						//print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
2997
						if (class_exists($classname))
2998
						{
2999
							foreach($objectids as $i => $objectid)	// $i is rowid into llx_element_element
3000
							{
3001
								$object = new $classname($this->db);
3002
								$ret = $object->fetch($objectid);
3003
								if ($ret >= 0)
3004
								{
3005
									$this->linkedObjects[$objecttype][$i] = $object;
3006
								}
3007
							}
3008
						}
3009
					}
3010
				}
3011
			}
3012
			return 1;
3013
		}
3014
		else
3015
		{
3016
			dol_print_error($this->db);
3017
			return -1;
3018
		}
3019
	}
3020
3021
	/**
3022
	 *	Update object linked of a current object
3023
	 *
3024
	 *	@param	int		$sourceid		Object source id
3025
	 *	@param  string	$sourcetype		Object source type
3026
	 *	@param  int		$targetid		Object target id
3027
	 *	@param  string	$targettype		Object target type
3028
	 *	@return							int	>0 if OK, <0 if KO
3029
	 *	@see	add_object_linked, fetObjectLinked, deleteObjectLinked
3030
	 */
3031
	function updateObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='')
3032
	{
3033
		$updatesource=false;
3034
		$updatetarget=false;
3035
3036
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource=true;
3037
		else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $updatetarget=true;
3038
3039
		$sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET ";
3040
		if ($updatesource)
3041
		{
3042
			$sql.= "fk_source = ".$sourceid;
3043
			$sql.= ", sourcetype = '".$this->db->escape($sourcetype)."'";
3044
			$sql.= " WHERE fk_target = ".$this->id;
3045
			$sql.= " AND targettype = '".$this->db->escape($this->element)."'";
3046
		}
3047
		else if ($updatetarget)
3048
		{
3049
			$sql.= "fk_target = ".$targetid;
3050
			$sql.= ", targettype = '".$this->db->escape($targettype)."'";
3051
			$sql.= " WHERE fk_source = ".$this->id;
3052
			$sql.= " AND sourcetype = '".$this->db->escape($this->element)."'";
3053
		}
3054
3055
		dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG);
3056
		if ($this->db->query($sql))
3057
		{
3058
			return 1;
3059
		}
3060
		else
3061
		{
3062
			$this->error=$this->db->lasterror();
3063
			return -1;
3064
		}
3065
	}
3066
3067
	/**
3068
	 *	Delete all links between an object $this
3069
	 *
3070
	 *	@param	int		$sourceid		Object source id
3071
	 *	@param  string	$sourcetype		Object source type
3072
	 *	@param  int		$targetid		Object target id
3073
	 *	@param  string	$targettype		Object target type
3074
	 *  @param	int		$rowid			Row id of line to delete. If defined, other parameters are not used.
3075
	 *	@return     					int	>0 if OK, <0 if KO
3076
	 *	@see	add_object_linked, updateObjectLinked, fetchObjectLinked
3077
	 */
3078
	function deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='')
3079
	{
3080
		$deletesource=false;
3081
		$deletetarget=false;
3082
3083
		if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource=true;
3084
		else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $deletetarget=true;
3085
3086
		$sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3087
		$sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3088
		$targetid = (! empty($targetid) ? $targetid : $this->id);
3089
		$targettype = (! empty($targettype) ? $targettype : $this->element);
3090
3091
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element";
3092
		$sql.= " WHERE";
3093
		if ($rowid > 0)
3094
		{
3095
			$sql.=" rowid = ".$rowid;
3096
		}
3097
		else
3098
		{
3099
			if ($deletesource)
3100
			{
3101
				$sql.= " fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3102
				$sql.= " AND fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."'";
3103
			}
3104
			else if ($deletetarget)
3105
			{
3106
				$sql.= " fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'";
3107
				$sql.= " AND fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."'";
3108
			}
3109
			else
3110
			{
3111
				$sql.= " (fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."')";
3112
				$sql.= " OR";
3113
				$sql.= " (fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."')";
3114
			}
3115
		}
3116
3117
		dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG);
3118
		if ($this->db->query($sql))
3119
		{
3120
			return 1;
3121
		}
3122
		else
3123
		{
3124
			$this->error=$this->db->lasterror();
3125
			$this->errors[]=$this->error;
3126
			return -1;
3127
		}
3128
	}
3129
3130
	/**
3131
	 *      Set status of an object
3132
	 *
3133
	 *      @param	int		$status			Status to set
3134
	 *      @param	int		$elementId		Id of element to force (use this->id by default)
3135
	 *      @param	string	$elementType	Type of element to force (use this->table_element by default)
3136
	 *      @param	string	$trigkey		Trigger key to use for trigger
3137
	 *      @return int						<0 if KO, >0 if OK
3138
	 */
3139
	function setStatut($status, $elementId=null, $elementType='', $trigkey='')
3140
	{
3141
		global $user,$langs,$conf;
3142
3143
		$savElementId=$elementId;  // To be used later to know if we were using the method using the id of this or not.
3144
3145
		$elementId = (!empty($elementId)?$elementId:$this->id);
3146
		$elementTable = (!empty($elementType)?$elementType:$this->table_element);
3147
3148
		$this->db->begin();
3149
3150
		$fieldstatus="fk_statut";
3151
		if ($elementTable == 'facture_rec') $fieldstatus="suspended";
3152
		if ($elementTable == 'mailing') $fieldstatus="statut";
3153
		if ($elementTable == 'cronjob') $fieldstatus="status";
3154
		if ($elementTable == 'user') $fieldstatus="statut";
3155
		if ($elementTable == 'expensereport') $fieldstatus="fk_statut";
3156
		if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status";
3157
3158
		$sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable;
3159
		$sql.= " SET ".$fieldstatus." = ".$status;
3160
		// If status = 1 = validated, update also fk_user_valid
3161
		if ($status == 1 && $elementTable == 'expensereport') $sql.=", fk_user_valid = ".$user->id;
3162
		$sql.= " WHERE rowid=".$elementId;
3163
3164
		dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
3165
		if ($this->db->query($sql))
3166
		{
3167
			$error = 0;
3168
3169
			// Try autoset of trigkey
3170
			if (empty($trigkey))
3171
			{
3172
				if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_SIGN';   // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
3173
				if ($this->element == 'supplier_proposal' && $status == 3) $trigkey='SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
3174
				if ($this->element == 'supplier_proposal' && $status == 4) $trigkey='SUPPLIER_PROPOSAL_CLOSE';  // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
3175
				if ($this->element == 'fichinter' && $status == 3) $trigkey='FICHINTER_CLASSIFY_DONE';
3176
				if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED';
3177
				if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED';
3178
			}
3179
3180
			if ($trigkey)
3181
			{
3182
				// Appel des triggers
3183
				include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
3184
				$interface=new Interfaces($this->db);
3185
				$result=$interface->run_triggers($trigkey,$this,$user,$langs,$conf);
3186
				if ($result < 0) {
3187
					$error++; $this->errors=$interface->errors;
3188
				}
3189
				// Fin appel triggers
3190
			}
3191
3192
			if (! $error)
3193
			{
3194
				$this->db->commit();
3195
3196
				if (empty($savElementId))    // If the element we update was $this (so $elementId is null)
3197
				{
3198
					$this->statut = $status;
3199
					$this->status = $status;
3200
				}
3201
3202
				return 1;
3203
			}
3204
			else
3205
			{
3206
				$this->db->rollback();
3207
				dol_syslog(get_class($this)."::setStatus ".$this->error,LOG_ERR);
3208
				return -1;
3209
			}
3210
		}
3211
		else
3212
		{
3213
			$this->error=$this->db->lasterror();
3214
			$this->db->rollback();
3215
			return -1;
3216
		}
3217
	}
3218
3219
3220
	/**
3221
	 *  Load type of canvas of an object if it exists
3222
	 *
3223
	 *  @param      int		$id     Record id
3224
	 *  @param      string	$ref    Record ref
3225
	 *  @return		int				<0 if KO, 0 if nothing done, >0 if OK
3226
	 */
3227
	function getCanvas($id=0,$ref='')
3228
	{
3229
		global $conf;
3230
3231
		if (empty($id) && empty($ref)) return 0;
3232
		if (! empty($conf->global->MAIN_DISABLE_CANVAS)) return 0;    // To increase speed. Not enabled by default.
3233
3234
		// Clean parameters
3235
		$ref = trim($ref);
3236
3237
		$sql = "SELECT rowid, canvas";
3238
		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
3239
		$sql.= " WHERE entity IN (".getEntity($this->element).")";
3240
		if (! empty($id))  $sql.= " AND rowid = ".$id;
3241
		if (! empty($ref)) $sql.= " AND ref = '".$this->db->escape($ref)."'";
3242
3243
		$resql = $this->db->query($sql);
3244
		if ($resql)
3245
		{
3246
			$obj = $this->db->fetch_object($resql);
3247
			if ($obj)
3248
			{
3249
				$this->canvas   = $obj->canvas;
3250
				return 1;
3251
			}
3252
			else return 0;
3253
		}
3254
		else
3255
		{
3256
			dol_print_error($this->db);
3257
			return -1;
3258
		}
3259
	}
3260
3261
3262
	/**
3263
	 * 	Get special code of a line
3264
	 *
3265
	 * 	@param	int		$lineid		Id of line
3266
	 * 	@return	int					Special code
3267
	 */
3268
	function getSpecialCode($lineid)
3269
	{
3270
		$sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line;
3271
		$sql.= ' WHERE rowid = '.$lineid;
3272
		$resql = $this->db->query($sql);
3273
		if ($resql)
3274
		{
3275
			$row = $this->db->fetch_row($resql);
3276
			return $row[0];
3277
		}
3278
	}
3279
3280
	/**
3281
	 *  Function to check if an object is used by others.
3282
	 *  Check is done into this->childtables. There is no check into llx_element_element.
3283
	 *
3284
	 *  @param	int		$id			Force id of object
3285
	 *  @return	int					<0 if KO, 0 if not used, >0 if already used
3286
	 */
3287
	function isObjectUsed($id=0)
3288
	{
3289
		global $langs;
3290
3291
		if (empty($id)) $id=$this->id;
3292
3293
		// Check parameters
3294
		if (! isset($this->childtables) || ! is_array($this->childtables) || count($this->childtables) == 0)
3295
		{
3296
			dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
3297
			return -1;
3298
		}
3299
3300
		$arraytoscan = $this->childtables;
3301
		// For backward compatibility, we check if array is old format array('table1', 'table2', ...)
3302
		$tmparray=array_keys($this->childtables);
3303
		if (is_numeric($tmparray[0]))
3304
		{
3305
			$arraytoscan = array_flip($this->childtables);
3306
		}
3307
3308
		// Test if child exists
3309
		$haschild=0;
3310
		foreach($arraytoscan as $table => $elementname)
3311
		{
3312
			//print $id.'-'.$table.'-'.$elementname.'<br>';
3313
			// Check if third party can be deleted
3314
			$sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table;
3315
			$sql.= " WHERE ".$this->fk_element." = ".$id;
3316
			$resql=$this->db->query($sql);
3317
			if ($resql)
3318
			{
3319
				$obj=$this->db->fetch_object($resql);
3320
				if ($obj->nb > 0)
3321
				{
3322
					$langs->load("errors");
3323
					//print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
3324
					$haschild += $obj->nb;
3325
					if (is_numeric($elementname))	// old usage
3326
					{
3327
						$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $table);
3328
					}
3329
					else	// new usage: $elementname=Translation key
3330
					{
3331
						$this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $langs->transnoentitiesnoconv($elementname));
3332
					}
3333
					break;    // We found at least one, we stop here
3334
				}
3335
			}
3336
			else
3337
			{
3338
				$this->errors[]=$this->db->lasterror();
3339
				return -1;
3340
			}
3341
		}
3342
		if ($haschild > 0)
3343
		{
3344
			$this->errors[]="ErrorRecordHasChildren";
3345
			return $haschild;
3346
		}
3347
		else return 0;
3348
	}
3349
3350
	/**
3351
	 *  Function to say how many lines object contains
3352
	 *
3353
	 *	@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
3354
	 *  @return	int						<0 if KO, 0 if no predefined products, nb of lines with predefined products if found
3355
	 */
3356
	function hasProductsOrServices($predefined=-1)
3357
	{
3358
		$nb=0;
3359
3360
		foreach($this->lines as $key => $val)
3361
		{
3362
			$qualified=0;
3363
			if ($predefined == -1) $qualified=1;
3364
			if ($predefined == 1 && $val->fk_product > 0) $qualified=1;
3365
			if ($predefined == 0 && $val->fk_product <= 0) $qualified=1;
3366
			if ($predefined == 2 && $val->fk_product > 0 && $val->product_type==0) $qualified=1;
3367
			if ($predefined == 3 && $val->fk_product > 0 && $val->product_type==1) $qualified=1;
3368
			if ($qualified) $nb++;
3369
		}
3370
		dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
3371
		return $nb;
3372
	}
3373
3374
	/**
3375
	 * Function that returns the total amount HT of discounts applied for all lines.
3376
	 *
3377
	 * @return 	float
3378
	 */
3379
	function getTotalDiscount()
3380
	{
3381
		$total_discount=0.00;
3382
3383
		$sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
3384
		$sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element."det";
3385
		$sql.= " WHERE ".$this->fk_element." = ".$this->id;
3386
3387
		dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
3388
		$resql = $this->db->query($sql);
3389
		if ($resql)
3390
		{
3391
			$num=$this->db->num_rows($resql);
3392
			$i=0;
3393
			while ($i < $num)
3394
			{
3395
				$obj = $this->db->fetch_object($resql);
3396
3397
				$pu_ht = $obj->pu_ht;
3398
				$qty= $obj->qty;
3399
				$total_ht = $obj->total_ht;
3400
3401
				$total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
3402
				$total_discount += $total_discount_line;
3403
3404
				$i++;
3405
			}
3406
		}
3407
3408
		//print $total_discount; exit;
3409
		return price2num($total_discount);
3410
	}
3411
3412
3413
	/**
3414
	 * Return into unit=0, the calculated total of weight and volume of all lines * qty
3415
	 * Calculate by adding weight and volume of each product line, so properties ->volume/volume_units/weight/weight_units must be loaded on line.
3416
	 *
3417
	 * @return  array                           array('weight'=>...,'volume'=>...)
3418
	 */
3419
	function getTotalWeightVolume()
3420
	{
3421
		$totalWeight = 0;
3422
		$totalVolume = 0;
3423
		// defined for shipment only
3424
		$totalOrdered = '';
3425
		// defined for shipment only
3426
		$totalToShip = '';
3427
3428
		foreach ($this->lines as $line)
3429
		{
3430
			if (isset($line->qty_asked))
3431
			{
3432
				if (empty($totalOrdered)) $totalOrdered=0;  // Avoid warning because $totalOrdered is ''
3433
				$totalOrdered+=$line->qty_asked;    // defined for shipment only
3434
			}
3435
			if (isset($line->qty_shipped))
3436
			{
3437
				if (empty($totalToShip)) $totalToShip=0;    // Avoid warning because $totalToShip is ''
3438
				$totalToShip+=$line->qty_shipped;   // defined for shipment only
3439
			}
3440
3441
			// Define qty, weight, volume, weight_units, volume_units
3442
			if ($this->element == 'shipping') {
3443
				// for shipments
3444
				$qty = $line->qty_shipped ? $line->qty_shipped : 0;
3445
			}
3446
			else {
3447
				$qty = $line->qty ? $line->qty : 0;
3448
			}
3449
3450
			$weight = $line->weight ? $line->weight : 0;
3451
			$volume = $line->volume ? $line->volume : 0;
3452
3453
			$weight_units=$line->weight_units;
3454
			$volume_units=$line->volume_units;
3455
3456
			$weightUnit=0;
3457
			$volumeUnit=0;
3458
			if (! empty($weight_units)) $weightUnit = $weight_units;
3459
			if (! empty($volume_units)) $volumeUnit = $volume_units;
3460
3461
			if (empty($totalWeight)) $totalWeight=0;  // Avoid warning because $totalWeight is ''
3462
			if (empty($totalVolume)) $totalVolume=0;  // Avoid warning because $totalVolume is ''
3463
3464
			//var_dump($line->volume_units);
3465
			if ($weight_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3466
			{
3467
				$trueWeightUnit=pow(10, $weightUnit);
3468
				$totalWeight += $weight * $qty * $trueWeightUnit;
3469
			}
3470
			else {
3471
		if ($weight_units == 99) {
3472
			// conversion 1 Pound = 0.45359237 KG
3473
			$trueWeightUnit = 0.45359237;
3474
			$totalWeight += $weight * $qty * $trueWeightUnit;
3475
		} elseif ($weight_units == 98) {
3476
			// conversion 1 Ounce = 0.0283495 KG
3477
			$trueWeightUnit = 0.0283495;
3478
			$totalWeight += $weight * $qty * $trueWeightUnit;
3479
		}
3480
		else
3481
					$totalWeight += $weight * $qty;   // This may be wrong if we mix different units
3482
			}
3483
			if ($volume_units < 50)   // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3484
			{
3485
				//print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
3486
				$trueVolumeUnit=pow(10, $volumeUnit);
3487
				//print $line->volume;
3488
				$totalVolume += $volume * $qty * $trueVolumeUnit;
3489
			}
3490
			else
3491
			{
3492
				$totalVolume += $volume * $qty;   // This may be wrong if we mix different units
3493
			}
3494
		}
3495
3496
		return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
3497
	}
3498
3499
3500
	/**
3501
	 *	Set extra parameters
3502
	 *
3503
	 *	@return	int      <0 if KO, >0 if OK
3504
	 */
3505
	function setExtraParameters()
3506
	{
3507
		$this->db->begin();
3508
3509
		$extraparams = (! empty($this->extraparams) ? json_encode($this->extraparams) : null);
3510
3511
		$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3512
		$sql.= " SET extraparams = ".(! empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
3513
		$sql.= " WHERE rowid = ".$this->id;
3514
3515
		dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
3516
		$resql = $this->db->query($sql);
3517
		if (! $resql)
3518
		{
3519
			$this->error=$this->db->lasterror();
3520
			$this->db->rollback();
3521
			return -1;
3522
		}
3523
		else
3524
		{
3525
			$this->db->commit();
3526
			return 1;
3527
		}
3528
	}
3529
3530
3531
	/**
3532
	 *    Return incoterms informations
3533
	 *    TODO Use a cache for label get
3534
	 *
3535
	 *    @return	string	incoterms info
3536
	 */
3537
	function display_incoterms()
3538
	{
3539
		$out = '';
3540
		$this->libelle_incoterms = '';
3541
		if (!empty($this->fk_incoterms))
3542
		{
3543
			$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3544
			$result = $this->db->query($sql);
3545
			if ($result)
3546
			{
3547
				$res = $this->db->fetch_object($result);
3548
				$out .= $res->code;
3549
			}
3550
		}
3551
3552
		$out .= (($res->code && $this->location_incoterms)?' - ':'').$this->location_incoterms;
0 ignored issues
show
Bug introduced by
The variable $res does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3553
3554
		return $out;
3555
	}
3556
3557
	/**
3558
	 *    Return incoterms informations for pdf display
3559
	 *
3560
	 *    @return	string		incoterms info
3561
	 */
3562
	function getIncotermsForPDF()
3563
	{
3564
		$sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3565
		$resql = $this->db->query($sql);
3566
		if ($resql)
3567
		{
3568
			$num = $this->db->num_rows($resql);
3569
			if ($num > 0)
3570
			{
3571
				$res = $this->db->fetch_object($resql);
3572
				return 'Incoterm : '.$res->code.' - '.$this->location_incoterms;
3573
			}
3574
			else
3575
			{
3576
				return '';
3577
			}
3578
		}
3579
		else
3580
		{
3581
			$this->errors[] = $this->db->lasterror();
3582
			return false;
3583
		}
3584
	}
3585
3586
	/**
3587
	 *    Define incoterms values of current object
3588
	 *
3589
	 *    @param	int		$id_incoterm     Id of incoterm to set or '' to remove
3590
	 * 	  @param 	string  $location		 location of incoterm
3591
	 *    @return	int     		<0 if KO, >0 if OK
3592
	 */
3593
	function setIncoterms($id_incoterm, $location)
3594
	{
3595
		if ($this->id && $this->table_element)
3596
		{
3597
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3598
			$sql.= " SET fk_incoterms = ".($id_incoterm > 0 ? $id_incoterm : "null");
3599
			$sql.= ", location_incoterms = ".($id_incoterm > 0 ? "'".$this->db->escape($location)."'" : "null");
3600
			$sql.= " WHERE rowid = " . $this->id;
3601
			dol_syslog(get_class($this).'::setIncoterms', LOG_DEBUG);
3602
			$resql=$this->db->query($sql);
3603
			if ($resql)
3604
			{
3605
				$this->fk_incoterms = $id_incoterm;
3606
				$this->location_incoterms = $location;
3607
3608
				$sql = 'SELECT libelle FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3609
				$res = $this->db->query($sql);
3610
				if ($res)
3611
				{
3612
					$obj = $this->db->fetch_object($res);
3613
					$this->libelle_incoterms = $obj->libelle;
3614
				}
3615
				return 1;
3616
			}
3617
			else
3618
			{
3619
				$this->errors[] = $this->db->lasterror();
3620
				return -1;
3621
			}
3622
		}
3623
		else return -1;
3624
	}
3625
3626
3627
	// --------------------
3628
	// TODO: All functions here must be redesigned and moved as they are not business functions but output functions
3629
	// --------------------
3630
3631
	/* This is to show add lines */
3632
3633
	/**
3634
	 *	Show add free and predefined products/services form
3635
	 *
3636
	 *  @param	int		        $dateSelector       1=Show also date range input fields
3637
	 *  @param	Societe			$seller				Object thirdparty who sell
3638
	 *  @param	Societe			$buyer				Object thirdparty who buy
3639
	 *	@return	void
3640
	 */
3641
	function formAddObjectLine($dateSelector, $seller, $buyer)
3642
	{
3643
		global $conf,$user,$langs,$object,$hookmanager;
3644
		global $form,$bcnd,$var;
3645
3646
		// Line extrafield
3647
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3648
		$extrafieldsline = new ExtraFields($this->db);
3649
		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3650
3651
		// Output template part (modules that overwrite templates must declare this into descriptor)
3652
		// Use global variables + $dateSelector + $seller and $buyer
3653
		$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3654
		foreach($dirtpls as $reldir)
3655
		{
3656
			$tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
3657
			if (empty($conf->file->strict_mode)) {
3658
				$res=@include $tpl;
3659
			} else {
3660
				$res=include $tpl; // for debug
3661
			}
3662
			if ($res) break;
3663
		}
3664
	}
3665
3666
3667
3668
	/* This is to show array of line of details */
3669
3670
3671
	/**
3672
	 *	Return HTML table for object lines
3673
	 *	TODO Move this into an output class file (htmlline.class.php)
3674
	 *	If lines are into a template, title must also be into a template
3675
	 *	But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
3676
	 *
3677
	 *	@param	string		$action				Action code
3678
	 *	@param  string		$seller            	Object of seller third party
3679
	 *	@param  string  	$buyer             	Object of buyer third party
3680
	 *	@param	int			$selected		   	Object line selected
3681
	 *	@param  int	    	$dateSelector      	1=Show also date range input fields
3682
	 *	@return	void
3683
	 */
3684
	function printObjectLines($action, $seller, $buyer, $selected=0, $dateSelector=0)
3685
	{
3686
		global $conf, $hookmanager, $langs, $user;
3687
		// TODO We should not use global var for this !
3688
		global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
3689
3690
		// Define usemargins
3691
		$usemargins=0;
3692
		if (! empty($conf->margin->enabled) && ! empty($this->element) && in_array($this->element,array('facture','propal','commande'))) $usemargins=1;
3693
3694
		$num = count($this->lines);
3695
3696
		//Line extrafield
3697
		require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3698
		$extrafieldsline = new ExtraFields($this->db);
3699
		$extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3700
3701
		$parameters = array('num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
0 ignored issues
show
Bug introduced by
The variable $i seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
3702
		$reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3703
		if (empty($reshook))
3704
		{
3705
		    print "<thead>\n";
3706
3707
			print '<tr class="liste_titre nodrag nodrop">';
3708
3709
			if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) print '<td class="linecolnum" align="center" width="5">&nbsp;</td>';
3710
3711
			// Description
3712
			print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
3713
3714
			if ($this->element == 'supplier_proposal' || $this->element == 'order_supplier' || $this->element == 'invoice_supplier')
3715
			{
3716
				print '<td class="linerefsupplier"><span id="title_fourn_ref">'.$langs->trans("SupplierRef").'</span></td>';
3717
			}
3718
3719
			// VAT
3720
			print '<td class="linecolvat" align="right" width="80">'.$langs->trans('VAT').'</td>';
3721
3722
			// Price HT
3723
			print '<td class="linecoluht" align="right" width="80">'.$langs->trans('PriceUHT').'</td>';
3724
3725
			// Multicurrency
3726
			if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf->currency) print '<td class="linecoluht_currency" align="right" width="80">'.$langs->trans('PriceUHTCurrency', $this->multicurrency_code).'</td>';
3727
3728
			if ($inputalsopricewithtax) print '<td align="right" width="80">'.$langs->trans('PriceUTTC').'</td>';
3729
3730
			// Qty
3731
			print '<td class="linecolqty" align="right">'.$langs->trans('Qty').'</td>';
3732
3733
			if($conf->global->PRODUCT_USE_UNITS)
3734
			{
3735
				print '<td class="linecoluseunit" align="left">'.$langs->trans('Unit').'</td>';
3736
			}
3737
3738
			// Reduction short
3739
			print '<td class="linecoldiscount" align="right">'.$langs->trans('ReductionShort').'</td>';
3740
3741
			if ($this->situation_cycle_ref) {
3742
				print '<td class="linecolcycleref" align="right">' . $langs->trans('Progress') . '</td>';
3743
			}
3744
3745
			if ($usemargins && ! empty($conf->margin->enabled) && empty($user->societe_id))
3746
			{
3747
				if (!empty($user->rights->margins->creer))
3748
				{
3749
					if ($conf->global->MARGIN_TYPE == "1")
3750
						print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('BuyingPrice').'</td>';
3751
					else
3752
						print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('CostPrice').'</td>';
3753
				}
3754
3755
				if (! empty($conf->global->DISPLAY_MARGIN_RATES) && $user->rights->margins->liretous)
3756
					print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarginRate').'</td>';
3757
				if (! empty($conf->global->DISPLAY_MARK_RATES) && $user->rights->margins->liretous)
3758
					print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarkRate').'</td>';
3759
			}
3760
3761
			// Total HT
3762
			print '<td class="linecolht" align="right">'.$langs->trans('TotalHTShort').'</td>';
3763
3764
			// Multicurrency
3765
			if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf->currency) print '<td class="linecoltotalht_currency" align="right">'.$langs->trans('TotalHTShortCurrency', $this->multicurrency_code).'</td>';
3766
3767
			if ($outputalsopricetotalwithtax) print '<td align="right" width="80">'.$langs->trans('TotalTTCShort').'</td>';
3768
3769
			print '<td class="linecoledit"></td>';  // No width to allow autodim
3770
3771
			print '<td class="linecoldelete" width="10"></td>';
3772
3773
			print '<td class="linecolmove" width="10"></td>';
3774
3775
			if($action == 'selectlines')
3776
			{
3777
			    print '<td class="linecolcheckall" align="center">';
3778
			    print '<input type="checkbox" class="linecheckboxtoggle" />';
3779
			    print '<script type="text/javascript">$(document).ready(function() {$(".linecheckboxtoggle").click(function() {var checkBoxes = $(".linecheckbox");checkBoxes.prop("checked", this.checked);})});</script>';
3780
			    print '</td>';
3781
			}
3782
3783
			print "</tr>\n";
3784
			print "</thead>\n";
3785
		}
3786
3787
		$var = true;
3788
		$i	 = 0;
3789
3790
		print "<tbody>\n";
3791
		foreach ($this->lines as $line)
3792
		{
3793
			//Line extrafield
3794
			$line->fetch_optionals();
3795
3796
3797
			//if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
3798
			if (is_object($hookmanager))   // Old code is commented on preceding line.
3799
			{
3800
				if (empty($line->fk_parent_line))
3801
				{
3802
					$parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
3803
					$reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3804
				}
3805
				else
3806
				{
3807
					$parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline, 'fk_parent_line'=>$line->fk_parent_line);
3808
					$reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
3809
				}
3810
			}
3811
			if (empty($reshook))
3812
			{
3813
				$this->printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected,$extrafieldsline);
3814
			}
3815
3816
			$i++;
3817
		}
3818
		print "</tbody>\n";
3819
	}
3820
3821
	/**
3822
	 *	Return HTML content of a detail line
3823
	 *	TODO Move this into an output class file (htmlline.class.php)
3824
	 *
3825
	 *	@param	string		$action				GET/POST action
3826
	 *	@param CommonObjectLine $line		       	Selected object line to output
3827
	 *	@param  string	    $var               	Is it a an odd line (true)
3828
	 *	@param  int		    $num               	Number of line (0)
3829
	 *	@param  int		    $i					I
3830
	 *	@param  int		    $dateSelector      	1=Show also date range input fields
3831
	 *	@param  string	    $seller            	Object of seller third party
3832
	 *	@param  string	    $buyer             	Object of buyer third party
3833
	 *	@param	int			$selected		   	Object line selected
3834
	 *  @param  int			$extrafieldsline	Object of extrafield line attribute
3835
	 *	@return	void
3836
	 */
3837
	function printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected=0,$extrafieldsline=0)
3838
	{
3839
		global $conf,$langs,$user,$object,$hookmanager;
3840
		global $form,$bc,$bcdd;
3841
		global $object_rights, $disableedit, $disablemove;   // TODO We should not use global var for this !
3842
3843
		$object_rights = $this->getRights();
3844
3845
		$element=$this->element;
3846
3847
		$text=''; $description=''; $type=0;
3848
3849
		// Show product and description
3850
		$type=(! empty($line->product_type)?$line->product_type:$line->fk_product_type);
3851
		// Try to enhance type detection using date_start and date_end for free lines where type was not saved.
3852
		if (! empty($line->date_start)) $type=1; // deprecated
3853
		if (! empty($line->date_end)) $type=1; // deprecated
3854
3855
		// Ligne en mode visu
3856
		if ($action != 'editline' || $selected != $line->id)
3857
		{
3858
			// Product
3859
			if ($line->fk_product > 0)
3860
			{
3861
				$product_static = new Product($this->db);
3862
				$product_static->fetch($line->fk_product);
3863
3864
				$product_static->ref = $line->ref; //can change ref in hook
3865
				$product_static->label = $line->label; //can change label in hook
3866
				$text=$product_static->getNomUrl(1);
3867
3868
				// Define output language and label
3869
				if (! empty($conf->global->MAIN_MULTILANGS))
3870
				{
3871
					if (! is_object($this->thirdparty))
3872
					{
3873
						dol_print_error('','Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
3874
						return;
3875
					}
3876
3877
					$prod = new Product($this->db);
3878
					$prod->fetch($line->fk_product);
3879
3880
					$outputlangs = $langs;
3881
					$newlang='';
3882
					if (empty($newlang) && GETPOST('lang_id','aZ09')) $newlang=GETPOST('lang_id','aZ09');
3883
					if (! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang)) $newlang=$this->thirdparty->default_lang;		// For language to language of customer
3884
					if (! empty($newlang))
3885
					{
3886
						$outputlangs = new Translate("",$conf);
3887
						$outputlangs->setDefaultLang($newlang);
3888
					}
3889
3890
					$label = (! empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
3891
				}
3892
				else
3893
				{
3894
					$label = $line->product_label;
3895
				}
3896
3897
				$text.= ' - '.(! empty($line->label)?$line->label:$label);
3898
				$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.
3899
			}
3900
3901
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
3902
3903
			// Output template part (modules that overwrite templates must declare this into descriptor)
3904
			// Use global variables + $dateSelector + $seller and $buyer
3905
			$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3906
			foreach($dirtpls as $reldir)
3907
			{
3908
				$tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
3909
				if (empty($conf->file->strict_mode)) {
3910
					$res=@include $tpl;
3911
				} else {
3912
					$res=include $tpl; // for debug
3913
				}
3914
				if ($res) break;
3915
			}
3916
		}
3917
3918
		// Ligne en mode update
3919
		if ($this->statut == 0 && $action == 'editline' && $selected == $line->id)
3920
		{
3921
			$label = (! empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
3922
			$placeholder=' placeholder="'.$langs->trans("Label").'"';
3923
3924
			$line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
3925
3926
			// Output template part (modules that overwrite templates must declare this into descriptor)
3927
			// Use global variables + $dateSelector + $seller and $buyer
3928
			$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3929
			foreach($dirtpls as $reldir)
3930
			{
3931
				$tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
3932
				if (empty($conf->file->strict_mode)) {
3933
					$res=@include $tpl;
3934
				} else {
3935
					$res=include $tpl; // for debug
3936
				}
3937
				if ($res) break;
3938
			}
3939
		}
3940
	}
3941
3942
3943
	/* This is to show array of line of details of source object */
3944
3945
3946
	/**
3947
	 * 	Return HTML table table of source object lines
3948
	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
3949
	 *  If lines are into a template, title must also be into a template
3950
	 *  But for the moment we don't know if it's possible, so we keep the method available on overloaded objects.
3951
	 *
3952
	 *	@param	string		$restrictlist		''=All lines, 'services'=Restrict to services only
3953
	 *  @return	void
3954
	 */
3955
	function printOriginLinesList($restrictlist='')
3956
	{
3957
		global $langs, $hookmanager, $conf;
3958
3959
		print '<tr class="liste_titre">';
3960
		print '<td>'.$langs->trans('Ref').'</td>';
3961
		print '<td>'.$langs->trans('Description').'</td>';
3962
		print '<td align="right">'.$langs->trans('VATRate').'</td>';
3963
		print '<td align="right">'.$langs->trans('PriceUHT').'</td>';
3964
		if (!empty($conf->multicurrency->enabled)) print '<td align="right">'.$langs->trans('PriceUHTCurrency').'</td>';
3965
		print '<td align="right">'.$langs->trans('Qty').'</td>';
3966
		if($conf->global->PRODUCT_USE_UNITS)
3967
		{
3968
			print '<td align="left">'.$langs->trans('Unit').'</td>';
3969
		}
3970
		print '<td align="right">'.$langs->trans('ReductionShort').'</td></tr>';
3971
3972
		$var = true;
3973
		$i	 = 0;
3974
3975
		if (! empty($this->lines))
3976
		{
3977
			foreach ($this->lines as $line)
3978
			{
3979
				if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
3980
				{
3981
					if (empty($line->fk_parent_line))
3982
					{
3983
						$parameters=array('line'=>$line,'var'=>$var,'i'=>$i);
3984
						$action='';
3985
						$hookmanager->executeHooks('printOriginObjectLine',$parameters,$this,$action);    // Note that $action and $object may have been modified by some hooks
3986
					}
3987
				}
3988
				else
3989
				{
3990
					$this->printOriginLine($line, $var, $restrictlist);
3991
				}
3992
3993
				$i++;
3994
			}
3995
		}
3996
	}
3997
3998
	/**
3999
	 * 	Return HTML with a line of table array of source object lines
4000
	 *  TODO Move this and previous function into output html class file (htmlline.class.php).
4001
	 *  If lines are into a template, title must also be into a template
4002
	 *  But for the moment we don't know if it's possible as we keep a method available on overloaded objects.
4003
	 *
4004
	 * 	@param	CommonObjectLine	$line				Line
4005
	 * 	@param	string				$var				Var
4006
	 *	@param	string				$restrictlist		''=All lines, 'services'=Restrict to services only (strike line if not)
4007
	 * 	@return	void
4008
	 */
4009
	function printOriginLine($line, $var, $restrictlist='')
4010
	{
4011
		global $langs, $conf;
4012
4013
		//var_dump($line);
4014
		if (!empty($line->date_start))
4015
		{
4016
			$date_start=$line->date_start;
4017
		}
4018
		else
4019
		{
4020
			$date_start=$line->date_debut_prevue;
4021
			if ($line->date_debut_reel) $date_start=$line->date_debut_reel;
4022
		}
4023
		if (!empty($line->date_end))
4024
		{
4025
			$date_end=$line->date_end;
4026
		}
4027
		else
4028
		{
4029
			$date_end=$line->date_fin_prevue;
4030
			if ($line->date_fin_reel) $date_end=$line->date_fin_reel;
4031
		}
4032
4033
		$this->tpl['label'] = '';
4034
		if (! empty($line->fk_parent_line)) $this->tpl['label'].= img_picto('', 'rightarrow');
4035
4036
		if (($line->info_bits & 2) == 2)  // TODO Not sure this is used for source object
4037
		{
4038
			$discount=new DiscountAbsolute($this->db);
4039
			$discount->fk_soc = $this->socid;
4040
			$this->tpl['label'].= $discount->getNomUrl(0,'discount');
4041
		}
4042
		else if (! empty($line->fk_product))
4043
		{
4044
			$productstatic = new Product($this->db);
4045
			$productstatic->id = $line->fk_product;
4046
			$productstatic->ref = $line->ref;
4047
			$productstatic->type = $line->fk_product_type;
4048
			$this->tpl['label'].= $productstatic->getNomUrl(1);
4049
			$this->tpl['label'].= ' - '.(! empty($line->label)?$line->label:$line->product_label);
4050
			// Dates
4051
			if ($line->product_type == 1 && ($date_start || $date_end))
4052
			{
4053
				$this->tpl['label'].= get_date_range($date_start,$date_end);
4054
			}
4055
		}
4056
		else
4057
		{
4058
			$this->tpl['label'].= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''),'service') : img_object($langs->trans(''),'product')));
4059
			if (!empty($line->desc)) {
4060
				$this->tpl['label'].=$line->desc;
4061
			}else {
4062
				$this->tpl['label'].= ($line->label ? '&nbsp;'.$line->label : '');
4063
			}
4064
			// Dates
4065
			if ($line->product_type == 1 && ($date_start || $date_end))
4066
			{
4067
				$this->tpl['label'].= get_date_range($date_start,$date_end);
4068
			}
4069
		}
4070
4071
		if (! empty($line->desc))
4072
		{
4073
			if ($line->desc == '(CREDIT_NOTE)')  // TODO Not sure this is used for source object
4074
			{
4075
				$discount=new DiscountAbsolute($this->db);
4076
				$discount->fetch($line->fk_remise_except);
4077
				$this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote",$discount->getNomUrl(0));
4078
			}
4079
			elseif ($line->desc == '(DEPOSIT)')  // TODO Not sure this is used for source object
4080
			{
4081
				$discount=new DiscountAbsolute($this->db);
4082
				$discount->fetch($line->fk_remise_except);
4083
				$this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit",$discount->getNomUrl(0));
4084
			}
4085
			elseif ($line->desc == '(EXCESS RECEIVED)')
4086
			{
4087
				$discount=new DiscountAbsolute($this->db);
4088
				$discount->fetch($line->fk_remise_except);
4089
				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived",$discount->getNomUrl(0));
4090
			}
4091
			elseif ($line->desc == '(EXCESS PAID)')
4092
			{
4093
				$discount=new DiscountAbsolute($this->db);
4094
				$discount->fetch($line->fk_remise_except);
4095
				$this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid",$discount->getNomUrl(0));
4096
			}
4097
			else
4098
			{
4099
				$this->tpl['description'] = dol_trunc($line->desc,60);
4100
			}
4101
		}
4102
		else
4103
		{
4104
			$this->tpl['description'] = '&nbsp;';
4105
		}
4106
4107
        // VAT Rate
4108
        $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
4109
        $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
4110
        if (! empty($line->vat_src_code) && ! preg_match('/\(/', $this->tpl['vat_rate'])) $this->tpl['vat_rate'].=' ('.$line->vat_src_code.')';
4111
4112
		$this->tpl['price'] = price($line->subprice);
4113
		$this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
4114
		$this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
4115
		if ($conf->global->PRODUCT_USE_UNITS) $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
4116
		$this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
4117
4118
		// Is the line strike or not
4119
		$this->tpl['strike']=0;
4120
		if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) $this->tpl['strike']=1;
4121
4122
		// Output template part (modules that overwrite templates must declare this into descriptor)
4123
		// Use global variables + $dateSelector + $seller and $buyer
4124
		$dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
4125
		foreach($dirtpls as $reldir)
4126
		{
4127
			$tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
4128
			if (empty($conf->file->strict_mode)) {
4129
				$res=@include $tpl;
4130
			} else {
4131
				$res=include $tpl; // for debug
4132
			}
4133
			if ($res) break;
4134
		}
4135
	}
4136
4137
4138
	/**
4139
	 *	Add resources to the current object : add entry into llx_element_resources
4140
	 *	Need $this->element & $this->id
4141
	 *
4142
	 *	@param		int		$resource_id		Resource id
4143
	 *	@param		string	$resource_type		'resource'
4144
	 *	@param		int		$busy				Busy or not
4145
	 *	@param		int		$mandatory			Mandatory or not
4146
	 *	@return		int							<=0 if KO, >0 if OK
4147
	 */
4148
	function add_element_resource($resource_id, $resource_type, $busy=0, $mandatory=0)
4149
	{
4150
		$this->db->begin();
4151
4152
		$sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources (";
4153
		$sql.= "resource_id";
4154
		$sql.= ", resource_type";
4155
		$sql.= ", element_id";
4156
		$sql.= ", element_type";
4157
		$sql.= ", busy";
4158
		$sql.= ", mandatory";
4159
		$sql.= ") VALUES (";
4160
		$sql.= $resource_id;
4161
		$sql.= ", '".$this->db->escape($resource_type)."'";
4162
		$sql.= ", '".$this->db->escape($this->id)."'";
4163
		$sql.= ", '".$this->db->escape($this->element)."'";
4164
		$sql.= ", '".$this->db->escape($busy)."'";
4165
		$sql.= ", '".$this->db->escape($mandatory)."'";
4166
		$sql.= ")";
4167
4168
		dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
4169
		if ($this->db->query($sql))
4170
		{
4171
			$this->db->commit();
4172
			return 1;
4173
		}
4174
		else
4175
		{
4176
			$this->error=$this->db->lasterror();
4177
			$this->db->rollback();
4178
			return  0;
4179
		}
4180
	}
4181
4182
	/**
4183
	 *    Delete a link to resource line
4184
	 *
4185
	 *    @param	int		$rowid			Id of resource line to delete
4186
	 *    @param	int		$element		element name (for trigger) TODO: use $this->element into commonobject class
4187
	 *    @param	int		$notrigger		Disable all triggers
4188
	 *    @return   int						>0 if OK, <0 if KO
4189
	 */
4190
	function delete_resource($rowid, $element, $notrigger=0)
4191
	{
4192
		global $user;
4193
4194
		$this->db->begin();
4195
4196
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources";
4197
		$sql.= " WHERE rowid=".$rowid;
4198
4199
		dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
4200
4201
		$resql=$this->db->query($sql);
4202
		if (! $resql)
4203
		{
4204
			$this->error=$this->db->lasterror();
4205
			$this->db->rollback();
4206
			return -1;
4207
		}
4208
		else
4209
		{
4210
			if (! $notrigger)
4211
			{
4212
				$result=$this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
4213
				if ($result < 0) { $this->db->rollback(); return -1; }
4214
			}
4215
			$this->db->commit();
4216
			return 1;
4217
		}
4218
	}
4219
4220
4221
	/**
4222
	 * Overwrite magic function to solve problem of cloning object that are kept as references
4223
	 *
4224
	 * @return void
4225
	 */
4226
	function __clone()
4227
	{
4228
		// Force a copy of this->lines, otherwise it will point to same object.
4229
		if (isset($this->lines) && is_array($this->lines))
4230
		{
4231
			$nboflines=count($this->lines);
4232
			for($i=0; $i < $nboflines; $i++)
4233
			{
4234
				$this->lines[$i] = clone $this->lines[$i];
4235
			}
4236
		}
4237
	}
4238
4239
	/**
4240
	 * Common function for all objects extending CommonObject for generating documents
4241
	 *
4242
	 * @param 	string 		$modelspath 	Relative folder where generators are placed
4243
	 * @param 	string 		$modele 		Generator to use. Caller must set it to obj->modelpdf or GETPOST('modelpdf') for example.
4244
	 * @param 	Translate 	$outputlangs 	Output language to use
4245
	 * @param 	int 		$hidedetails 	1 to hide details. 0 by default
4246
	 * @param 	int 		$hidedesc 		1 to hide product description. 0 by default
4247
	 * @param 	int 		$hideref 		1 to hide product reference. 0 by default
4248
	 * @param   null|array  $moreparams     Array to provide more information
4249
	 * @return 	int 						>0 if OK, <0 if KO
4250
	 * @see	addFileIntoDatabaseIndex
4251
	 */
4252
	protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
4253
	{
4254
		global $conf, $langs, $user;
4255
4256
		$srctemplatepath='';
4257
4258
		// Increase limit for PDF build
4259
		$err=error_reporting();
4260
		error_reporting(0);
4261
		@set_time_limit(120);
4262
		error_reporting($err);
4263
4264
		// If selected model is a filename template (then $modele="modelname" or "modelname:filename")
4265
		$tmp=explode(':',$modele,2);
4266
		if (! empty($tmp[1]))
4267
		{
4268
			$modele=$tmp[0];
4269
			$srctemplatepath=$tmp[1];
4270
		}
4271
4272
		// Search template files
4273
		$file=''; $classname=''; $filefound=0;
4274
		$dirmodels=array('/');
4275
		if (is_array($conf->modules_parts['models'])) $dirmodels=array_merge($dirmodels,$conf->modules_parts['models']);
4276
		foreach($dirmodels as $reldir)
4277
		{
4278
			foreach(array('doc','pdf') as $prefix)
4279
			{
4280
				if (in_array(get_class($this), array('Adherent'))) $file = $prefix."_".$modele.".class.php";     // Member module use prefix_module.class.php
4281
				else $file = $prefix."_".$modele.".modules.php";
4282
4283
				// On verifie l'emplacement du modele
4284
				$file=dol_buildpath($reldir.$modelspath.$file,0);
4285
				if (file_exists($file))
4286
				{
4287
					$filefound=1;
4288
					$classname=$prefix.'_'.$modele;
4289
					break;
4290
				}
4291
			}
4292
			if ($filefound) break;
4293
		}
4294
4295
		// If generator was found
4296
		if ($filefound)
4297
		{
4298
			global $db;  // Required to solve a conception default in commonstickergenerator.class.php making an include of code using $db
4299
4300
			require_once $file;
4301
4302
			$obj = new $classname($this->db);
4303
4304
			// If generator is ODT, we must have srctemplatepath defined, if not we set it.
4305
			if ($obj->type == 'odt' && empty($srctemplatepath))
4306
			{
4307
				$varfortemplatedir=$obj->scandir;
4308
				if ($varfortemplatedir && ! empty($conf->global->$varfortemplatedir))
4309
				{
4310
					$dirtoscan=$conf->global->$varfortemplatedir;
4311
4312
					$listoffiles=array();
4313
4314
					// Now we add first model found in directories scanned
4315
					$listofdir=explode(',',$dirtoscan);
4316
					foreach($listofdir as $key => $tmpdir)
4317
					{
4318
						$tmpdir=trim($tmpdir);
4319
						$tmpdir=preg_replace('/DOL_DATA_ROOT/',DOL_DATA_ROOT,$tmpdir);
4320
						if (! $tmpdir) { unset($listofdir[$key]); continue; }
4321
						if (is_dir($tmpdir))
4322
						{
4323
							$tmpfiles=dol_dir_list($tmpdir,'files',0,'\.od(s|t)$','','name',SORT_ASC,0);
4324
							if (count($tmpfiles)) $listoffiles=array_merge($listoffiles,$tmpfiles);
4325
						}
4326
					}
4327
4328
					if (count($listoffiles))
4329
					{
4330
						foreach($listoffiles as $record)
4331
						{
4332
							$srctemplatepath=$record['fullname'];
4333
							break;
4334
						}
4335
					}
4336
				}
4337
4338
				if (empty($srctemplatepath))
4339
				{
4340
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
4341
					return -1;
4342
				}
4343
			}
4344
4345
			if ($obj->type == 'odt' && ! empty($srctemplatepath))
4346
			{
4347
				if (! dol_is_file($srctemplatepath))
4348
				{
4349
					$this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
4350
					return -1;
4351
				}
4352
			}
4353
4354
			// We save charset_output to restore it because write_file can change it if needed for
4355
			// output format that does not support UTF8.
4356
			$sav_charset_output=$outputlangs->charset_output;
4357
4358
			if (in_array(get_class($this), array('Adherent')))
4359
			{
4360
				$arrayofrecords = array();   // The write_file of templates of adherent class need this var
4361
				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams);
4362
			}
4363
			else
4364
			{
4365
				$resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
4366
			}
4367
			// After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
4368
4369
			if ($resultwritefile > 0)
4370
			{
4371
				$outputlangs->charset_output=$sav_charset_output;
4372
4373
				// We delete old preview
4374
				require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4375
				dol_delete_preview($this);
4376
4377
				// Index file in database
4378
				if (! empty($obj->result['fullpath']))
4379
				{
4380
					$destfull = $obj->result['fullpath'];
4381
					$upload_dir = dirname($destfull);
4382
					$destfile = basename($destfull);
4383
					$rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $upload_dir);
4384
4385
					if (! preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir))     // If not a tmp dir
4386
					{
4387
						$filename = basename($destfile);
4388
						$rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
4389
						$rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
4390
4391
						include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
4392
						$ecmfile=new EcmFiles($this->db);
4393
						$result = $ecmfile->fetch(0, '', ($rel_dir?$rel_dir.'/':'').$filename);
4394
4395
						// Set the public "share" key
4396
						$setsharekey = false;
4397
						if ($this->element == 'propal')
4398
						{
4399
							$useonlinesignature = $conf->global->MAIN_FEATURES_LEVEL;	// Replace this with 1 when feature to make online signature is ok
4400
							if ($useonlinesignature) $setsharekey=true;
4401
							if (! empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4402
						}
4403
						if ($this->element == 'commande'     && ! empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD))        $setsharekey=true;
4404
						if ($this->element == 'facture'      && ! empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD))      $setsharekey=true;
4405
						if ($this->element == 'bank_account' && ! empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4406
4407
						if ($setsharekey)
4408
						{
4409
							if (empty($ecmfile->share))	// Because object not found or share not set yet
4410
							{
4411
								require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
4412
								$ecmfile->share = getRandomPassword(true);
4413
							}
4414
						}
4415
4416
						if ($result > 0)
4417
						{
4418
							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4419
							$ecmfile->fullpath_orig = '';
4420
							$ecmfile->gen_or_uploaded = 'generated';
4421
							$ecmfile->description = '';    // indexed content
4422
							$ecmfile->keyword = '';        // keyword content
4423
							$result = $ecmfile->update($user);
4424
							if ($result < 0)
4425
							{
4426
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4427
							}
4428
						}
4429
						else
4430
						{
4431
							$ecmfile->entity = $conf->entity;
4432
							$ecmfile->filepath = $rel_dir;
4433
							$ecmfile->filename = $filename;
4434
							$ecmfile->label = md5_file(dol_osencode($destfull));	// hash of file content
4435
							$ecmfile->fullpath_orig = '';
4436
							$ecmfile->gen_or_uploaded = 'generated';
4437
							$ecmfile->description = '';    // indexed content
4438
							$ecmfile->keyword = '';        // keyword content
4439
							$ecmfile->src_object_type = $this->table_element;
4440
							$ecmfile->src_object_id   = $this->id;
4441
4442
							$result = $ecmfile->create($user);
4443
							if ($result < 0)
4444
							{
4445
								setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4446
							}
4447
						}
4448
4449
						/*$this->result['fullname']=$destfull;
4450
						$this->result['filepath']=$ecmfile->filepath;
4451
						$this->result['filename']=$ecmfile->filename;*/
4452
						//var_dump($obj->update_main_doc_field);exit;
4453
4454
						// Update the last_main_doc field into main object (if documenent generator has property ->update_main_doc_field set)
4455
						$update_main_doc_field=0;
4456
						if (! empty($obj->update_main_doc_field)) $update_main_doc_field=1;
4457
						if ($update_main_doc_field && ! empty($this->table_element))
4458
						{
4459
							$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET last_main_doc = '".($ecmfile->filepath.'/'.$ecmfile->filename)."'";
4460
							$sql.= ' WHERE rowid = '.$this->id;
4461
							$resql = $this->db->query($sql);
4462
							if (! $resql) dol_print_error($this->db);
4463
						}
4464
					}
4465
				}
4466
				else
4467
				{
4468
					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);
4469
				}
4470
4471
				// Success in building document. We build meta file.
4472
				dol_meta_create($this);
4473
4474
				return 1;
4475
			}
4476
			else
4477
			{
4478
				$outputlangs->charset_output=$sav_charset_output;
4479
				dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors);
4480
				return -1;
4481
			}
4482
4483
		}
4484
		else
4485
		{
4486
			$this->error=$langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists",$file);
4487
			dol_print_error('',$this->error);
4488
			return -1;
4489
		}
4490
	}
4491
4492
	/**
4493
	 *  Build thumb
4494
	 *  @TODO Move this into files.lib.php
4495
	 *
4496
	 *  @param      string	$file           Path file in UTF8 to original file to create thumbs from.
4497
	 *	@return		void
4498
	 */
4499
	function addThumbs($file)
4500
	{
4501
		global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality;
4502
4503
		require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';		// This define also $maxwidthsmall, $quality, ...
4504
4505
		$file_osencoded=dol_osencode($file);
4506
		if (file_exists($file_osencoded))
4507
		{
4508
			// Create small thumbs for company (Ratio is near 16/9)
4509
			// Used on logon for example
4510
			vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
4511
4512
			// Create mini thumbs for company (Ratio is near 16/9)
4513
			// Used on menu or for setup page for example
4514
			vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
4515
		}
4516
	}
4517
4518
4519
	/* Functions common to commonobject and commonobjectline */
4520
4521
	/* For default values */
4522
4523
	/**
4524
	 * Return the default value to use for a field when showing the create form of object.
4525
	 * Return values in this order:
4526
	 * 1) If parameter is available into POST, we return it first.
4527
	 * 2) If not but an alternate value was provided as parameter of function, we return it.
4528
	 * 3) If not but a constant $conf->global->OBJECTELEMENT_FIELDNAME is set, we return it (It is better to use the dedicated table).
4529
	 * 4) Return value found into database (TODO No yet implemented)
4530
	 *
4531
	 * @param   string              $fieldname          Name of field
4532
	 * @param   string              $alternatevalue     Alternate value to use
4533
	 * @return  string|string[]                         Default value (can be an array if the GETPOST return an array)
4534
	 **/
4535
	function getDefaultCreateValueFor($fieldname, $alternatevalue=null)
4536
	{
4537
		global $conf, $_POST;
4538
4539
		// If param here has been posted, we use this value first.
4540
		if (isset($_POST[$fieldname])) return GETPOST($fieldname, 2);
4541
4542
		if (isset($alternatevalue)) return $alternatevalue;
4543
4544
		$newelement=$this->element;
4545
		if ($newelement == 'facture') $newelement='invoice';
4546
		if ($newelement == 'commande') $newelement='order';
4547
		if (empty($newelement))
4548
		{
4549
			dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
4550
			return '';
4551
		}
4552
4553
		$keyforfieldname=strtoupper($newelement.'_DEFAULT_'.$fieldname);
4554
		//var_dump($keyforfieldname);
4555
		if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname;
4556
4557
		// TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
4558
4559
	}
4560
4561
4562
	/* For triggers */
4563
4564
4565
	/**
4566
	 * Call trigger based on this instance.
4567
	 * Some context information may also be provided into array property this->context.
4568
	 * NB:  Error from trigger are stacked in interface->errors
4569
	 * NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction.
4570
	 *
4571
	 * @param   string    $trigger_name   trigger's name to execute
4572
	 * @param   User      $user           Object user
4573
	 * @return  int                       Result of run_triggers
4574
	 */
4575
	function call_trigger($trigger_name, $user)
4576
	{
4577
		global $langs,$conf;
4578
4579
		include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
4580
		$interface=new Interfaces($this->db);
4581
		$result=$interface->run_triggers($trigger_name,$this,$user,$langs,$conf);
4582
4583
		if ($result < 0)
4584
		{
4585
			if (!empty($this->errors))
4586
			{
4587
				$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.
4588
			}
4589
			else
4590
			{
4591
				$this->errors=$interface->errors;
4592
			}
4593
		}
4594
		return $result;
4595
	}
4596
4597
4598
	/* Functions for extrafields */
4599
4600
4601
	/**
4602
	 *  Function to get extra fields of an object into $this->array_options
4603
	 *  This method is in most cases called by method fetch of objects but you can call it separately.
4604
	 *
4605
	 *  @param	int		$rowid			Id of line. Use the id of object if not defined. Deprecated. Function must be called without parameters.
4606
	 *  @param  array	$optionsArray   Array resulting of call of extrafields->fetch_name_optionals_label(). Deprecated. Function must be called without parameters.
4607
	 *  @return	int						<0 if error, 0 if no values of extrafield to find nor found, 1 if an attribute is found and value loaded
4608
	 */
4609
	function fetch_optionals($rowid=null, $optionsArray=null)
4610
	{
4611
		if (empty($rowid)) $rowid=$this->id;
4612
4613
		// To avoid SQL errors. Probably not the better solution though
4614
		if (!$this->table_element) {
4615
			return 0;
4616
		}
4617
4618
		$this->array_options=array();
4619
4620
		if (! is_array($optionsArray))
4621
		{
4622
			// If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
4623
			// TODO Use of existing $extrafield is not yet ready (must mutualize code that use extrafields in form first)
4624
			// global $extrafields;
4625
			//if (! is_object($extrafields))
4626
			//{
4627
				require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4628
				$extrafields = new ExtraFields($this->db);
4629
			//}
4630
4631
			// Load array of extrafields for elementype = $this->table_element
4632
			if (empty($extrafields->attributes[$this->table_element]['loaded']))
4633
			{
4634
				$extrafields->fetch_name_optionals_label($this->table_element);
4635
			}
4636
			$optionsArray = (! empty($extrafields->attributes[$this->table_element]['label'])?$extrafields->attributes[$this->table_element]['label']:null);
4637
		}
4638
		else
4639
		{
4640
			global $extrafields;
4641
			dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
4642
		}
4643
4644
		$table_element = $this->table_element;
4645
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4646
4647
		// Request to get complementary values
4648
		if (is_array($optionsArray) && count($optionsArray) > 0)
4649
		{
4650
			$sql = "SELECT rowid";
4651
			foreach ($optionsArray as $name => $label)
4652
			{
4653
				if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate')
4654
				{
4655
					$sql.= ", ".$name;
4656
				}
4657
			}
4658
			$sql.= " FROM ".MAIN_DB_PREFIX.$table_element."_extrafields";
4659
			$sql.= " WHERE fk_object = ".$rowid;
4660
4661
			//dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG);		// Too verbose
4662
			$resql=$this->db->query($sql);
4663
			if ($resql)
4664
			{
4665
				$numrows=$this->db->num_rows($resql);
4666
				if ($numrows)
4667
				{
4668
					$tab = $this->db->fetch_array($resql);
4669
4670
					foreach ($tab as $key => $value)
4671
					{
4672
						// 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)
4673
						if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && ! is_int($key))
4674
						{
4675
							// we can add this attribute to object
4676
							if (! empty($extrafields) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime')))
4677
							{
4678
								//var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
4679
								$this->array_options["options_".$key]=$this->db->jdate($value);
4680
							}
4681
							else
4682
							{
4683
								$this->array_options["options_".$key]=$value;
4684
							}
4685
4686
							//var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
4687
						}
4688
					}
4689
				}
4690
4691
				$this->db->free($resql);
4692
4693
				if ($numrows) return $numrows;
4694
				else return 0;
4695
			}
4696
			else
4697
			{
4698
				dol_print_error($this->db);
4699
				return -1;
4700
			}
4701
		}
4702
		return 0;
4703
	}
4704
4705
	/**
4706
	 *	Delete all extra fields values for the current object.
4707
	 *
4708
	 *  @return	int		<0 if KO, >0 if OK
4709
	 */
4710
	function deleteExtraFields()
4711
	{
4712
		$this->db->begin();
4713
4714
		$table_element = $this->table_element;
4715
		if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4716
4717
		$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
4718
		dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
4719
		$resql=$this->db->query($sql_del);
4720
		if (! $resql)
4721
		{
4722
			$this->error=$this->db->lasterror();
4723
			$this->db->rollback();
4724
			return -1;
4725
		}
4726
		else
4727
		{
4728
			$this->db->commit();
4729
			return 1;
4730
		}
4731
	}
4732
4733
	/**
4734
	 *	Add/Update all extra fields values for the current object.
4735
	 *  Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
4736
	 *  This function delete record with all extrafields and insert them again from the array $this->array_options.
4737
	 *
4738
	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
4739
	 *  @param	User		$userused		Object user
4740
	 *  @return int 						-1=error, O=did nothing, 1=OK
4741
	 *  @see updateExtraField, setValueFrom
4742
	 */
4743
	function insertExtraFields($trigger='', $userused=null)
4744
	{
4745
		global $conf,$langs,$user;
4746
4747
		if (empty($userused)) $userused=$user;
4748
4749
		$error=0;
4750
4751
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
4752
4753
		if (! empty($this->array_options))
4754
		{
4755
			// Check parameters
4756
			$langs->load('admin');
4757
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4758
			$extrafields = new ExtraFields($this->db);
4759
			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
4760
4761
			//Eliminate copied source object extra_fields that do not exist in target object
4762
			$new_array_options=array();
4763
			foreach ($this->array_options as $key => $value) {
4764
				if (in_array(substr($key,8), array_keys($target_extrafields)))	// We remove the 'options_' from $key for test
4765
					$new_array_options[$key] = $value;
4766
				elseif (in_array($key, array_keys($target_extrafields)))		// We test on $key that does not contains the 'options_' prefix
4767
					$new_array_options['options_'.$key] = $value;
4768
			}
4769
4770
			foreach($new_array_options as $key => $value)
4771
			{
4772
			   	$attributeKey      = substr($key,8);   // Remove 'options_' prefix
4773
			   	$attributeType     = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
4774
			   	$attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
4775
			   	$attributeParam    = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
4776
			   	$attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
4777
4778
			   	if ($attributeRequired)
4779
			   	{
4780
			   		$mandatorypb=false;
4781
			   		if ($attributeType == 'link' && $this->array_options[$key] == '-1') $mandatorypb=true;
4782
			   		if ($this->array_options[$key] === '') $mandatorypb=true;
4783
			   		if ($mandatorypb)
4784
			   		{
4785
			   			$this->errors[]=$langs->trans('ErrorFieldRequired', $attributeLabel);
4786
			   			return -1;
4787
			   		}
4788
			   	}
4789
4790
			   	switch ($attributeType)
4791
			   	{
4792
			   		case 'int':
4793
			  			if (!is_numeric($value) && $value!='')
4794
			   			{
4795
			   				$this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
4796
			   				return -1;
4797
			  			}
4798
			   			elseif ($value=='')
4799
			   			{
4800
			   				$new_array_options[$key] = null;
4801
			   			}
4802
			 			break;
4803
			 		/*case 'select':	// Not required, we chosed value='0' for undefined values
4804
             			if ($value=='-1')
4805
             			{
4806
             				$this->array_options[$key] = null;
4807
             			}
4808
             			break;*/
4809
			   		case 'password':
4810
			   			$algo='';
4811
			   			if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']))
4812
			   			{
4813
			   				// If there is an encryption choice, we use it to crypt data before insert
4814
			   				$tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
4815
			   				$algo=reset($tmparrays);
4816
			   				if ($algo != '')
4817
			   				{
4818
			   					//global $action;		// $action may be 'create', 'update', 'update_extras'...
4819
			   					//var_dump($action);
4820
			   					//var_dump($this->oldcopy);exit;
4821
			   					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
4822
			   					{
4823
			   						//var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
4824
				   					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.
4825
				   					{
4826
				   						$new_array_options[$key] = $this->array_options[$key];	// Value is kept
4827
				   					}
4828
									else
4829
									{
4830
										// var_dump($algo);
4831
										$newvalue = dol_hash($this->array_options[$key], $algo);
4832
										$new_array_options[$key] = $newvalue;
4833
									}
4834
			   					}
4835
			   					else
4836
			   					{
4837
			   						$new_array_options[$key] = $this->array_options[$key];	// Value is kept
4838
			   					}
4839
			   				}
4840
			   			}
4841
			   			else	// Common usage
4842
			   			{
4843
			   				$new_array_options[$key] = $this->array_options[$key];
4844
			   			}
4845
			   			break;
4846
			   		case 'price':
4847
						$new_array_options[$key] = price2num($this->array_options[$key]);
4848
						break;
4849
					case 'date':
4850
						$new_array_options[$key] = $this->db->idate($this->array_options[$key]);
4851
						break;
4852
					case 'datetime':
4853
						// If data is a string instead of a timestamp, we convert it
4854
						if (! is_int($this->array_options[$key])) {
4855
							$this->array_options[$key] = strtotime($this->array_options[$key]);
4856
						}
4857
						$new_array_options[$key] = $this->db->idate($this->array_options[$key]);
4858
						break;
4859
		   			case 'link':
4860
						$param_list=array_keys($attributeParam['options']);
4861
						// 0 : ObjectName
4862
						// 1 : classPath
4863
						$InfoFieldList = explode(":", $param_list[0]);
4864
						dol_include_once($InfoFieldList[1]);
4865
						if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
4866
						{
4867
							if ($value == '-1')	// -1 is key for no defined in combo list of objects
4868
							{
4869
								$new_array_options[$key]='';
4870
							}
4871
							elseif ($value)
4872
							{
4873
								$object = new $InfoFieldList[0]($this->db);
4874
								if (is_numeric($value)) $res=$object->fetch($value);
4875
								else $res=$object->fetch('',$value);
4876
4877
								if ($res > 0) $new_array_options[$key]=$object->id;
4878
								else
4879
								{
4880
									$this->error="Id/Ref '".$value."' for object '".$object->element."' not found";
4881
									$this->db->rollback();
4882
									return -1;
4883
								}
4884
							}
4885
						}
4886
						else
4887
						{
4888
							dol_syslog('Error bad setup of extrafield', LOG_WARNING);
4889
						}
4890
						break;
4891
			   	}
4892
			}
4893
4894
			$this->db->begin();
4895
4896
			$table_element = $this->table_element;
4897
			if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4898
4899
			$sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
4900
			dol_syslog(get_class($this)."::insertExtraFields delete", LOG_DEBUG);
4901
			$this->db->query($sql_del);
4902
4903
			$sql = "INSERT INTO ".MAIN_DB_PREFIX.$table_element."_extrafields (fk_object";
4904
			foreach($new_array_options as $key => $value)
4905
			{
4906
				$attributeKey = substr($key,8);   // Remove 'options_' prefix
4907
				// Add field of attribut
4908
				if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator
4909
					$sql.=",".$attributeKey;
4910
			}
4911
			$sql .= ") VALUES (".$this->id;
4912
4913
			foreach($new_array_options as $key => $value)
4914
			{
4915
				$attributeKey = substr($key,8);   // Remove 'options_' prefix
4916
				// Add field of attribute
4917
				if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator)
4918
				{
4919
					if ($new_array_options[$key] != '')
4920
					{
4921
						$sql.=",'".$this->db->escape($new_array_options[$key])."'";
4922
					}
4923
					else
4924
					{
4925
						$sql.=",null";
4926
					}
4927
				}
4928
			}
4929
			$sql.=")";
4930
4931
			dol_syslog(get_class($this)."::insertExtraFields insert", LOG_DEBUG);
4932
			$resql = $this->db->query($sql);
4933
			if (! $resql)
4934
			{
4935
				$this->error=$this->db->lasterror();
4936
				$error++;
4937
			}
4938
4939
			if (! $error && $trigger)
4940
			{
4941
				// Call trigger
4942
				$this->context=array('extrafieldaddupdate'=>1);
4943
				$result=$this->call_trigger($trigger, $userused);
4944
				if ($result < 0) $error++;
4945
				// End call trigger
4946
			}
4947
4948
			if ($error)
4949
			{
4950
				$this->db->rollback();
4951
				return -1;
4952
			}
4953
			else
4954
			{
4955
				$this->db->commit();
4956
				return 1;
4957
			}
4958
		}
4959
		else return 0;
4960
	}
4961
4962
	/**
4963
	 *	Update an extra field value for the current object.
4964
	 *  Data to describe values to update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...)
4965
	 *
4966
	 *  @param  string      $key    		Key of the extrafield (without starting 'options_')
4967
	 *  @param	string		$trigger		If defined, call also the trigger (for example COMPANY_MODIFY)
4968
	 *  @param	User		$userused		Object user
4969
	 *  @return int                 		-1=error, O=did nothing, 1=OK
4970
	 *  @see setValueFrom, insertExtraFields
4971
	 */
4972
	function updateExtraField($key, $trigger, $userused)
4973
	{
4974
		global $conf,$langs,$user;
4975
4976
		if (empty($userused)) $userused=$user;
4977
4978
		$error=0;
4979
4980
		if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;	// For avoid conflicts if trigger used
4981
4982
		if (! empty($this->array_options) && isset($this->array_options["options_".$key]))
4983
		{
4984
			// Check parameters
4985
			$langs->load('admin');
4986
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4987
			$extrafields = new ExtraFields($this->db);
4988
			$target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
4989
4990
			$value=$this->array_options["options_".$key];
4991
4992
			$attributeType     = $extrafields->attributes[$this->table_element]['type'][$key];
4993
			$attributeLabel    = $extrafields->attributes[$this->table_element]['label'][$key];
4994
			$attributeParam    = $extrafields->attributes[$this->table_element]['param'][$key];
4995
			$attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
4996
4997
			switch ($attributeType)
4998
			{
4999
				case 'int':
5000
					if (!is_numeric($value) && $value!='')
5001
					{
5002
						$this->errors[]=$langs->trans("ExtraFieldHasWrongValue",$attributeLabel);
5003
						return -1;
5004
					}
5005
					elseif ($value=='')
5006
					{
5007
						$this->array_options["options_".$key] = null;
5008
					}
5009
					break;
5010
			 	/*case 'select':	// Not required, we chosed value='0' for undefined values
5011
             		if ($value=='-1')
5012
             		{
5013
             			$this->array_options[$key] = null;
5014
             		}
5015
             		break;*/
5016
				case 'price':
5017
					$this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
5018
					break;
5019
				case 'date':
5020
					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5021
					break;
5022
				case 'datetime':
5023
					$this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5024
					break;
5025
				case 'link':
5026
					$param_list=array_keys($attributeParam['options']);
5027
					// 0 : ObjectName
5028
					// 1 : classPath
5029
					$InfoFieldList = explode(":", $param_list[0]);
5030
					dol_include_once($InfoFieldList[1]);
5031
					if ($value)
5032
					{
5033
						$object = new $InfoFieldList[0]($this->db);
5034
						$object->fetch(0,$value);
5035
						$this->array_options["options_".$key]=$object->id;
5036
					}
5037
					break;
5038
			}
5039
5040
			$this->db->begin();
5041
			$sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key."='".$this->db->escape($this->array_options["options_".$key])."'";
5042
			$sql .= " WHERE fk_object = ".$this->id;
5043
			$resql = $this->db->query($sql);
5044
			if (! $resql)
5045
			{
5046
				$error++;
5047
				$this->error=$this->db->lasterror();
5048
			}
5049
5050
			if (! $error && $trigger)
5051
			{
5052
				// Call trigger
5053
				$this->context=array('extrafieldupdate'=>1);
5054
				$result=$this->call_trigger($trigger, $userused);
5055
				if ($result < 0) $error++;
5056
				// End call trigger
5057
			}
5058
5059
			if ($error)
5060
			{
5061
				dol_syslog(get_class($this) . "::".__METHOD__ . $this->error, LOG_ERR);
5062
				$this->db->rollback();
5063
				return -1;
5064
			}
5065
			else
5066
			{
5067
				$this->db->commit();
5068
				return 1;
5069
			}
5070
		}
5071
		else return 0;
5072
	}
5073
5074
5075
	/**
5076
	 * Return HTML string to put an input field into a page
5077
	 * Code very similar with showInputField of extra fields
5078
	 *
5079
	 * @param  array   		$val	       Array of properties for field to show
5080
	 * @param  string  		$key           Key of attribute
5081
	 * @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)
5082
	 * @param  string  		$moreparam     To add more parameters on html input tag
5083
	 * @param  string  		$keysuffix     Prefix string to add into name and id of field (can be used to avoid duplicate names)
5084
	 * @param  string  		$keyprefix     Suffix string to add into name and id of field (can be used to avoid duplicate names)
5085
	 * @param  string|int	$showsize      Value for css to define size. May also be a numeric.
5086
	 * @return string
5087
	 */
5088
	function showInputField($val, $key, $value, $moreparam='', $keysuffix='', $keyprefix='', $showsize=0)
5089
	{
5090
		global $conf,$langs,$form;
5091
5092
		if (! is_object($form))
5093
		{
5094
			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5095
			$form=new Form($this->db);
5096
		}
5097
5098
		$objectid = $this->id;
5099
5100
		$label= $val['label'];
5101
		$type = $val['type'];
5102
		$size = $val['css'];
5103
5104
		// Convert var to be able to share same code than showInputField of extrafields
5105
		if (preg_match('/varchar\((\d+)\)/', $type, $reg))
5106
		{
5107
			$type = 'varchar';		// convert varchar(xx) int varchar
5108
			$size = $reg[1];
5109
		}
5110
		elseif (preg_match('/varchar/', $type)) $type = 'varchar';		// convert varchar(xx) into varchar
5111
		elseif (preg_match('/double/', $type)) $type = 'double';		// convert double(xx) into double
5112
		if (is_array($val['arrayofkeyval'])) $type='select';
5113
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) $type='link';
5114
5115
		$default=$val['default'];
5116
		$computed=$val['computed'];
5117
		$unique=$val['unique'];
5118
		$required=$val['required'];
5119
		$param=$val['param'];
5120
		if (is_array($val['arrayofkeyval'])) $param['options'] = $val['arrayofkeyval'];
5121
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg))
5122
		{
5123
			$type='link';
5124
			$param['options']=array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
5125
		}
5126
		$langfile=$val['langfile'];
5127
		$list=$val['list'];
5128
		$hidden=(abs($val['visible'])!=1 ? 1 : 0);
5129
		$help=$val['help'];
5130
5131
		if ($computed)
5132
		{
5133
			if (! preg_match('/^search_/', $keyprefix)) return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
5134
			else return '';
5135
		}
5136
5137
		// Use in priority showsize from parameters, then $val['css'] then autodefine
5138
		if (empty($showsize) && ! empty($val['css']))
5139
		{
5140
			$showsize = $val['css'];
5141
		}
5142
		if (empty($showsize))
5143
		{
5144
			if ($type == 'date')
5145
			{
5146
				//$showsize=10;
5147
				$showsize = 'minwidth100imp';
5148
			}
5149
			elseif ($type == 'datetime')
5150
			{
5151
				//$showsize=19;
5152
				$showsize = 'minwidth200imp';
5153
			}
5154
			elseif (in_array($type,array('int','double','price')))
5155
			{
5156
				//$showsize=10;
5157
				$showsize = 'maxwidth75';
5158
			}
5159
			elseif ($type == 'url')
5160
			{
5161
				$showsize='minwidth400';
5162
			}
5163
			elseif ($type == 'boolean')
5164
			{
5165
				$showsize='';
5166
			}
5167
			else
5168
			{
5169
				if (round($size) < 12)
5170
				{
5171
					$showsize = 'minwidth100';
5172
				}
5173
				else if (round($size) <= 48)
5174
				{
5175
					$showsize = 'minwidth200';
5176
				}
5177
				else
5178
				{
5179
					//$showsize=48;
5180
					$showsize = 'minwidth400';
5181
				}
5182
			}
5183
		}
5184
		//var_dump($showsize.' '.$size);
5185
		if (in_array($type,array('date','datetime')))
5186
		{
5187
			$tmp=explode(',',$size);
5188
			$newsize=$tmp[0];
5189
5190
			$showtime = in_array($type,array('datetime')) ? 1 : 0;
5191
5192
			// Do not show current date when field not required (see select_date() method)
5193
			if (!$required && $value == '') $value = '-1';
5194
5195
			// TODO Must also support $moreparam
5196
			$out = $form->select_date($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, ($keyprefix != 'search_' ? 1 : 0), 1, 0, 1);
5197
		}
5198
		elseif (in_array($type,array('int','integer')))
5199
		{
5200
			$tmp=explode(',',$size);
5201
			$newsize=$tmp[0];
5202
			$out='<input type="text" class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$newsize.'" value="'.$value.'"'.($moreparam?$moreparam:'').'>';
5203
		}
5204
		elseif (preg_match('/varchar/', $type))
5205
		{
5206
			$out='<input type="text" class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$size.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5207
		}
5208
		elseif (in_array($type, array('mail', 'phone', 'url')))
5209
		{
5210
			$out='<input type="text" class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'>';
5211
		}
5212
		elseif ($type == 'text')
5213
		{
5214
			require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5215
			$doleditor=new DolEditor($keyprefix.$key.$keysuffix,$value,'',200,'dolibarr_notes','In',false,false,0,ROWS_5,'90%');
5216
			$out=$doleditor->Create(1);
5217
		}
5218
		elseif ($type == 'html')
5219
		{
5220
			require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5221
			$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%');
5222
			$out=$doleditor->Create(1);
5223
		}
5224
		elseif ($type == 'boolean')
5225
		{
5226
			$checked='';
5227
			if (!empty($value)) {
5228
				$checked=' checked value="1" ';
5229
			} else {
5230
				$checked=' value="1" ';
5231
			}
5232
			$out='<input type="checkbox" class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam?$moreparam:'').'>';
5233
		}
5234
		elseif ($type == 'price')
5235
		{
5236
			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5237
				$value=price($value);
5238
			}
5239
			$out='<input type="text" class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> '.$langs->getCurrencySymbol($conf->currency);
5240
		}
5241
		elseif ($type == 'double')
5242
		{
5243
			if (!empty($value)) {		// $value in memory is a php numeric, we format it into user number format.
5244
				$value=price($value);
5245
			}
5246
			$out='<input type="text" class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> ';
5247
		}
5248
		elseif ($type == 'select')
5249
		{
5250
			$out = '';
5251
			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5252
			{
5253
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5254
				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5255
			}
5256
5257
			$out.='<select class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5258
			if ((! isset($val['default'])) || ($val['notnull'] != 1)) $out.='<option value="0">&nbsp;</option>';
5259
			foreach ($param['options'] as $key => $val)
5260
			{
5261
				if ((string) $key == '') continue;
5262
				list($val, $parent) = explode('|', $val);
5263
				$out.='<option value="'.$key.'"';
5264
				$out.= (((string) $value == (string) $key)?' selected':'');
5265
				$out.= (!empty($parent)?' parent="'.$parent.'"':'');
5266
				$out.='>'.$val.'</option>';
5267
			}
5268
			$out.='</select>';
5269
		}
5270
		elseif ($type == 'sellist')
5271
		{
5272
			$out = '';
5273
			if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5274
			{
5275
				include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5276
				$out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5277
			}
5278
5279
			$out.='<select class="flat '.$showsize.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5280
			if (is_array($param['options']))
5281
			{
5282
				$param_list=array_keys($param['options']);
5283
				$InfoFieldList = explode(":", $param_list[0]);
5284
				// 0 : tableName
5285
				// 1 : label field name
5286
				// 2 : key fields name (if differ of rowid)
5287
				// 3 : key field parent (for dependent lists)
5288
				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5289
				$keyList=(empty($InfoFieldList[2])?'rowid':$InfoFieldList[2].' as rowid');
5290
5291
5292
				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4]))
5293
				{
5294
					if (strpos($InfoFieldList[4], 'extra.') !== false)
5295
					{
5296
						$keyList='main.'.$InfoFieldList[2].' as rowid';
5297
					} else {
5298
						$keyList=$InfoFieldList[2].' as rowid';
5299
					}
5300
				}
5301
				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3]))
5302
				{
5303
					list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
5304
					$keyList.= ', '.$parentField;
5305
				}
5306
5307
				$fields_label = explode('|',$InfoFieldList[1]);
5308
				if (is_array($fields_label))
5309
				{
5310
					$keyList .=', ';
5311
					$keyList .= implode(', ', $fields_label);
5312
				}
5313
5314
				$sqlwhere='';
5315
				$sql = 'SELECT '.$keyList;
5316
				$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
5317
				if (!empty($InfoFieldList[4]))
5318
				{
5319
					// can use SELECT request
5320
					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5321
						$InfoFieldList[4]=str_replace('$SEL$','SELECT',$InfoFieldList[4]);
5322
					}
5323
5324
					// current object id can be use into filter
5325
					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5326
						$InfoFieldList[4]=str_replace('$ID$',$objectid,$InfoFieldList[4]);
5327
					} else {
5328
						$InfoFieldList[4]=str_replace('$ID$','0',$InfoFieldList[4]);
5329
					}
5330
					//We have to join on extrafield table
5331
					if (strpos($InfoFieldList[4], 'extra')!==false)
5332
					{
5333
						$sql.= ' as main, '.MAIN_DB_PREFIX .$InfoFieldList[0].'_extrafields as extra';
5334
						$sqlwhere.= ' WHERE extra.fk_object=main.'.$InfoFieldList[2]. ' AND '.$InfoFieldList[4];
5335
					}
5336
					else
5337
					{
5338
						$sqlwhere.= ' WHERE '.$InfoFieldList[4];
5339
					}
5340
				}
5341
				else
5342
				{
5343
					$sqlwhere.= ' WHERE 1=1';
5344
				}
5345
				// Some tables may have field, some other not. For the moment we disable it.
5346
				if (in_array($InfoFieldList[0],array('tablewithentity')))
5347
				{
5348
					$sqlwhere.= ' AND entity = '.$conf->entity;
5349
				}
5350
				$sql.=$sqlwhere;
5351
				//print $sql;
5352
5353
				$sql .= ' ORDER BY ' . implode(', ', $fields_label);
5354
5355
				dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
5356
				$resql = $this->db->query($sql);
5357
				if ($resql)
5358
				{
5359
					$out.='<option value="0">&nbsp;</option>';
5360
					$num = $this->db->num_rows($resql);
5361
					$i = 0;
5362
					while ($i < $num)
5363
					{
5364
						$labeltoshow='';
5365
						$obj = $this->db->fetch_object($resql);
5366
5367
						// Several field into label (eq table:code|libelle:rowid)
5368
						$fields_label = explode('|',$InfoFieldList[1]);
5369
						if(is_array($fields_label))
5370
						{
5371
							$notrans = true;
5372
							foreach ($fields_label as $field_toshow)
5373
							{
5374
								$labeltoshow.= $obj->$field_toshow.' ';
5375
							}
5376
						}
5377
						else
5378
						{
5379
							$labeltoshow=$obj->{$InfoFieldList[1]};
5380
						}
5381
						$labeltoshow=dol_trunc($labeltoshow,45);
5382
5383
						if ($value==$obj->rowid)
5384
						{
5385
							foreach ($fields_label as $field_toshow)
5386
							{
5387
								$translabel=$langs->trans($obj->$field_toshow);
5388
								if ($translabel!=$obj->$field_toshow) {
5389
									$labeltoshow=dol_trunc($translabel,18).' ';
5390
								}else {
5391
									$labeltoshow=dol_trunc($obj->$field_toshow,18).' ';
5392
								}
5393
							}
5394
							$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5395
						}
5396
						else
5397
						{
5398
							if(!$notrans)
0 ignored issues
show
Bug introduced by
The variable $notrans does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
5399
							{
5400
								$translabel=$langs->trans($obj->{$InfoFieldList[1]});
5401
								if ($translabel!=$obj->{$InfoFieldList[1]}) {
5402
									$labeltoshow=dol_trunc($translabel,18);
5403
								}
5404
								else {
5405
									$labeltoshow=dol_trunc($obj->{$InfoFieldList[1]},18);
5406
								}
5407
							}
5408
							if (empty($labeltoshow)) $labeltoshow='(not defined)';
5409
							if ($value==$obj->rowid)
5410
							{
5411
								$out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5412
							}
5413
5414
							if (!empty($InfoFieldList[3]))
5415
							{
5416
								$parent = $parentName.':'.$obj->{$parentField};
0 ignored issues
show
Bug introduced by
The variable $parentName does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $parentField does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
5417
							}
5418
5419
							$out.='<option value="'.$obj->rowid.'"';
5420
							$out.= ($value==$obj->rowid?' selected':'');
5421
							$out.= (!empty($parent)?' parent="'.$parent.'"':'');
5422
							$out.='>'.$labeltoshow.'</option>';
5423
						}
5424
5425
						$i++;
5426
					}
5427
					$this->db->free($resql);
5428
				}
5429
				else {
5430
					print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
5431
				}
5432
			}
5433
			$out.='</select>';
5434
		}
5435
		elseif ($type == 'checkbox')
5436
		{
5437
			$value_arr=explode(',',$value);
5438
			$out=$form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options'])?null:$param['options']), $value_arr, '', 0, '', 0, '100%');
5439
		}
5440
		elseif ($type == 'radio')
5441
		{
5442
			$out='';
5443
			foreach ($param['options'] as $keyopt => $val)
5444
			{
5445
				$out.='<input class="flat '.$showsize.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'');
5446
				$out.=' value="'.$keyopt.'"';
5447
				$out.=' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
5448
				$out.= ($value==$keyopt?'checked':'');
5449
				$out.='/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$val.'</label><br>';
5450
			}
5451
		}
5452
		elseif ($type == 'chkbxlst')
5453
		{
5454
			if (is_array($value)) {
5455
				$value_arr = $value;
5456
			}
5457
			else {
5458
				$value_arr = explode(',', $value);
5459
			}
5460
5461
			if (is_array($param['options'])) {
5462
				$param_list = array_keys($param['options']);
5463
				$InfoFieldList = explode(":", $param_list[0]);
5464
				// 0 : tableName
5465
				// 1 : label field name
5466
				// 2 : key fields name (if differ of rowid)
5467
				// 3 : key field parent (for dependent lists)
5468
				// 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5469
				$keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
5470
5471
				if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3])) {
5472
					list ( $parentName, $parentField ) = explode('|', $InfoFieldList[3]);
5473
					$keyList .= ', ' . $parentField;
5474
				}
5475
				if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4])) {
5476
					if (strpos($InfoFieldList[4], 'extra.') !== false) {
5477
						$keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
5478
					} else {
5479
						$keyList = $InfoFieldList[2] . ' as rowid';
5480
					}
5481
				}
5482
5483
				$fields_label = explode('|', $InfoFieldList[1]);
5484
				if (is_array($fields_label)) {
5485
					$keyList .= ', ';
5486
					$keyList .= implode(', ', $fields_label);
5487
				}
5488
5489
				$sqlwhere = '';
5490
				$sql = 'SELECT ' . $keyList;
5491
				$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
5492
				if (! empty($InfoFieldList[4])) {
5493
5494
					// can use SELECT request
5495
					if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5496
						$InfoFieldList[4]=str_replace('$SEL$','SELECT',$InfoFieldList[4]);
5497
					}
5498
5499
					// current object id can be use into filter
5500
					if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5501
						$InfoFieldList[4]=str_replace('$ID$',$objectid,$InfoFieldList[4]);
5502
					} else {
5503
						$InfoFieldList[4]=str_replace('$ID$','0',$InfoFieldList[4]);
5504
					}
5505
5506
					// We have to join on extrafield table
5507
					if (strpos($InfoFieldList[4], 'extra') !== false) {
5508
						$sql .= ' as main, ' . MAIN_DB_PREFIX . $InfoFieldList[0] . '_extrafields as extra';
5509
						$sqlwhere .= ' WHERE extra.fk_object=main.' . $InfoFieldList[2] . ' AND ' . $InfoFieldList[4];
5510
					} else {
5511
						$sqlwhere .= ' WHERE ' . $InfoFieldList[4];
5512
					}
5513
				} else {
5514
					$sqlwhere .= ' WHERE 1=1';
5515
				}
5516
				// Some tables may have field, some other not. For the moment we disable it.
5517
				if (in_array($InfoFieldList[0], array ('tablewithentity')))
5518
				{
5519
					$sqlwhere .= ' AND entity = ' . $conf->entity;
5520
				}
5521
				// $sql.=preg_replace('/^ AND /','',$sqlwhere);
5522
				// print $sql;
5523
5524
				$sql .= $sqlwhere;
5525
				dol_syslog(get_class($this) . '::showInputField type=chkbxlst',LOG_DEBUG);
5526
				$resql = $this->db->query($sql);
5527
				if ($resql) {
5528
					$num = $this->db->num_rows($resql);
5529
					$i = 0;
5530
5531
					$data=array();
5532
5533
					while ( $i < $num ) {
5534
						$labeltoshow = '';
5535
						$obj = $this->db->fetch_object($resql);
5536
5537
						// Several field into label (eq table:code|libelle:rowid)
5538
						$fields_label = explode('|', $InfoFieldList[1]);
5539
						if (is_array($fields_label)) {
5540
							$notrans = true;
5541
							foreach ( $fields_label as $field_toshow ) {
5542
								$labeltoshow .= $obj->$field_toshow . ' ';
5543
							}
5544
						} else {
5545
							$labeltoshow = $obj->{$InfoFieldList[1]};
5546
						}
5547
						$labeltoshow = dol_trunc($labeltoshow, 45);
5548
5549
						if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5550
							foreach ( $fields_label as $field_toshow ) {
5551
								$translabel = $langs->trans($obj->$field_toshow);
5552
								if ($translabel != $obj->$field_toshow) {
5553
									$labeltoshow = dol_trunc($translabel, 18) . ' ';
5554
								} else {
5555
									$labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
5556
								}
5557
							}
5558
5559
							$data[$obj->rowid]=$labeltoshow;
5560
5561
						} else {
5562
							if (! $notrans) {
5563
								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
5564
								if ($translabel != $obj->{$InfoFieldList[1]}) {
5565
									$labeltoshow = dol_trunc($translabel, 18);
5566
								} else {
5567
									$labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
5568
								}
5569
							}
5570
							if (empty($labeltoshow))
5571
								$labeltoshow = '(not defined)';
5572
5573
								if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5574
									$data[$obj->rowid]=$labeltoshow;
5575
								}
5576
5577
								if (! empty($InfoFieldList[3])) {
5578
									$parent = $parentName . ':' . $obj->{$parentField};
5579
								}
5580
5581
								$data[$obj->rowid]=$labeltoshow;
5582
						}
5583
5584
						$i ++;
5585
					}
5586
					$this->db->free($resql);
5587
5588
					$out=$form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
5589
5590
				} else {
5591
					print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
5592
				}
5593
			}
5594
			$out .= '</select>';
0 ignored issues
show
Bug introduced by
The variable $out does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
5595
		}
5596
		elseif ($type == 'link')
5597
		{
5598
			$param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
5599
			$showempty=(($val['notnull'] == 1 && $val['default'] != '')?0:1);
5600
			$out=$form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty);
5601
		}
5602
		elseif ($type == 'password')
5603
		{
5604
			// If prefix is 'search_', field is used as a filter, we use a common text field.
5605
			$out='<input type="'.($keyprefix=='search_'?'text':'password').'" class="flat '.$showsize.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'>';
5606
		}
5607
		elseif ($type == 'array')
5608
		{
5609
			$newval = $val;
5610
			$newval['type'] = 'varchar(256)';
5611
5612
			$out='';
5613
5614
			$inputs = array();
5615
			if(! empty($value)) {
5616
				foreach($value as $option) {
0 ignored issues
show
Bug introduced by
The expression $value of type string is not traversable.
Loading history...
5617
					$out.= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5618
					$out.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $showsize).'<br></span>';
5619
				}
5620
			}
5621
5622
			$out.= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
5623
5624
			$newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5625
			$newInput.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $showsize).'<br></span>';
5626
5627
			if(! empty($conf->use_javascript_ajax)) {
5628
				$out.= '
5629
					<script type="text/javascript">
5630
					$(document).ready(function() {
5631
						$("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
5632
							$("'.dol_escape_js($newInput).'").insertBefore(this);
5633
						});
5634
5635
						$(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
5636
							$(this).parent().remove();
5637
						});
5638
					});
5639
					</script>';
5640
			}
5641
		}
5642
		if (!empty($hidden)) {
5643
			$out='<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
5644
		}
5645
		/* Add comments
5646
		 if ($type == 'date') $out.=' (YYYY-MM-DD)';
5647
		 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
5648
		 */
5649
		return $out;
5650
	}
5651
5652
5653
	/**
5654
	 * Return HTML string to show a field into a page
5655
	 * Code very similar with showOutputField of extra fields
5656
	 *
5657
	 * @param  array   $val		       Array of properties of field to show
5658
	 * @param  string  $key            Key of attribute
5659
	 * @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)
5660
	 * @param  string  $moreparam      To add more parametes on html input tag
5661
	 * @param  string  $keysuffix      Prefix string to add into name and id of field (can be used to avoid duplicate names)
5662
	 * @param  string  $keyprefix      Suffix string to add into name and id of field (can be used to avoid duplicate names)
5663
	 * @param  mixed   $showsize       Value for css to define size. May also be a numeric.
5664
	 * @return string
5665
	 */
5666
	function showOutputField($val, $key, $value, $moreparam='', $keysuffix='', $keyprefix='', $showsize=0)
5667
	{
5668
		global $conf,$langs,$form;
5669
5670
		if (! is_object($form))
5671
		{
5672
			require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5673
			$form=new Form($this->db);
5674
		}
5675
5676
		$objectid = $this->id;
5677
		$label = $val['label'];
5678
		$type  = $val['type'];
5679
		$size  = $val['css'];
5680
5681
		// Convert var to be able to share same code than showOutputField of extrafields
5682
		if (preg_match('/varchar\((\d+)\)/', $type, $reg))
5683
		{
5684
			$type = 'varchar';		// convert varchar(xx) int varchar
5685
			$size = $reg[1];
5686
		}
5687
		elseif (preg_match('/varchar/', $type)) $type = 'varchar';		// convert varchar(xx) int varchar
5688
		if (is_array($val['arrayofkeyval'])) $type='select';
5689
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) $type='link';
5690
5691
		$default=$val['default'];
5692
		$computed=$val['computed'];
5693
		$unique=$val['unique'];
5694
		$required=$val['required'];
5695
		$param=$val['param'];
5696
		if (is_array($val['arrayofkeyval'])) $param['options'] = $val['arrayofkeyval'];
5697
		if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg))
5698
		{
5699
			$type='link';
5700
			$param['options']=array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
5701
		}
5702
		$langfile=$val['langfile'];
5703
		$list=$val['list'];
5704
		$help=$val['help'];
5705
		$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)
5706
5707
		if ($hidden) return '';
5708
5709
		// If field is a computed field, value must become result of compute
5710
		if ($computed)
5711
		{
5712
			// Make the eval of compute string
5713
			//var_dump($computed);
5714
			$value = dol_eval($computed, 1, 0);
5715
		}
5716
5717
		if (empty($showsize))
5718
		{
5719
			if ($type == 'date')
5720
			{
5721
				//$showsize=10;
5722
				$showsize = 'minwidth100imp';
5723
			}
5724
			elseif ($type == 'datetime')
5725
			{
5726
				//$showsize=19;
5727
				$showsize = 'minwidth200imp';
5728
			}
5729
			elseif (in_array($type,array('int','double','price')))
5730
			{
5731
				//$showsize=10;
5732
				$showsize = 'maxwidth75';
5733
			}
5734
			elseif ($type == 'url')
5735
			{
5736
				$showsize='minwidth400';
5737
			}
5738
			elseif ($type == 'boolean')
5739
			{
5740
				$showsize='';
5741
			}
5742
			else
5743
			{
5744
				if (round($size) < 12)
5745
				{
5746
					$showsize = 'minwidth100';
5747
				}
5748
				else if (round($size) <= 48)
5749
				{
5750
					$showsize = 'minwidth200';
5751
				}
5752
				else
5753
				{
5754
					//$showsize=48;
5755
					$showsize = 'minwidth400';
5756
				}
5757
			}
5758
		}
5759
5760
		// Format output value differently according to properties of field
5761
		if ($key == 'ref' && method_exists($this, 'getNomUrl')) $value=$this->getNomUrl(1, '', 0, '', 1);
5762
		elseif ($key == 'status' && method_exists($this, 'getLibStatut')) $value=$this->getLibStatut(3);
5763
		elseif ($type == 'date')
5764
		{
5765
			if(! empty($value)) {
5766
				$value=dol_print_date($value,'day');
5767
			} else {
5768
				$value='';
5769
			}
5770
		}
5771
		elseif ($type == 'datetime')
5772
		{
5773
			if(! empty($value)) {
5774
				$value=dol_print_date($value,'dayhour');
5775
			} else {
5776
				$value='';
5777
			}
5778
		}
5779
		elseif ($type == 'double')
5780
		{
5781
			if (!empty($value)) {
5782
				$value=price($value);
5783
			}
5784
		}
5785
		elseif ($type == 'boolean')
5786
		{
5787
			$checked='';
5788
			if (!empty($value)) {
5789
				$checked=' checked ';
5790
			}
5791
			$value='<input type="checkbox" '.$checked.' '.($moreparam?$moreparam:'').' readonly disabled>';
5792
		}
5793
		elseif ($type == 'mail')
5794
		{
5795
			$value=dol_print_email($value,0,0,0,64,1,1);
5796
		}
5797
		elseif ($type == 'url')
5798
		{
5799
			$value=dol_print_url($value,'_blank',32,1);
5800
		}
5801
		elseif ($type == 'phone')
5802
		{
5803
			$value=dol_print_phone($value, '', 0, 0, '', '&nbsp;', 1);
5804
		}
5805
		elseif ($type == 'price')
5806
		{
5807
			$value=price($value,0,$langs,0,0,-1,$conf->currency);
5808
		}
5809
		elseif ($type == 'select')
5810
		{
5811
			$value=$param['options'][$value];
5812
		}
5813
		elseif ($type == 'sellist')
5814
		{
5815
			$param_list=array_keys($param['options']);
5816
			$InfoFieldList = explode(":", $param_list[0]);
5817
5818
			$selectkey="rowid";
5819
			$keyList='rowid';
5820
5821
			if (count($InfoFieldList)>=3)
5822
			{
5823
				$selectkey = $InfoFieldList[2];
5824
				$keyList=$InfoFieldList[2].' as rowid';
5825
			}
5826
5827
			$fields_label = explode('|',$InfoFieldList[1]);
5828
			if(is_array($fields_label)) {
5829
				$keyList .=', ';
5830
				$keyList .= implode(', ', $fields_label);
5831
			}
5832
5833
			$sql = 'SELECT '.$keyList;
5834
			$sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
5835
			if (strpos($InfoFieldList[4], 'extra')!==false)
5836
			{
5837
				$sql.= ' as main';
5838
			}
5839
			if ($selectkey=='rowid' && empty($value)) {
5840
				$sql.= " WHERE ".$selectkey."=0";
5841
			} elseif ($selectkey=='rowid') {
5842
				$sql.= " WHERE ".$selectkey."=".$this->db->escape($value);
5843
			}else {
5844
				$sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
5845
			}
5846
5847
			//$sql.= ' AND entity = '.$conf->entity;
5848
5849
			dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
5850
			$resql = $this->db->query($sql);
5851
			if ($resql)
5852
			{
5853
				$value='';	// value was used, so now we reste it to use it to build final output
5854
5855
				$obj = $this->db->fetch_object($resql);
5856
5857
				// Several field into label (eq table:code|libelle:rowid)
5858
				$fields_label = explode('|',$InfoFieldList[1]);
5859
5860
				if(is_array($fields_label) && count($fields_label)>1)
5861
				{
5862
					foreach ($fields_label as $field_toshow)
5863
					{
5864
						$translabel='';
5865
						if (!empty($obj->$field_toshow)) {
5866
							$translabel=$langs->trans($obj->$field_toshow);
5867
						}
5868
						if ($translabel!=$field_toshow) {
5869
							$value.=dol_trunc($translabel,18).' ';
5870
						}else {
5871
							$value.=$obj->$field_toshow.' ';
5872
						}
5873
					}
5874
				}
5875
				else
5876
				{
5877
					$translabel='';
5878
					if (!empty($obj->{$InfoFieldList[1]})) {
5879
						$translabel=$langs->trans($obj->{$InfoFieldList[1]});
5880
					}
5881
					if ($translabel!=$obj->{$InfoFieldList[1]}) {
5882
						$value=dol_trunc($translabel,18);
5883
					}else {
5884
						$value=$obj->{$InfoFieldList[1]};
5885
					}
5886
				}
5887
			}
5888
			else dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
5889
		}
5890
		elseif ($type == 'radio')
5891
		{
5892
			$value=$param['options'][$value];
5893
		}
5894
		elseif ($type == 'checkbox')
5895
		{
5896
			$value_arr=explode(',',$value);
5897
			$value='';
5898
			if (is_array($value_arr))
5899
			{
5900
				foreach ($value_arr as $keyval=>$valueval) {
5901
					$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$param['options'][$valueval].'</li>';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$toprint was never initialized. Although not strictly required by PHP, it is generally a good practice to add $toprint = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
5902
				}
5903
			}
5904
			$value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
0 ignored issues
show
Bug introduced by
The variable $toprint does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
5905
		}
5906
		elseif ($type == 'chkbxlst')
5907
		{
5908
			$value_arr = explode(',', $value);
5909
5910
			$param_list = array_keys($param['options']);
5911
			$InfoFieldList = explode(":", $param_list[0]);
5912
5913
			$selectkey = "rowid";
5914
			$keyList = 'rowid';
5915
5916
			if (count($InfoFieldList) >= 3) {
5917
				$selectkey = $InfoFieldList[2];
5918
				$keyList = $InfoFieldList[2] . ' as rowid';
5919
			}
5920
5921
			$fields_label = explode('|', $InfoFieldList[1]);
5922
			if (is_array($fields_label)) {
5923
				$keyList .= ', ';
5924
				$keyList .= implode(', ', $fields_label);
5925
			}
5926
5927
			$sql = 'SELECT ' . $keyList;
5928
			$sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
5929
			if (strpos($InfoFieldList[4], 'extra') !== false) {
5930
				$sql .= ' as main';
5931
			}
5932
			// $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
5933
			// $sql.= ' AND entity = '.$conf->entity;
5934
5935
			dol_syslog(get_class($this) . ':showOutputField:$type=chkbxlst',LOG_DEBUG);
5936
			$resql = $this->db->query($sql);
5937
			if ($resql) {
5938
				$value = ''; // value was used, so now we reste it to use it to build final output
5939
				$toprint=array();
5940
				while ( $obj = $this->db->fetch_object($resql) ) {
5941
5942
					// Several field into label (eq table:code|libelle:rowid)
5943
					$fields_label = explode('|', $InfoFieldList[1]);
5944
					if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5945
						if (is_array($fields_label) && count($fields_label) > 1) {
5946
							foreach ( $fields_label as $field_toshow ) {
5947
								$translabel = '';
5948
								if (! empty($obj->$field_toshow)) {
5949
									$translabel = $langs->trans($obj->$field_toshow);
5950
								}
5951
								if ($translabel != $field_toshow) {
5952
									$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
5953
								} else {
5954
									$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->$field_toshow.'</li>';
5955
								}
5956
							}
5957
						} else {
5958
							$translabel = '';
5959
							if (! empty($obj->{$InfoFieldList[1]})) {
5960
								$translabel = $langs->trans($obj->{$InfoFieldList[1]});
5961
							}
5962
							if ($translabel != $obj->{$InfoFieldList[1]}) {
5963
								$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
5964
							} else {
5965
								$toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->{$InfoFieldList[1]}.'</li>';
5966
							}
5967
						}
5968
					}
5969
				}
5970
				$value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
5971
5972
			} else {
5973
				dol_syslog(get_class($this) . '::showOutputField error ' . $this->db->lasterror(), LOG_WARNING);
5974
			}
5975
		}
5976
		elseif ($type == 'link')
5977
		{
5978
			$out='';
5979
5980
			// only if something to display (perf)
5981
			if ($value)
5982
			{
5983
				$param_list=array_keys($param['options']);				// $param_list='ObjectName:classPath'
5984
5985
				$InfoFieldList = explode(":", $param_list[0]);
5986
				$classname=$InfoFieldList[0];
5987
				$classpath=$InfoFieldList[1];
5988
				if (! empty($classpath))
5989
				{
5990
					dol_include_once($InfoFieldList[1]);
5991
					if ($classname && class_exists($classname))
5992
					{
5993
						$object = new $classname($this->db);
5994
						$object->fetch($value);
5995
						$value=$object->getNomUrl(3);
5996
					}
5997
				}
5998
				else
5999
				{
6000
					dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6001
					return 'Error bad setup of extrafield';
6002
				}
6003
			}
6004
		}
6005
		elseif ($type == 'text' || $type == 'html')
6006
		{
6007
			$value=dol_htmlentitiesbr($value);
6008
		}
6009
		elseif ($type == 'password')
6010
		{
6011
			$value=preg_replace('/./i','*',$value);
6012
		}
6013
		elseif ($type == 'array')
6014
		{
6015
			$value = implode('<br>', $value);
6016
		}
6017
6018
		//print $type.'-'.$size;
6019
		$out=$value;
6020
6021
		return $out;
6022
	}
6023
6024
6025
	/**
6026
	 * Function to show lines of extrafields with output datas
6027
	 *
6028
	 * @param 	Extrafields $extrafields    Extrafield Object
6029
	 * @param 	string      $mode           Show output (view) or input (edit) for extrafield
6030
	 * @param 	array       $params         Optional parameters
6031
	 * @param 	string      $keysuffix      Suffix string to add after name and id of field (can be used to avoid duplicate names)
6032
	 * @param 	string      $keyprefix      Prefix string to add before name and id of field (can be used to avoid duplicate names)
6033
	 * @param	string		$onetrtd		All fields in same tr td
6034
	 * @return 	string
6035
	 */
6036
	function showOptionals($extrafields, $mode='view', $params=null, $keysuffix='', $keyprefix='', $onetrtd=0)
6037
	{
6038
		global $db, $conf, $langs, $action, $form;
6039
6040
		if (! is_object($form)) $form=new Form($db);
6041
6042
		$out = '';
6043
6044
		if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0)
6045
		{
6046
			$out .= "\n";
6047
			$out .= '<!-- showOptionalsInput --> ';
6048
			$out .= "\n";
6049
6050
			$e = 0;
6051
			foreach($extrafields->attributes[$this->table_element]['label'] as $key=>$label)
6052
			{
6053
				$enabled = 1;
6054
				if ($enabled && isset($extrafields->attributes[$this->table_element]['list'][$key]))
6055
				{
6056
					$enabled = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1);
6057
				}
6058
6059
				$perms = 1;
6060
				if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key]))
6061
				{
6062
					$perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1);
6063
				}
6064
6065
				if (($mode == 'create' || $mode == 'edit') && abs($enabled) != 1 && abs($enabled) != 3) continue;	// <> -1 and <> 1 and <> 3 = not visible on forms, only on list
6066
				if (empty($perms)) continue;
6067
6068
				// Load language if required
6069
				if (! empty($extrafields->attributes[$this->table_element]['langfile'][$key])) $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
6070
6071
				if (is_array($params) && count($params)>0) {
6072
					if (array_key_exists('colspan',$params)) {
6073
						$colspan=$params['colspan'];
6074
					}
6075
				}else {
6076
					$colspan='3';
6077
				}
6078
6079
				switch($mode) {
6080
					case "view":
6081
						$value=$this->array_options["options_".$key.$keysuffix];
6082
						break;
6083
					case "edit":
6084
						$getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, 'none');				// GETPOST can get value from GET, POST or setup of default values.
6085
						// GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
6086
						if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix))
6087
						{
6088
							if (is_array($getposttemp)) {
6089
								// $getposttemp is an array but following code expects a comma separated string
6090
								$value = implode(",", $getposttemp);
6091
							} else {
6092
								$value = $getposttemp;
6093
							}
6094
						} else {
6095
							$value = $this->array_options["options_" . $key];			// No GET, no POST, no default value, so we take value of object.
6096
						}
6097
						//var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
6098
						break;
6099
				}
6100
6101
				if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate')
6102
				{
6103
					$out .= $extrafields->showSeparator($key, $this);
6104
				}
6105
				else
6106
				{
6107
					$csstyle='';
6108
					$class=(!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
6109
					if (is_array($params) && count($params)>0) {
6110
						if (array_key_exists('style',$params)) {
6111
							$csstyle=$params['style'];
6112
						}
6113
					}
6114
6115
					// add html5 elements
6116
					$domData  = ' data-element="extrafield"';
6117
					$domData .= ' data-targetelement="'.$this->element.'"';
6118
					$domData .= ' data-targetid="'.$this->id.'"';
6119
6120
					$html_id = !empty($this->id) ? 'extrarow-'.$this->element.'_'.$key.'_'.$this->id : '';
6121
6122
					$out .= '<tr id="'.$html_id.'" '.$csstyle.' class="'.$class.$this->element.'_extras_'.$key.'" '.$domData.' >';
6123
6124
					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0)
6125
					{
6126
						if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) { $colspan='0'; }
6127
					}
6128
6129
					if ($action == 'selectlines') { $colspan++; }
0 ignored issues
show
Bug introduced by
The variable $colspan does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
6130
6131
					// Convert date into timestamp format (value in memory must be a timestamp)
6132
					if (in_array($extrafields->attributes[$this->table_element]['type'][$key],array('date','datetime')))
6133
					{
6134
						$datenotinstring = $this->array_options['options_' . $key];
6135
						if (! is_numeric($this->array_options['options_' . $key]))	// For backward compatibility
6136
						{
6137
							$datenotinstring = $this->db->jdate($datenotinstring);
6138
						}
6139
						$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;
6140
					}
6141
					// Convert float submited string into real php numeric (value in memory must be a php numeric)
6142
					if (in_array($extrafields->attributes[$this->table_element]['type'][$key],array('price','double')))
6143
					{
6144
						$value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?price2num(GETPOST($keyprefix.'options_'.$key.$keysuffix, 'alpha', 3)):$this->array_options['options_'.$key];
6145
					}
6146
6147
					$labeltoshow = $langs->trans($label);
6148
6149
					$out .= '<td class="titlefield';
6150
					if (GETPOST('action','none') == 'create') $out.='create';
6151
					if ($mode != 'view' && ! empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= ' fieldrequired';
6152
					$out .= '">';
6153
					if (! empty($extrafields->attributes[$object->table_element]['help'][$key])) $out .= $form->textwithpicto($labeltoshow, $extrafields->attributes[$object->table_element]['help'][$key]);
0 ignored issues
show
Bug introduced by
The variable $object does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
6154
					else $out .= $labeltoshow;
6155
					$out .= '</td>';
6156
6157
					$html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
6158
					$out .='<td id="'.$html_id.'" class="'.$this->element.'_extras_'.$key.'" '.($colspan?' colspan="'.$colspan.'"':'').'>';
6159
6160
					switch($mode) {
6161
						case "view":
6162
							$out .= $extrafields->showOutputField($key, $value);
0 ignored issues
show
Bug introduced by
The variable $value does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
6163
							break;
6164
						case "edit":
6165
							$out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id);
6166
							break;
6167
					}
6168
6169
					$out .= '</td>';
6170
6171
					if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>';
6172
					else $out .= '</tr>';
6173
					$e++;
6174
				}
6175
			}
6176
			$out .= "\n";
6177
			// Add code to manage list depending on others
6178
			if (! empty($conf->use_javascript_ajax)) {
6179
				$out .= '
6180
				<script type="text/javascript">
6181
				    jQuery(document).ready(function() {
6182
				    	function showOptions(child_list, parent_list)
6183
				    	{
6184
				    		var val = $("select[name=\"options_"+parent_list+"\"]").val();
6185
				    		var parentVal = parent_list + ":" + val;
6186
							if(val > 0) {
6187
					    		$("select[name=\""+child_list+"\"] option[parent]").hide();
6188
					    		$("select[name=\""+child_list+"\"] option[parent=\""+parentVal+"\"]").show();
6189
							} else {
6190
								$("select[name=\""+child_list+"\"] option").show();
6191
							}
6192
				    	}
6193
						function setListDependencies() {
6194
					    	jQuery("select option[parent]").parent().each(function() {
6195
					    		var child_list = $(this).attr("name");
6196
								var parent = $(this).find("option[parent]:first").attr("parent");
6197
								var infos = parent.split(":");
6198
								var parent_list = infos[0];
6199
								$("select[name=\""+parent_list+"\"]").change(function() {
6200
									showOptions(child_list, parent_list);
6201
								});
6202
					    	});
6203
						}
6204
6205
						setListDependencies();
6206
				    });
6207
				</script>'."\n";
6208
				$out .= '<!-- /showOptionalsInput --> '."\n";
6209
			}
6210
		}
6211
		return $out;
6212
	}
6213
6214
6215
	/**
6216
	 * Returns the rights used for this class
6217
	 * @return stdClass
6218
	 */
6219
	public function getRights()
6220
	{
6221
		global $user;
6222
6223
		$element = $this->element;
6224
		if ($element == 'facturerec') $element='facture';
6225
6226
		return $user->rights->{$element};
6227
	}
6228
6229
	/**
6230
	 * Function used to replace a thirdparty id with another one.
6231
	 * This function is meant to be called from replaceThirdparty with the appropiate tables
6232
	 * Column name fk_soc MUST be used to identify thirdparties
6233
	 *
6234
	 * @param  DoliDB 	   $db 			  Database handler
6235
	 * @param  int 		   $origin_id     Old thirdparty id (the thirdparty to delete)
6236
	 * @param  int 		   $dest_id       New thirdparty id (the thirdparty that will received element of the other)
6237
	 * @param  string[]    $tables        Tables that need to be changed
6238
	 * @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)
6239
	 * @return bool						  True if success, False if error
6240
	 */
6241
	public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
6242
	{
6243
		foreach ($tables as $table)
6244
		{
6245
			$sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id;
6246
6247
			if (! $db->query($sql))
6248
			{
6249
				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.
6250
				//$this->errors = $db->lasterror();
6251
				return false;
6252
			}
6253
		}
6254
6255
		return true;
6256
	}
6257
6258
	/**
6259
	 * Get buy price to use for margin calculation. This function is called when buy price is unknown.
6260
	 *	 Set buy price = sell price if ForceBuyingPriceIfNull configured,
6261
	 *   else if calculation MARGIN_TYPE = 'costprice' and costprice is defined, use costprice as buyprice
6262
	 *	 else if calculation MARGIN_TYPE = 'pmp' and pmp is calculated, use pmp as buyprice
6263
	 *	 else set min buy price as buy price
6264
	 *
6265
	 * @param float		$unitPrice		 Product unit price
6266
	 * @param float		$discountPercent Line discount percent
6267
	 * @param int		$fk_product		 Product id
6268
	 * @return	float                    <0 if KO, buyprice if OK
6269
	 */
6270
	public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
6271
	{
6272
		global $conf;
6273
6274
		$buyPrice = 0;
6275
6276
		if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
6277
		{
6278
			$buyPrice = $unitPrice * (1 - $discountPercent / 100);
6279
		}
6280
		else
6281
		{
6282
			// Get cost price for margin calculation
6283
			if (! empty($fk_product))
6284
			{
6285
				if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice')
6286
				{
6287
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6288
					$product = new Product($this->db);
6289
					$result = $product->fetch($fk_product);
6290
					if ($result <= 0)
6291
					{
6292
						$this->errors[] = 'ErrorProductIdDoesNotExists';
6293
						return -1;
6294
					}
6295
					if ($product->cost_price > 0)
6296
					{
6297
						$buyPrice = $product->cost_price;
6298
					}
6299
					else if ($product->pmp > 0)
6300
					{
6301
						$buyPrice = $product->pmp;
6302
					}
6303
				}
6304
				else if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')
6305
				{
6306
					require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6307
					$product = new Product($this->db);
6308
					$result = $product->fetch($fk_product);
6309
					if ($result <= 0)
6310
					{
6311
						$this->errors[] = 'ErrorProductIdDoesNotExists';
6312
						return -1;
6313
					}
6314
					if ($product->pmp > 0)
6315
					{
6316
						$buyPrice = $product->pmp;
6317
					}
6318
				}
6319
6320
				if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1','pmp','costprice')))
6321
				{
6322
					require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6323
					$productFournisseur = new ProductFournisseur($this->db);
6324
					if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0)
6325
					{
6326
						$buyPrice = $productFournisseur->fourn_unitprice;
6327
					}
6328
					else if ($result < 0)
6329
					{
6330
						$this->errors[] = $productFournisseur->error;
6331
						return -2;
6332
					}
6333
				}
6334
			}
6335
		}
6336
		return $buyPrice;
6337
	}
6338
6339
	/**
6340
	 *  Show photos of an object (nbmax maximum), into several columns
6341
	 *
6342
	 *  @param		string	$modulepart		'product', 'ticket', ...
6343
	 *  @param      string	$sdir        	Directory to scan (full absolute path)
6344
	 *  @param      int		$size        	0=original size, 1='small' use thumbnail if possible
6345
	 *  @param      int		$nbmax       	Nombre maximum de photos (0=pas de max)
6346
	 *  @param      int		$nbbyrow     	Number of image per line or -1 to use div. Used only if size=1.
6347
	 * 	@param		int		$showfilename	1=Show filename
6348
	 * 	@param		int		$showaction		1=Show icon with action links (resize, delete)
6349
	 * 	@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.
6350
	 * 	@param		int		$maxWidth		Max width of original image when size='small'
6351
	 *  @param      int     $nolink         Do not add a href link to view enlarged imaged into a new tab
6352
	 *  @param      int     $notitle        Do not add title tag on image
6353
	 *  @param		int		$usesharelink	Use the public shared link of image (if not available, the 'nophoto' image will be shown instead)
6354
	 *  @return     string					Html code to show photo. Number of photos shown is saved in this->nbphoto
6355
	 */
6356
	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)
6357
	{
6358
		global $conf,$user,$langs;
6359
6360
		include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php';
6361
		include_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';
6362
6363
		$sortfield='position_name';
6364
		$sortorder='asc';
6365
6366
		$dir = $sdir . '/';
6367
		$pdir = '/';
6368
		if ($modulepart == 'ticket')
6369
		{
6370
			$dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6371
			$pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6372
		}
6373
		else
6374
		{
6375
			$dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6376
			$pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6377
		}
6378
6379
		// For backward compatibility
6380
		if ($modulepart == 'product' && ! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))
6381
		{
6382
			$dir = $sdir . '/'. get_exdir($this->id,2,0,0,$this,$modulepart) . $this->id ."/photos/";
6383
			$pdir = '/' . get_exdir($this->id,2,0,0,$this,$modulepart) . $this->id ."/photos/";
6384
		}
6385
6386
		// Defined relative dir to DOL_DATA_ROOT
6387
		$relativedir = '';
6388
		if ($dir)
6389
		{
6390
			$relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $dir);
6391
			$relativedir = preg_replace('/^[\\/]/','',$relativedir);
6392
			$relativedir = preg_replace('/[\\/]$/','',$relativedir);
6393
		}
6394
6395
		$dirthumb = $dir.'thumbs/';
6396
		$pdirthumb = $pdir.'thumbs/';
6397
6398
		$return ='<!-- Photo -->'."\n";
6399
		$nbphoto=0;
6400
6401
		$filearray=dol_dir_list($dir,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
6402
6403
		/*if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))    // For backward compatiblity, we scan also old dirs
6404
		 {
6405
		 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
6406
		 $filearray=array_merge($filearray, $filearrayold);
6407
		 }*/
6408
6409
		completeFileArrayWithDatabaseInfo($filearray, $relativedir);
6410
6411
		if (count($filearray))
6412
		{
6413
			if ($sortfield && $sortorder)
6414
			{
6415
				$filearray=dol_sort_array($filearray, $sortfield, $sortorder);
6416
			}
6417
6418
			foreach($filearray as $key => $val)
6419
			{
6420
				$photo='';
6421
				$file = $val['name'];
6422
6423
				//if (! utf8_check($file)) $file=utf8_encode($file);	// To be sure file is stored in UTF8 in memory
6424
6425
				//if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
6426
				if (image_format_supported($file) >= 0)
6427
				{
6428
					$nbphoto++;
6429
					$photo = $file;
6430
					$viewfilename = $file;
6431
6432
					if ($size == 1 || $size == 'small') {   // Format vignette
6433
6434
						// Find name of thumb file
6435
						$photo_vignette=basename(getImageFileNameForSize($dir.$file, '_small'));
6436
						if (! dol_is_file($dirthumb.$photo_vignette)) $photo_vignette='';
6437
6438
						// Get filesize of original file
6439
						$imgarray=dol_getImageSize($dir.$photo);
6440
6441
						if ($nbbyrow > 0)
6442
						{
6443
							if ($nbphoto == 1) $return.= '<table width="100%" valign="top" align="center" border="0" cellpadding="2" cellspacing="2">';
6444
6445
							if ($nbphoto % $nbbyrow == 1) $return.= '<tr align=center valign=middle border=1>';
6446
							$return.= '<td width="'.ceil(100/$nbbyrow).'%" class="photo">';
6447
						}
6448
						else if ($nbbyrow < 0) $return .= '<div class="inline-block">';
6449
6450
						$return.= "\n";
6451
6452
						$relativefile=preg_replace('/^\//', '', $pdir.$photo);
6453
						if (empty($nolink))
6454
						{
6455
							$urladvanced=getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
6456
							if ($urladvanced) $return.='<a href="'.$urladvanced.'">';
6457
							else $return.= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank">';
6458
						}
6459
6460
						// Show image (width height=$maxHeight)
6461
						// Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
6462
						$alt=$langs->transnoentitiesnoconv('File').': '.$relativefile;
6463
						$alt.=' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
6464
						if ($notitle) $alt='';
6465
6466
						if ($usesharelink)
6467
						{
6468
							if ($val['share'])
6469
							{
6470
								if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6471
								{
6472
									$return.= '<!-- Show original file (thumb not yet available with shared links) -->';
6473
									$return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6474
								}
6475
								else {
6476
									$return.= '<!-- Show original file -->';
6477
									$return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6478
								}
6479
							}
6480
							else
6481
							{
6482
								$return.= '<!-- Show nophoto file (because file is not shared) -->';
6483
								$return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
6484
							}
6485
						}
6486
						else
6487
						{
6488
							if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6489
							{
6490
								$return.= '<!-- Show thumb -->';
6491
								$return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdirthumb.$photo_vignette).'" title="'.dol_escape_htmltag($alt).'">';
6492
							}
6493
							else {
6494
								$return.= '<!-- Show original file -->';
6495
								$return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" title="'.dol_escape_htmltag($alt).'">';
6496
							}
6497
						}
6498
6499
						if (empty($nolink)) $return.= '</a>';
6500
						$return.="\n";
6501
6502
						if ($showfilename) $return.= '<br>'.$viewfilename;
6503
						if ($showaction)
6504
						{
6505
							$return.= '<br>';
6506
							// On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
6507
							if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight))
6508
							{
6509
								$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>';
6510
							}
6511
							// Special cas for product
6512
							if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6513
							{
6514
								// Link to resize
6515
								$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; ';
6516
6517
								// Link to delete
6518
								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6519
								$return.= img_delete().'</a>';
6520
							}
6521
						}
6522
						$return.= "\n";
6523
6524
						if ($nbbyrow > 0)
6525
						{
6526
							$return.= '</td>';
6527
							if (($nbphoto % $nbbyrow) == 0) $return.= '</tr>';
6528
						}
6529
						else if ($nbbyrow < 0) $return.='</div>';
6530
					}
6531
6532
					if (empty($size)) {     // Format origine
6533
						$return.= '<img class="photo photowithmargin" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
6534
6535
						if ($showfilename) $return.= '<br>'.$viewfilename;
6536
						if ($showaction)
6537
						{
6538
							// Special case for product
6539
							if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6540
							{
6541
								// Link to resize
6542
								$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; ';
6543
6544
								// Link to delete
6545
								$return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6546
								$return.= img_delete().'</a>';
6547
							}
6548
						}
6549
					}
6550
6551
					// On continue ou on arrete de boucler ?
6552
					if ($nbmax && $nbphoto >= $nbmax) break;
6553
				}
6554
			}
6555
6556
			if ($size==1 || $size=='small')
6557
			{
6558
				if ($nbbyrow > 0)
6559
				{
6560
					// Ferme tableau
6561
					while ($nbphoto % $nbbyrow)
6562
					{
6563
						$return.= '<td width="'.ceil(100/$nbbyrow).'%">&nbsp;</td>';
6564
						$nbphoto++;
6565
					}
6566
6567
					if ($nbphoto) $return.= '</table>';
6568
				}
6569
			}
6570
		}
6571
6572
		$this->nbphoto = $nbphoto;
6573
6574
		return $return;
6575
	}
6576
6577
6578
	/**
6579
	 * Function test if type is array
6580
	 *
6581
	 * @param   array   $info   content informations of field
6582
	 * @return                  bool
6583
	 */
6584
	protected function isArray($info)
6585
	{
6586
		if(is_array($info))
6587
		{
6588
			if(isset($info['type']) && $info['type']=='array') return true;
6589
			else return false;
6590
		}
6591
		else return false;
6592
	}
6593
6594
	/**
6595
	 * Function test if type is null
6596
	 *
6597
	 * @param   array   $info   content informations of field
6598
	 * @return                  bool
6599
	 */
6600
	protected function isNull($info)
6601
	{
6602
		if(is_array($info))
6603
		{
6604
			if(isset($info['type']) && $info['type']=='null') return true;
6605
			else return false;
6606
		}
6607
		else return false;
6608
	}
6609
6610
	/**
6611
	 * Function test if type is date
6612
	 *
6613
	 * @param   array   $info   content informations of field
6614
	 * @return                  bool
6615
	 */
6616
	public function isDate($info)
6617
	{
6618
		if(isset($info['type']) && ($info['type']=='date' || $info['type']=='datetime' || $info['type']=='timestamp')) return true;
6619
		else return false;
6620
	}
6621
6622
	/**
6623
	 * Function test if type is integer
6624
	 *
6625
	 * @param   array   $info   content informations of field
6626
	 * @return                  bool
6627
	 */
6628
	public function isInt($info)
6629
	{
6630
		if(is_array($info))
6631
		{
6632
			if(isset($info['type']) && ($info['type']=='int' || preg_match('/^integer/i',$info['type']) ) ) return true;
6633
			else return false;
6634
		}
6635
		else return false;
6636
	}
6637
6638
	/**
6639
	 * Function test if type is float
6640
	 *
6641
	 * @param   array   $info   content informations of field
6642
	 * @return                  bool
6643
	 */
6644
	public function isFloat($info)
6645
	{
6646
		if(is_array($info))
6647
		{
6648
			if (isset($info['type']) && (preg_match('/^(double|real)/i', $info['type']))) return true;
6649
			else return false;
6650
		}
6651
		else return false;
6652
	}
6653
6654
	/**
6655
	 * Function test if type is text
6656
	 *
6657
	 * @param   array   $info   content informations of field
6658
	 * @return                  bool
6659
	 */
6660
	public function isText($info)
6661
	{
6662
		if(is_array($info))
6663
		{
6664
			if(isset($info['type']) && $info['type']=='text') return true;
6665
			else return false;
6666
		}
6667
		else return false;
6668
	}
6669
6670
	/**
6671
	 * Function test if is indexed
6672
	 *
6673
	 * @param   array   $info   content informations of field
6674
	 * @return                  bool
6675
	 */
6676
	protected function isIndex($info)
6677
	{
6678
		if(is_array($info))
6679
		{
6680
			if(isset($info['index']) && $info['index']==true) return true;
6681
			else return false;
6682
		}
6683
		else return false;
6684
	}
6685
6686
	/**
6687
	 * Function to prepare the values to insert.
6688
	 * Note $this->${field} are set by the page that make the createCommon or the updateCommon.
6689
	 *
6690
	 * @return array
6691
	 */
6692
	protected function setSaveQuery()
6693
	{
6694
		global $conf;
6695
6696
		$queryarray=array();
6697
		foreach ($this->fields as $field=>$info)	// Loop on definition of fields
6698
		{
6699
			// Depending on field type ('datetime', ...)
6700
			if($this->isDate($info))
6701
			{
6702
				if(empty($this->{$field}))
6703
				{
6704
					$queryarray[$field] = null;
6705
				}
6706
				else
6707
				{
6708
					$queryarray[$field] = $this->db->idate($this->{$field});
6709
				}
6710
			}
6711
			else if($this->isArray($info))
6712
			{
6713
				if(! empty($this->{$field})) {
6714
					if(! is_array($this->{$field})) {
6715
						$this->{$field} = array($this->{$field});
6716
					}
6717
					$queryarray[$field] = serialize($this->{$field});
6718
				} else {
6719
					$queryarray[$field] = null;
6720
				}
6721
			}
6722
			else if($this->isInt($info))
6723
			{
6724
				if ($field == 'entity' && is_null($this->{$field})) $queryarray[$field]=$conf->entity;
6725
				else
6726
				{
6727
					$queryarray[$field] = (int) price2num($this->{$field});
6728
					if (empty($queryarray[$field])) $queryarray[$field]=0;		// May be reset to null later if property 'notnull' is -1 for this field.
6729
				}
6730
			}
6731
			else if($this->isFloat($info))
6732
			{
6733
				$queryarray[$field] = (double) price2num($this->{$field});
6734
				if (empty($queryarray[$field])) $queryarray[$field]=0;
6735
			}
6736
			else
6737
			{
6738
				$queryarray[$field] = $this->{$field};
6739
			}
6740
6741
			if ($info['type'] == 'timestamp' && empty($queryarray[$field])) unset($queryarray[$field]);
6742
			if (! empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) $queryarray[$field] = null;
6743
		}
6744
6745
		return $queryarray;
6746
	}
6747
6748
	/**
6749
	 * Function to load data from a SQL pointer into properties of current object $this
6750
	 *
6751
	 * @param   stdClass    $obj    Contain data of object from database
6752
	 */
6753
	protected function setVarsFromFetchObj(&$obj)
6754
	{
6755
		foreach ($this->fields as $field => $info)
6756
		{
6757
			if($this->isDate($info))
6758
			{
6759
				if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
6760
				else $this->{$field} = strtotime($obj->{$field});
6761
			}
6762
			elseif($this->isArray($info))
6763
			{
6764
				if(! empty($obj->{$field})) {
6765
					$this->{$field} = @unserialize($obj->{$field});
6766
					// Hack for data not in UTF8
6767
					if($this->{$field } === false) @unserialize(utf8_decode($obj->{$field}));
6768
				} else {
6769
					$this->{$field} = array();
6770
				}
6771
			}
6772
			elseif($this->isInt($info))
6773
			{
6774
				if ($field == 'rowid') $this->id = (int) $obj->{$field};
6775
				else $this->{$field} = (int) $obj->{$field};
6776
			}
6777
			elseif($this->isFloat($info))
6778
			{
6779
				$this->{$field} = (double) $obj->{$field};
6780
			}
6781
			elseif($this->isNull($info))
6782
			{
6783
				$val = $obj->{$field};
6784
				// zero is not null
6785
				$this->{$field} = (is_null($val) || (empty($val) && $val!==0 && $val!=='0') ? null : $val);
6786
			}
6787
			else
6788
			{
6789
				$this->{$field} = $obj->{$field};
6790
			}
6791
		}
6792
6793
		// If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
6794
		if (! isset($this->fields['ref']) && isset($this->id)) $this->ref = $this->id;
6795
	}
6796
6797
	/**
6798
	 * Function to concat keys of fields
6799
	 *
6800
	 * @return string
6801
	 */
6802
	protected function getFieldList()
6803
	{
6804
		$keys = array_keys($this->fields);
6805
		return implode(',', $keys);
6806
	}
6807
6808
	/**
6809
	 * Add quote to field value if necessary
6810
	 *
6811
	 * @param 	string|int	$value			Value to protect
6812
	 * @param	array		$fieldsentry	Properties of field
6813
	 * @return 	string
6814
	 */
6815
	protected function quote($value, $fieldsentry) {
6816
		if (is_null($value)) return 'NULL';
6817
		else if (preg_match('/^(int|double|real)/i', $fieldsentry['type'])) return $this->db->escape("$value");
6818
		else return "'".$this->db->escape($value)."'";
6819
	}
6820
6821
6822
	/**
6823
	 * Create object into database
6824
	 *
6825
	 * @param  User $user      User that creates
6826
	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
6827
	 * @return int             <0 if KO, Id of created object if OK
6828
	 */
6829
	public function createCommon(User $user, $notrigger = false)
6830
	{
6831
		global $langs;
6832
6833
		$error = 0;
6834
6835
		$now=dol_now();
6836
6837
		$fieldvalues = $this->setSaveQuery();
6838
		if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) $fieldvalues['date_creation']=$this->db->idate($now);
6839
		if (array_key_exists('fk_user_creat', $fieldvalues) && ! ($fieldvalues['fk_user_creat'] > 0)) $fieldvalues['fk_user_creat']=$user->id;
6840
		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
6841
6842
		$keys=array();
6843
		$values = array();
6844
		foreach ($fieldvalues as $k => $v) {
6845
			$keys[$k] = $k;
6846
			$value = $this->fields[$k];
6847
			$values[$k] = $this->quote($v, $value);
6848
		}
6849
6850
		// Clean and check mandatory
6851
		foreach($keys as $key)
6852
		{
6853
			// If field is an implicit foreign key field
6854
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';
6855
			if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';
6856
6857
			//var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
6858
			if ($this->fields[$key]['notnull'] == 1 && ! isset($values[$key]) && is_null($val['default']))
0 ignored issues
show
Bug introduced by
The variable $val does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
6859
			{
6860
				$error++;
6861
				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
6862
			}
6863
6864
			// If field is an implicit foreign key field
6865
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) $values[$key]='null';
6866
			if (! empty($this->fields[$key]['foreignkey']) && empty($values[$key])) $values[$key]='null';
6867
		}
6868
6869
		if ($error) return -1;
6870
6871
		$this->db->begin();
6872
6873
		if (! $error)
6874
		{
6875
			$sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
6876
			$sql.= ' ('.implode( ", ", $keys ).')';
6877
			$sql.= ' VALUES ('.implode( ", ", $values ).')';
6878
6879
			$res = $this->db->query($sql);
6880
			if ($res===false) {
6881
				$error++;
6882
				$this->errors[] = $this->db->lasterror();
6883
			}
6884
		}
6885
6886
		if (! $error)
6887
		{
6888
			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
6889
		}
6890
6891
		// Create extrafields
6892
		if (! $error)
6893
		{
6894
			$result=$this->insertExtraFields();
6895
			if ($result < 0) $error++;
6896
		}
6897
6898
		// Triggers
6899
		if (! $error && ! $notrigger)
6900
		{
6901
			// Call triggers
6902
			$result=$this->call_trigger(strtoupper(get_class($this)).'_CREATE',$user);
6903
			if ($result < 0) { $error++; }
6904
			// End call triggers
6905
		}
6906
6907
		// Commit or rollback
6908
		if ($error) {
6909
			$this->db->rollback();
6910
			return -1;
6911
		} else {
6912
			$this->db->commit();
6913
			return $this->id;
6914
		}
6915
	}
6916
6917
6918
	/**
6919
	 * Load object in memory from the database
6920
	 *
6921
	 * @param	int    $id				Id object
6922
	 * @param	string $ref				Ref
6923
	 * @param	string	$morewhere		More SQL filters (' AND ...')
6924
	 * @return 	int         			<0 if KO, 0 if not found, >0 if OK
6925
	 */
6926
	public function fetchCommon($id, $ref = null, $morewhere = '')
6927
	{
6928
		if (empty($id) && empty($ref) && empty($morewhere)) return -1;
6929
6930
		$sql = 'SELECT '.$this->getFieldList();
6931
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
6932
6933
		if (!empty($id))  $sql.= ' WHERE rowid = '.$id;
6934
		elseif (!empty($ref)) $sql.= " WHERE ref = ".$this->quote($ref, $this->fields['ref']);
6935
		else $sql.=' WHERE 1 = 1';	// usage with empty id and empty ref is very rare
6936
		if ($morewhere)   $sql.= $morewhere;
6937
		$sql.=' LIMIT 1';	// This is a fetch, to be sure to get only one record
6938
6939
		$res = $this->db->query($sql);
6940
		if ($res)
6941
		{
6942
			$obj = $this->db->fetch_object($res);
6943
			if ($obj)
6944
			{
6945
				$this->setVarsFromFetchObj($obj);
6946
				return $this->id;
6947
			}
6948
			else
6949
			{
6950
				return 0;
6951
			}
6952
		}
6953
		else
6954
		{
6955
			$this->error = $this->db->lasterror();
6956
			$this->errors[] = $this->error;
6957
			return -1;
6958
		}
6959
	}
6960
6961
	/**
6962
	 * Update object into database
6963
	 *
6964
	 * @param  User $user      	User that modifies
6965
	 * @param  bool $notrigger 	false=launch triggers after, true=disable triggers
6966
	 * @return int             	<0 if KO, >0 if OK
6967
	 */
6968
	public function updateCommon(User $user, $notrigger = false)
6969
	{
6970
		global $conf, $langs;
6971
6972
		$error = 0;
6973
6974
		$now=dol_now();
6975
6976
		$fieldvalues = $this->setSaveQuery();
6977
		if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) $fieldvalues['date_modification']=$this->db->idate($now);
6978
		if (array_key_exists('fk_user_modif', $fieldvalues) && ! ($fieldvalues['fk_user_modif'] > 0)) $fieldvalues['fk_user_modif']=$user->id;
6979
		unset($fieldvalues['rowid']);	// The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
6980
6981
		$keys=array();
6982
		$values = array();
6983
		foreach ($fieldvalues as $k => $v) {
6984
			$keys[$k] = $k;
6985
			$value = $this->fields[$k];
6986
			$values[$k] = $this->quote($v, $value);
6987
			$tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$tmp was never initialized. Although not strictly required by PHP, it is generally a good practice to add $tmp = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
6988
		}
6989
6990
		// Clean and check mandatory
6991
		foreach($keys as $key)
6992
		{
6993
			if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';		// This is an implicit foreign key field
6994
			if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';					// This is an explicit foreign key field
6995
6996
			//var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
6997
			/*
6998
			if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
6999
			{
7000
				$error++;
7001
				$this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7002
			}*/
7003
		}
7004
7005
		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET '.implode( ',', $tmp ).' WHERE rowid='.$this->id ;
0 ignored issues
show
Bug introduced by
The variable $tmp does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
7006
7007
		$this->db->begin();
7008
		if (! $error)
7009
		{
7010
			$res = $this->db->query($sql);
7011
			if ($res===false)
7012
			{
7013
				$error++;
7014
				$this->errors[] = $this->db->lasterror();
7015
			}
7016
		}
7017
7018
		// Update extrafield
7019
		if (! $error)
7020
		{
7021
			if (empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) // For avoid conflicts if trigger used
7022
			{
7023
				$result=$this->insertExtraFields();
7024
				if ($result < 0)
7025
				{
7026
					$error++;
7027
				}
7028
			}
7029
		}
7030
7031
		// Triggers
7032
		if (! $error && ! $notrigger)
7033
		{
7034
			// Call triggers
7035
			$result=$this->call_trigger(strtoupper(get_class($this)).'_MODIFY',$user);
7036
			if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail
7037
			// End call triggers
7038
		}
7039
7040
		// Commit or rollback
7041
		if ($error) {
7042
			$this->db->rollback();
7043
			return -1;
7044
		} else {
7045
			$this->db->commit();
7046
			return $this->id;
7047
		}
7048
	}
7049
7050
	/**
7051
	 * Delete object in database
7052
	 *
7053
	 * @param User $user       User that deletes
7054
	 * @param bool $notrigger  false=launch triggers after, true=disable triggers
7055
	 * @return int             <0 if KO, >0 if OK
7056
	 */
7057
	public function deleteCommon(User $user, $notrigger = false)
7058
	{
7059
		$error=0;
7060
7061
		$this->db->begin();
7062
7063
		if (! $error) {
7064
			if (! $notrigger) {
7065
				// Call triggers
7066
				$result=$this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
7067
				if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail
7068
				// End call triggers
7069
			}
7070
		}
7071
7072
		if (! $error && ! empty($this->isextrafieldmanaged))
7073
		{
7074
			$sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element."_extrafields";
7075
			$sql.= " WHERE fk_object=" . $this->id;
7076
7077
			$resql = $this->db->query($sql);
7078
			if (! $resql)
7079
			{
7080
				$this->errors[] = $this->db->lasterror();
7081
				$error++;
7082
			}
7083
		}
7084
7085
		if (! $error)
7086
		{
7087
			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
7088
7089
			$res = $this->db->query($sql);
7090
			if($res===false) {
7091
				$error++;
7092
				$this->errors[] = $this->db->lasterror();
7093
			}
7094
		}
7095
7096
		// Commit or rollback
7097
		if ($error) {
7098
			$this->db->rollback();
7099
			return -1;
7100
		} else {
7101
			$this->db->commit();
7102
			return 1;
7103
		}
7104
	}
7105
7106
	/**
7107
	 * Initialise object with example values
7108
	 * Id must be 0 if object instance is a specimen
7109
	 *
7110
	 * @return void
7111
	 */
7112
	public function initAsSpecimenCommon()
7113
	{
7114
		$this->id = 0;
7115
7116
		// TODO...
7117
	}
7118
7119
7120
	/* Part for comments */
7121
7122
	/**
7123
	 * Load comments linked with current task
7124
	 *	@return boolean	1 if ok
7125
	 */
7126
	public function fetchComments()
7127
	{
7128
		require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
7129
7130
		$comment = new Comment($this->db);
7131
		$result=$comment->fetchAllFor($this->element, $this->id);
7132
		if ($result<0) {
7133
			$this->errors=array_merge($this->errors,$comment->errors);
7134
			return -1;
7135
		} else {
7136
			$this->comments = $comment->comments;
7137
		}
7138
		return count($this->comments);
7139
	}
7140
7141
	/**
7142
	 * Return nb comments already posted
7143
	 *
7144
	 * @return int
7145
	 */
7146
	public function getNbComments()
7147
	{
7148
		return count($this->comments);
7149
	}
7150
7151
}
7152