Passed
Branch develop (b426bb)
by
unknown
25:54
created

FactureRec::delete()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 49
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 30
nc 12
nop 3
dl 0
loc 49
rs 8.5066
c 1
b 0
f 0
1
<?php
2
/* Copyright (C) 2003-2005	Rodolphe Quiedeville	<[email protected]>
3
 * Copyright (C) 2004-2019	Laurent Destailleur		<[email protected]>
4
 * Copyright (C) 2009-2012	Regis Houssin			<[email protected]>
5
 * Copyright (C) 2010-2011	Juanjo Menent			<[email protected]>
6
 * Copyright (C) 2012       Cedric Salvador         <[email protected]>
7
 * Copyright (C) 2013       Florian Henry		  	<[email protected]>
8
 * Copyright (C) 2015       Marcos García           <[email protected]>
9
 * Copyright (C) 2017-2020  Frédéric France         <[email protected]>
10
 *
11
 * This program is free software; you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation; either version 3 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
23
 */
24
25
/**
26
 *	\file       htdocs/compta/facture/class/facture-rec.class.php
27
 *	\ingroup    facture
28
 *	\brief      Fichier de la classe des factures recurentes
29
 */
30
31
require_once DOL_DOCUMENT_ROOT.'/core/class/notify.class.php';
32
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
33
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
34
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
35
36
37
/**
38
 *	Class to manage invoice templates
39
 */
40
class FactureRec extends CommonInvoice
41
{
42
	/**
43
	 * @var string ID to identify managed object
44
	 */
45
	public $element = 'facturerec';
46
47
	/**
48
	 * @var string Name of table without prefix where object is stored
49
	 */
50
	public $table_element = 'facture_rec';
51
52
	/**
53
	 * @var int    Name of subtable line
54
	 */
55
	public $table_element_line = 'facturedet_rec';
56
57
	/**
58
	 * @var int Field with ID of parent key if this field has a parent
59
	 */
60
	public $fk_element = 'fk_facture';
61
62
	/**
63
	 * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
64
	 */
65
	public $picto = 'bill';
66
67
	/**
68
	 * @var int Entity
69
	 */
70
	public $entity;
71
72
	/**
73
	 * {@inheritdoc}
74
	 */
75
	protected $table_ref_field = 'titre';
76
77
	public $number;
78
	public $date;
79
	public $remise;
80
	public $tva;
81
	public $total;
82
	public $db_table;
83
	public $propalid;
84
85
	public $date_last_gen;
86
	public $date_when;
87
	public $nb_gen_done;
88
	public $nb_gen_max;
89
90
	/**
91
	 * @var int Frequency
92
	 */
93
	public $frequency;
94
95
    /**
96
	 * @var string Unit frequency
97
	 */
98
	public $unit_frequency;
99
100
	public $rang;
101
	public $special_code;
102
103
	public $usenewprice = 0;
104
105
	public $suspended; // status
106
107
	/**
108
	 *  'type' if the field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password')
109
	 *         Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)"
110
	 *  'label' the translation key.
111
	 *  'enabled' is a condition when the field must be managed.
112
	 *  'position' is the sort order of field.
113
	 *  'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
114
	 *  'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing)
115
	 *  'noteditable' says if field is not editable (1 or 0)
116
	 *  'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created.
117
	 *  'index' if we want an index in database.
118
	 *  'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...).
119
	 *  'searchall' is 1 if we want to search in this field when making a search from the quick search button.
120
	 *  'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8).
121
	 *  'css' is the CSS style to use on field. For example: 'maxwidth200'
122
	 *  'help' is a string visible as a tooltip on field
123
	 *  'showoncombobox' if value of the field must be visible into the label of the combobox that list record
124
	 *  'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code.
125
	 *  'arraykeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel")
126
	 *  'comment' is not used. You can store here any text of your choice. It is not used by application.
127
	 *
128
	 *  Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor.
129
	 */
130
131
	// BEGIN MODULEBUILDER PROPERTIES
132
	/**
133
	 * @var array  Array with all fields and their property. Do not use it as a static var. It may be modified by constructor.
134
	 */
135
	public $fields = array(
136
		'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
137
		'titre' =>array('type'=>'varchar(100)', 'label'=>'Titre', 'enabled'=>1, 'showoncombobox' => 1, 'visible'=>-1, 'position'=>15),
138
		'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>20, 'index'=>1),
139
		'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>25),
140
		'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>30),
141
		//'amount' =>array('type'=>'double(24,8)', 'label'=>'Amount', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>35),
142
		'remise' =>array('type'=>'double', 'label'=>'Remise', 'enabled'=>1, 'visible'=>-1, 'position'=>40),
143
		//'remise_percent' =>array('type'=>'double', 'label'=>'Remise percent', 'enabled'=>1, 'visible'=>-1, 'position'=>45),
144
		//'remise_absolue' =>array('type'=>'double', 'label'=>'Remise absolue', 'enabled'=>1, 'visible'=>-1, 'position'=>50),
145
		'tva' =>array('type'=>'double(24,8)', 'label'=>'Tva', 'enabled'=>1, 'visible'=>-1, 'position'=>55, 'isameasure'=>1),
146
		'localtax1' =>array('type'=>'double(24,8)', 'label'=>'Localtax1', 'enabled'=>1, 'visible'=>-1, 'position'=>60, 'isameasure'=>1),
147
		'localtax2' =>array('type'=>'double(24,8)', 'label'=>'Localtax2', 'enabled'=>1, 'visible'=>-1, 'position'=>65, 'isameasure'=>1),
148
		'total' =>array('type'=>'double(24,8)', 'label'=>'Total', 'enabled'=>1, 'visible'=>-1, 'position'=>70, 'isameasure'=>1),
149
		'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'Total ttc', 'enabled'=>1, 'visible'=>-1, 'position'=>75, 'isameasure'=>1),
150
		'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Fk user author', 'enabled'=>1, 'visible'=>-1, 'position'=>80),
151
		'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label'=>'Fk projet', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
152
		'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'Fk cond reglement', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
153
		'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'Fk mode reglement', 'enabled'=>1, 'visible'=>-1, 'position'=>95),
154
		'date_lim_reglement' =>array('type'=>'date', 'label'=>'Date lim reglement', 'enabled'=>1, 'visible'=>-1, 'position'=>100),
155
		'note_private' =>array('type'=>'text', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>105),
156
		'note_public' =>array('type'=>'text', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>110),
157
		'modelpdf' =>array('type'=>'varchar(255)', 'label'=>'Modelpdf', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
158
		'last_gen' =>array('type'=>'varchar(7)', 'label'=>'Last gen', 'enabled'=>1, 'visible'=>-1, 'position'=>120),
159
		'unit_frequency' =>array('type'=>'varchar(2)', 'label'=>'Unit frequency', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
160
		'date_when' =>array('type'=>'datetime', 'label'=>'Date when', 'enabled'=>1, 'visible'=>-1, 'position'=>130),
161
		'date_last_gen' =>array('type'=>'datetime', 'label'=>'Date last gen', 'enabled'=>1, 'visible'=>-1, 'position'=>135),
162
		'nb_gen_done' =>array('type'=>'integer', 'label'=>'Nb gen done', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
163
		'nb_gen_max' =>array('type'=>'integer', 'label'=>'Nb gen max', 'enabled'=>1, 'visible'=>-1, 'position'=>145),
164
		'frequency' =>array('type'=>'integer', 'label'=>'Frequency', 'enabled'=>1, 'visible'=>-1, 'position'=>150),
165
		'usenewprice' =>array('type'=>'integer', 'label'=>'UseNewPrice', 'enabled'=>1, 'visible'=>0, 'position'=>155),
166
		'revenuestamp' =>array('type'=>'double(24,8)', 'label'=>'RevenueStamp', 'enabled'=>1, 'visible'=>-1, 'position'=>160, 'isameasure'=>1),
167
		'auto_validate' =>array('type'=>'integer', 'label'=>'Auto validate', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
168
		'generate_pdf' =>array('type'=>'integer', 'label'=>'Generate pdf', 'enabled'=>1, 'visible'=>-1, 'position'=>170),
169
		'fk_account' =>array('type'=>'integer', 'label'=>'Fk account', 'enabled'=>1, 'visible'=>-1, 'position'=>175),
170
		'fk_multicurrency' =>array('type'=>'integer', 'label'=>'Fk multicurrency', 'enabled'=>1, 'visible'=>-1, 'position'=>180),
171
		'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'Multicurrency code', 'enabled'=>1, 'visible'=>-1, 'position'=>185),
172
		'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'Multicurrency tx', 'enabled'=>1, 'visible'=>-1, 'position'=>190, 'isameasure'=>1),
173
		'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'Multicurrency total ht', 'enabled'=>1, 'visible'=>-1, 'position'=>195, 'isameasure'=>1),
174
		'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'Multicurrency total tva', 'enabled'=>1, 'visible'=>-1, 'position'=>200, 'isameasure'=>1),
175
		'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'Multicurrency total ttc', 'enabled'=>1, 'visible'=>-1, 'position'=>205, 'isameasure'=>1),
176
		'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>210),
177
		'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>215),
178
		'suspended' =>array('type'=>'integer', 'label'=>'Suspended', 'enabled'=>1, 'visible'=>-1, 'position'=>225),
179
	);
180
	// END MODULEBUILDER PROPERTIES
181
182
	const STATUS_NOTSUSPENDED = 0;
183
	const STATUS_SUSPENDED = 1;
184
185
186
187
	/**
188
	 *	Constructor
189
	 *
190
	 * 	@param		DoliDB		$db		Database handler
191
	 */
192
	public function __construct($db)
193
	{
194
		$this->db = $db;
195
	}
196
197
	/**
198
	 * 	Create a predefined invoice
199
	 *
200
	 * 	@param		User	$user		User object
201
	 * 	@param		int		$facid		Id of source invoice
202
	 *	@return		int					<0 if KO, id of invoice created if OK
203
	 */
204
	public function create($user, $facid)
205
	{
206
		global $conf;
207
208
		$error = 0;
209
		$now = dol_now();
210
211
		// Clean parameters
212
		$this->titre = trim($this->titre); // deprecated
213
		$this->title = trim($this->title);
214
		$this->usenewprice = empty($this->usenewprice) ? 0 : $this->usenewprice;
215
		if (empty($this->suspended)) $this->suspended = 0;
216
217
		// No frequency defined then no next date to execution
218
		if (empty($this->frequency))
219
		{
220
			$this->frequency = 0;
221
			$this->date_when = null;
222
		}
223
224
225
		$this->frequency = abs($this->frequency);
226
		$this->nb_gen_done = 0;
227
		$this->nb_gen_max = empty($this->nb_gen_max) ? 0 : $this->nb_gen_max;
228
		$this->auto_validate = empty($this->auto_validate) ? 0 : $this->auto_validate;
229
		$this->generate_pdf = empty($this->generate_pdf) ? 0 : $this->generate_pdf;
230
231
		$this->db->begin();
232
233
		// Charge facture modele
234
		$facsrc = new Facture($this->db);
235
		$result = $facsrc->fetch($facid);
236
		if ($result > 0)
237
		{
238
			// On positionne en mode brouillon la facture
239
			$this->brouillon = 1;
240
241
			$sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_rec (";
242
			$sql .= "titre";
243
			$sql .= ", fk_soc";
244
			$sql .= ", entity";
245
			$sql .= ", datec";
246
			$sql .= ", amount";
247
			$sql .= ", remise";
248
			$sql .= ", note_private";
249
			$sql .= ", note_public";
250
			$sql .= ", modelpdf";
251
			$sql .= ", fk_user_author";
252
			$sql .= ", fk_projet";
253
			$sql .= ", fk_account";
254
			$sql .= ", fk_cond_reglement";
255
			$sql .= ", fk_mode_reglement";
256
			$sql .= ", usenewprice";
257
			$sql .= ", frequency";
258
			$sql .= ", unit_frequency";
259
			$sql .= ", date_when";
260
			$sql .= ", date_last_gen";
261
			$sql .= ", nb_gen_done";
262
			$sql .= ", nb_gen_max";
263
			$sql .= ", auto_validate";
264
			$sql .= ", generate_pdf";
265
			$sql .= ", fk_multicurrency";
266
			$sql .= ", multicurrency_code";
267
			$sql .= ", multicurrency_tx";
268
			$sql .= ", suspended";
269
			$sql .= ") VALUES (";
270
			$sql .= "'".$this->db->escape($this->titre ? $this->titre : $this->title)."'";
271
			$sql .= ", ".$facsrc->socid;
272
			$sql .= ", ".$conf->entity;
273
			$sql .= ", '".$this->db->idate($now)."'";
274
			$sql .= ", ".(!empty($facsrc->amount) ? $facsrc->amount : '0');
0 ignored issues
show
Bug introduced by
The property amount does not seem to exist on Facture.
Loading history...
275
			$sql .= ", ".(!empty($facsrc->remise) ? $this->remise : '0');
0 ignored issues
show
Bug introduced by
The property remise does not exist on Facture. Did you mean remise_percent?
Loading history...
276
			$sql .= ", ".(!empty($this->note_private) ? ("'".$this->db->escape($this->note_private)."'") : "NULL");
277
			$sql .= ", ".(!empty($this->note_public) ? ("'".$this->db->escape($this->note_public)."'") : "NULL");
278
			$sql .= ", ".(!empty($this->modelpdf) ? ("'".$this->db->escape($this->modelpdf)."'") : "NULL");
279
			$sql .= ", '".$this->db->escape($user->id)."'";
280
			$sql .= ", ".(!empty($facsrc->fk_project) ? "'".$facsrc->fk_project."'" : "null");
281
			$sql .= ", ".(!empty($facsrc->fk_account) ? "'".$facsrc->fk_account."'" : "null");
282
			$sql .= ", ".($facsrc->cond_reglement_id > 0 ? $this->db->escape($facsrc->cond_reglement_id) : "null");
283
			$sql .= ", ".($facsrc->mode_reglement_id > 0 ? $this->db->escape($facsrc->mode_reglement_id) : "null");
284
			$sql .= ", ".$this->usenewprice;
285
			$sql .= ", ".$this->frequency;
286
			$sql .= ", '".$this->db->escape($this->unit_frequency)."'";
287
			$sql .= ", ".(!empty($this->date_when) ? "'".$this->db->idate($this->date_when)."'" : 'NULL');
288
			$sql .= ", ".(!empty($this->date_last_gen) ? "'".$this->db->idate($this->date_last_gen)."'" : 'NULL');
289
			$sql .= ", ".$this->db->escape($this->nb_gen_done);
290
			$sql .= ", ".$this->db->escape($this->nb_gen_max);
291
			$sql .= ", ".$this->db->escape($this->auto_validate);
292
			$sql .= ", ".$this->db->escape($this->generate_pdf);
293
			$sql .= ", ".$this->db->escape($facsrc->fk_multicurrency);
294
			$sql .= ", '".$this->db->escape($facsrc->multicurrency_code)."'";
295
			$sql .= ", ".$this->db->escape($facsrc->multicurrency_tx);
296
			$sql .= ", ".$this->db->escape($this->suspended);
297
			$sql .= ")";
298
299
			if ($this->db->query($sql))
300
			{
301
				$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."facture_rec");
302
303
				// Fields used into addline later
304
				$this->fk_multicurrency = $facsrc->fk_multicurrency;
305
				$this->multicurrency_code = $facsrc->multicurrency_code;
306
				$this->multicurrency_tx = $facsrc->multicurrency_tx;
307
308
				// Add lines
309
				$num = count($facsrc->lines);
310
				for ($i = 0; $i < $num; $i++)
311
				{
312
					$tva_tx = $facsrc->lines[$i]->tva_tx;
313
					if (!empty($facsrc->lines[$i]->vat_src_code) && !preg_match('/\(/', $tva_tx)) $tva_tx .= ' ('.$facsrc->lines[$i]->vat_src_code.')';
314
315
                    $result_insert = $this->addline(
316
                        $facsrc->lines[$i]->desc,
317
                        $facsrc->lines[$i]->subprice,
318
                        $facsrc->lines[$i]->qty,
319
						$tva_tx,
320
                        $facsrc->lines[$i]->localtax1_tx,
321
                        $facsrc->lines[$i]->localtax2_tx,
322
                        $facsrc->lines[$i]->fk_product,
323
                        $facsrc->lines[$i]->remise_percent,
324
                        'HT',
325
						$facsrc->lines[$i]->info_bits,
326
                        '',
327
                        0,
328
                        $facsrc->lines[$i]->product_type,
329
                        $facsrc->lines[$i]->rang,
330
                        $facsrc->lines[$i]->special_code,
331
                    	$facsrc->lines[$i]->label,
1 ignored issue
show
Deprecated Code introduced by
The property FactureLigne::$label has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

331
                    	/** @scrutinizer ignore-deprecated */ $facsrc->lines[$i]->label,
Loading history...
332
						$facsrc->lines[$i]->fk_unit,
333
						$facsrc->lines[$i]->multicurrency_subprice
334
                    );
335
336
					if ($result_insert < 0)
337
					{
338
						$error++;
339
					}
340
					else {
341
					    $objectline = new FactureLigneRec($this->db);
342
					    if ($objectline->fetch($result_insert))
343
					    {
344
					        // Extrafields
345
					        if (method_exists($facsrc->lines[$i], 'fetch_optionals')) {
346
					            $facsrc->lines[$i]->fetch_optionals($facsrc->lines[$i]->rowid);
1 ignored issue
show
Deprecated Code introduced by
The property CommonObjectLine::$rowid has been deprecated: Try to use id property as possible (even if field into database is still rowid) ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

346
					            $facsrc->lines[$i]->fetch_optionals(/** @scrutinizer ignore-deprecated */ $facsrc->lines[$i]->rowid);

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
347
					            $objectline->array_options = $facsrc->lines[$i]->array_options;
348
					        }
349
350
					        $result = $objectline->insertExtraFields();
351
					        if ($result < 0)
352
					        {
353
					            $error++;
354
					        }
355
					    }
356
					}
357
				}
358
359
				if (!empty($this->linkedObjectsIds) && empty($this->linked_objects))	// To use new linkedObjectsIds instead of old linked_objects
360
				{
361
					$this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
362
				}
363
364
				// Add object linked
365
				if (!$error && $this->id && is_array($this->linked_objects) && !empty($this->linked_objects))
366
				{
367
					foreach ($this->linked_objects as $origin => $tmp_origin_id)
368
					{
369
					    if (is_array($tmp_origin_id))       // New behaviour, if linked_object can have several links per type, so is something like array('contract'=>array(id1, id2, ...))
370
					    {
371
					        foreach ($tmp_origin_id as $origin_id)
372
					        {
373
					            $ret = $this->add_object_linked($origin, $origin_id);
374
					            if (!$ret)
375
					            {
376
					                $this->error = $this->db->lasterror();
377
					                $error++;
378
					            }
379
					        }
380
					    }
381
					    else                                // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
382
					    {
383
					        $origin_id = $tmp_origin_id;
384
	    					$ret = $this->add_object_linked($origin, $origin_id);
385
	    					if (!$ret)
386
	    					{
387
	    						$this->error = $this->db->lasterror();
388
	    						$error++;
389
	    					}
390
					    }
391
					}
392
				}
393
394
				if ($error)
395
				{
396
					$this->db->rollback();
397
				}
398
				else
399
				{
400
					$this->db->commit();
401
					return $this->id;
402
				}
403
			}
404
			else
405
			{
406
			    $this->error = $this->db->lasterror();
407
				$this->db->rollback();
408
				return -2;
409
			}
410
		}
411
		else
412
		{
413
			$this->db->rollback();
414
			return -1;
415
		}
416
	}
417
418
419
	/**
420
	 * 	Update a line to invoice_rec.
421
	 *
422
	 *  @param		User	$user					User
423
	 *  @param		int		$notrigger				No trigger
424
	 *	@return    	int             				<0 if KO, Id of line if OK
425
	 */
426
	public function update(User $user, $notrigger = 0)
427
	{
428
	    global $conf;
429
430
        $error = 0;
431
432
	    $sql = "UPDATE ".MAIN_DB_PREFIX."facture_rec SET";
433
	    $sql .= " fk_soc = ".$this->fk_soc;
434
        // TODO Add missing fields
435
	    $sql .= " WHERE rowid = ".$this->id;
436
437
	    dol_syslog(get_class($this)."::update", LOG_DEBUG);
438
	    $resql = $this->db->query($sql);
439
	    if ($resql)
440
	    {
441
	        if (!$error)
442
	        {
443
	            $result = $this->insertExtraFields();
444
	            if ($result < 0)
445
	            {
446
	                $error++;
447
	            }
448
	        }
449
450
	        if (!$error && !$notrigger)
451
	        {
452
	            // Call trigger
453
	            $result = $this->call_trigger('BILLREC_UPDATE', $user);
454
	            if ($result < 0)
455
	            {
456
	                $this->db->rollback();
457
	                return -2;
458
	            }
459
	            // End call triggers
460
	        }
461
	        $this->db->commit();
462
	        return 1;
463
	    }
464
	    else
465
	    {
466
	        $this->error = $this->db->lasterror();
467
	        $this->db->rollback();
468
	        return -2;
469
	    }
470
	}
471
472
	/**
473
	 *	Load object and lines
474
	 *
475
	 *	@param      int		$rowid       	Id of object to load
476
	 * 	@param		string	$ref			Reference of recurring invoice
477
	 * 	@param		string	$ref_ext		External reference of invoice
478
	 *	@return     int         			>0 if OK, <0 if KO, 0 if not found
479
	 */
480
	public function fetch($rowid, $ref = '', $ref_ext = '')
481
	{
482
		$sql = 'SELECT f.rowid, f.entity, f.titre as title, f.suspended, f.fk_soc, f.tva, f.localtax1, f.localtax2, f.total, f.total_ttc';
483
		$sql .= ', f.remise_percent, f.remise_absolue, f.remise';
484
		$sql .= ', f.date_lim_reglement as dlr';
485
		$sql .= ', f.note_private, f.note_public, f.fk_user_author';
486
        $sql .= ', f.modelpdf';
487
		$sql .= ', f.fk_mode_reglement, f.fk_cond_reglement, f.fk_projet as fk_project';
488
		$sql .= ', f.fk_account';
489
		$sql .= ', f.frequency, f.unit_frequency, f.date_when, f.date_last_gen, f.nb_gen_done, f.nb_gen_max, f.usenewprice, f.auto_validate';
490
        $sql .= ', f.generate_pdf';
491
        $sql .= ", f.fk_multicurrency, f.multicurrency_code, f.multicurrency_tx, f.multicurrency_total_ht, f.multicurrency_total_tva, f.multicurrency_total_ttc";
492
        $sql .= ', p.code as mode_reglement_code, p.libelle as mode_reglement_libelle';
493
		$sql .= ', c.code as cond_reglement_code, c.libelle as cond_reglement_libelle, c.libelle_facture as cond_reglement_libelle_doc';
494
		//$sql.= ', el.fk_source';
495
		$sql .= ' FROM '.MAIN_DB_PREFIX.'facture_rec as f';
496
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_payment_term as c ON f.fk_cond_reglement = c.rowid';
497
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as p ON f.fk_mode_reglement = p.id';
498
		//$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."element_element as el ON el.fk_target = f.rowid AND el.targettype = 'facture'";
499
		$sql .= ' WHERE f.entity IN ('.getEntity('invoice').')';
500
		if ($rowid) $sql .= ' AND f.rowid='.$rowid;
501
		elseif ($ref) $sql .= " AND f.titre='".$this->db->escape($ref)."'";
502
		/* This field are not used for template invoice
503
		if ($ref_ext) $sql.= " AND f.ref_ext='".$this->db->escape($ref_ext)."'";
504
		if ($ref_int) $sql.= " AND f.ref_int='".$this->db->escape($ref_int)."'";
505
		*/
506
507
		$result = $this->db->query($sql);
508
		if ($result)
509
		{
510
			if ($this->db->num_rows($result))
511
			{
512
				$obj = $this->db->fetch_object($result);
513
514
				$this->id                     = $obj->rowid;
515
				$this->entity                 = $obj->entity;
516
				$this->titre                  = $obj->title; // deprecated
517
				$this->title                  = $obj->title;
518
				$this->ref                    = $obj->title;
519
				$this->ref_client             = $obj->ref_client;
520
				$this->suspended              = $obj->suspended;
521
				$this->type                   = $obj->type;
522
				$this->datep                  = $obj->dp;
523
				$this->date                   = $obj->df;
524
				$this->remise_percent         = $obj->remise_percent;
525
				$this->remise_absolue         = $obj->remise_absolue;
526
				$this->remise                 = $obj->remise;
527
				$this->total_ht               = $obj->total;
528
				$this->total_tva              = $obj->tva;
529
				$this->total_localtax1        = $obj->localtax1;
530
				$this->total_localtax2        = $obj->localtax2;
531
				$this->total_ttc              = $obj->total_ttc;
532
				$this->paye                   = $obj->paye;
533
				$this->close_code             = $obj->close_code;
534
				$this->close_note             = $obj->close_note;
535
				$this->socid                  = $obj->fk_soc;
536
				$this->date_lim_reglement     = $this->db->jdate($obj->dlr);
537
				$this->mode_reglement_id      = $obj->fk_mode_reglement;
538
				$this->mode_reglement_code    = $obj->mode_reglement_code;
539
				$this->mode_reglement         = $obj->mode_reglement_libelle;
540
				$this->cond_reglement_id      = $obj->fk_cond_reglement;
541
				$this->cond_reglement_code    = $obj->cond_reglement_code;
542
				$this->cond_reglement         = $obj->cond_reglement_libelle;
543
				$this->cond_reglement_doc     = $obj->cond_reglement_libelle_doc;
544
				$this->fk_project             = $obj->fk_project;
545
				$this->fk_account             = $obj->fk_account;
546
				$this->fk_facture_source      = $obj->fk_facture_source;
547
				$this->note_private           = $obj->note_private;
548
				$this->note_public            = $obj->note_public;
549
				$this->user_author            = $obj->fk_user_author;
550
				$this->modelpdf               = $obj->modelpdf;
551
				$this->rang = $obj->rang;
552
				$this->special_code = $obj->special_code;
553
				$this->frequency			  = $obj->frequency;
554
				$this->unit_frequency = $obj->unit_frequency;
555
				$this->date_when			  = $this->db->jdate($obj->date_when);
556
				$this->date_last_gen = $this->db->jdate($obj->date_last_gen);
557
				$this->nb_gen_done			  = $obj->nb_gen_done;
558
				$this->nb_gen_max = $obj->nb_gen_max;
559
				$this->usenewprice			  = $obj->usenewprice;
560
				$this->auto_validate = $obj->auto_validate;
561
				$this->generate_pdf = $obj->generate_pdf;
562
563
				// Multicurrency
564
				$this->fk_multicurrency 		= $obj->fk_multicurrency;
565
				$this->multicurrency_code = $obj->multicurrency_code;
566
				$this->multicurrency_tx 		= $obj->multicurrency_tx;
567
				$this->multicurrency_total_ht = $obj->multicurrency_total_ht;
568
				$this->multicurrency_total_tva 	= $obj->multicurrency_total_tva;
569
				$this->multicurrency_total_ttc 	= $obj->multicurrency_total_ttc;
570
571
				if ($this->statut == self::STATUS_DRAFT)	$this->brouillon = 1;
572
573
				// Retreive all extrafield
574
				// fetch optionals attributes and labels
575
				$this->fetch_optionals();
576
577
				/*
578
				 * Lines
579
				 */
580
				$result = $this->fetch_lines();
581
				if ($result < 0)
582
				{
583
					$this->error = $this->db->lasterror();
584
					return -3;
585
				}
586
				return 1;
587
			}
588
			else
589
			{
590
				$this->error = 'Bill with id '.$rowid.' or ref '.$ref.' not found sql='.$sql;
591
				dol_syslog('Facture::Fetch Error '.$this->error, LOG_ERR);
592
				return -2;
593
			}
594
		}
595
		else
596
		{
597
			$this->error = $this->db->error();
598
			return -1;
599
		}
600
	}
601
602
603
	/**
604
	 * 	Create an array of invoice lines
605
	 *
606
	 * 	@return int		>0 if OK, <0 if KO
607
	 */
608
	public function getLinesArray()
609
	{
610
	    return $this->fetch_lines();
611
	}
612
613
614
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
615
	/**
616
	 *	Get lines of template invoices into this->lines
617
	 *
618
	 *  @return     int         1 if OK, < 0 if KO
619
     */
620
	public function fetch_lines()
621
	{
622
		global $extrafields;
623
624
        // phpcs:enable
625
		$this->lines = array();
626
627
		// Retreive all extrafield for line
628
		// fetch optionals attributes and labels
629
		if (!is_object($extrafields))
630
		{
631
			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
632
			$extrafields = new ExtraFields($this->db);
633
		}
634
		$extrafields->fetch_name_optionals_label($this->table_element_line, true);
635
636
		$sql = 'SELECT l.rowid, l.fk_product, l.product_type, l.label as custom_label, l.description, l.product_type, l.price, l.qty, l.vat_src_code, l.tva_tx, ';
637
		$sql .= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.remise, l.remise_percent, l.subprice,';
638
		$sql .= ' l.info_bits, l.date_start_fill, l.date_end_fill, l.total_ht, l.total_tva, l.total_ttc, l.fk_product_fournisseur_price as fk_fournprice, l.buy_price_ht as pa_ht,';
639
		//$sql.= ' l.situation_percent, l.fk_prev_id,';
640
		//$sql.= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.remise_percent, l.fk_remise_except, l.subprice,';
641
		$sql .= ' l.rang, l.special_code,';
642
		//$sql.= ' l.info_bits, l.total_ht, l.total_tva, l.total_localtax1, l.total_localtax2, l.total_ttc, l.fk_code_ventilation, l.fk_product_fournisseur_price as fk_fournprice, l.buy_price_ht as pa_ht,';
643
		$sql .= ' l.fk_unit, l.fk_contract_line,';
644
		$sql .= ' l.fk_multicurrency, l.multicurrency_code, l.multicurrency_subprice, l.multicurrency_total_ht, l.multicurrency_total_tva, l.multicurrency_total_ttc,';
645
		$sql .= ' p.ref as product_ref, p.fk_product_type as fk_product_type, p.label as product_label, p.description as product_desc';
646
		$sql .= ' FROM '.MAIN_DB_PREFIX.'facturedet_rec as l';
647
		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON l.fk_product = p.rowid';
648
		$sql .= ' WHERE l.fk_facture = '.$this->id;
649
		$sql .= ' ORDER BY l.rang';
650
651
		dol_syslog('FactureRec::fetch_lines', LOG_DEBUG);
652
		$result = $this->db->query($sql);
653
		if ($result)
654
		{
655
			$num = $this->db->num_rows($result);
656
			$i = 0;
657
			while ($i < $num)
658
			{
659
				$objp = $this->db->fetch_object($result);
660
				$line = new FactureLigneRec($this->db);
661
662
				$line->id = $objp->rowid;
663
				$line->rowid = $objp->rowid;
664
				$line->desc             = $objp->description; // Description line
665
				$line->description      = $objp->description; // Description line
666
				$line->product_type     = $objp->product_type; // Type of line
667
				$line->ref              = $objp->product_ref; // Ref product
668
				$line->product_ref      = $objp->product_ref; // Ref product
669
				$line->libelle          = $objp->product_label; // deprecated
670
				$line->product_label = $objp->product_label; // Label product
671
				$line->product_desc     = $objp->product_desc; // Description product
672
				$line->fk_product_type  = $objp->fk_product_type; // Type of product
673
				$line->qty              = $objp->qty;
674
				$line->subprice         = $objp->subprice;
675
676
				$line->label            = $objp->custom_label; // @deprecated
677
678
				$line->vat_src_code     = $objp->vat_src_code;
679
				$line->tva_tx           = $objp->tva_tx;
680
				$line->localtax1_tx     = $objp->localtax1_tx;
681
				$line->localtax2_tx     = $objp->localtax2_tx;
682
				$line->localtax1_type   = $objp->localtax1_type;
683
				$line->localtax2_type   = $objp->localtax2_type;
684
				$line->remise_percent   = $objp->remise_percent;
685
				$line->fk_remise_except = $objp->fk_remise_except;
686
				$line->fk_product       = $objp->fk_product;
687
				$line->date_start_fill  = $objp->date_start_fill;
688
				$line->date_end_fill    = $objp->date_end_fill;
689
				$line->info_bits        = $objp->info_bits;
690
				$line->total_ht         = $objp->total_ht;
691
				$line->total_tva        = $objp->total_tva;
692
				$line->total_ttc        = $objp->total_ttc;
693
				$line->code_ventilation = $objp->fk_code_ventilation;
694
				$line->fk_fournprice = $objp->fk_fournprice;
695
				$marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
696
				$line->pa_ht = $marginInfos[0];
697
				$line->marge_tx			= $marginInfos[1];
698
				$line->marque_tx		= $marginInfos[2];
699
				$line->rang = $objp->rang;
700
				$line->special_code = $objp->special_code;
701
				$line->fk_unit = $objp->fk_unit;
702
                $line->fk_contract_line = $objp->fk_contract_line;
703
704
				// Ne plus utiliser
705
				$line->price            = $objp->price;
706
				$line->remise           = $objp->remise;
707
708
				$line->fetch_optionals();
709
710
				// Multicurrency
711
				$line->fk_multicurrency = $objp->fk_multicurrency;
712
				$line->multicurrency_code = $objp->multicurrency_code;
713
				$line->multicurrency_subprice 	= $objp->multicurrency_subprice;
714
				$line->multicurrency_total_ht 	= $objp->multicurrency_total_ht;
715
				$line->multicurrency_total_tva 	= $objp->multicurrency_total_tva;
716
				$line->multicurrency_total_ttc 	= $objp->multicurrency_total_ttc;
717
718
				$this->lines[$i] = $line;
719
720
				$i++;
721
			}
722
723
			$this->db->free($result);
724
			return 1;
725
		}
726
		else
727
		{
728
			$this->error = $this->db->lasterror();
729
			return -3;
730
		}
731
	}
732
733
734
	/**
735
	 * 	Delete template invoice
736
	 *
737
	 *	@param     	User	$user          	User that delete.
738
	 *	@param		int		$notrigger		1=Does not execute triggers, 0= execute triggers
739
	 *	@param		int		$idwarehouse	Id warehouse to use for stock change.
740
	 *	@return		int						<0 if KO, >0 if OK
741
	 */
742
	public function delete(User $user, $notrigger = 0, $idwarehouse = -1)
743
	{
744
	    $rowid = $this->id;
745
746
	    dol_syslog(get_class($this)."::delete rowid=".$rowid, LOG_DEBUG);
747
748
        $error = 0;
749
		$this->db->begin();
750
751
		$main = MAIN_DB_PREFIX . 'facturedet_rec';
752
        $ef = $main . "_extrafields";
753
        $sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_facture = $rowid)";
754
        dol_syslog($sqlef);
755
		$sql = "DELETE FROM ".MAIN_DB_PREFIX."facturedet_rec WHERE fk_facture = ".$rowid;
756
		dol_syslog($sql);
757
		if ($this->db->query($sqlef) && $this->db->query($sql))
758
		{
759
			$sql = "DELETE FROM ".MAIN_DB_PREFIX."facture_rec WHERE rowid = ".$rowid;
760
			dol_syslog($sql);
761
			if ($this->db->query($sql))
762
			{
763
				// Delete linked object
764
				$res = $this->deleteObjectLinked();
765
				if ($res < 0) $error = -3;
766
				// Delete extrafields
767
                $res = $this->deleteExtraFields();
768
                if ($res < 0) $error = -4;
769
			}
770
			else
771
			{
772
				$this->error = $this->db->lasterror();
773
				$error = -1;
774
			}
775
		}
776
		else
777
		{
778
			$this->error = $this->db->lasterror();
779
			$error = -2;
780
		}
781
782
		if (!$error)
783
		{
784
		    $this->db->commit();
785
		    return 1;
786
		}
787
		else
788
		{
789
	        $this->db->rollback();
790
	        return $error;
791
		}
792
	}
793
794
795
	/**
796
	 * 	Add a line to invoice
797
	 *
798
     *	@param    	string		$desc            	Description de la ligne
799
     *	@param    	double		$pu_ht              Prix unitaire HT (> 0 even for credit note)
800
     *	@param    	double		$qty             	Quantite
801
     *	@param    	double		$txtva           	Taux de tva force, sinon -1
802
	 * 	@param		double		$txlocaltax1		Local tax 1 rate (deprecated)
803
	 *  @param		double		$txlocaltax2		Local tax 2 rate (deprecated)
804
     *	@param    	int			$fk_product      	Product/Service ID predefined
805
     *	@param    	double		$remise_percent  	Percentage discount of the line
806
     *	@param		string		$price_base_type	HT or TTC
807
     *	@param    	int			$info_bits			VAT npr or not ?
808
     *	@param    	int			$fk_remise_except	Id remise
809
     *	@param    	double		$pu_ttc             Prix unitaire TTC (> 0 even for credit note)
810
     *	@param		int			$type				Type of line (0=product, 1=service)
811
     *	@param      int			$rang               Position of line
812
     *	@param		int			$special_code		Special code
813
     *	@param		string		$label				Label of the line
814
     *	@param		string		$fk_unit			Unit
815
	 * 	@param		double		$pu_ht_devise		Unit price in currency
816
	 *  @param		int			$date_start_fill	1=Flag to fill start date when generating invoice
817
	 *  @param		int			$date_end_fill		1=Flag to fill end date when generating invoice
818
	 * 	@param		int			$fk_fournprice		Supplier price id (to calculate margin) or ''
819
	 * 	@param		int			$pa_ht				Buying price of line (to calculate margin) or ''
820
     *	@return    	int             				<0 if KO, Id of line if OK
821
	 */
822
	public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $fk_product = 0, $remise_percent = 0, $price_base_type = 'HT', $info_bits = 0, $fk_remise_except = '', $pu_ttc = 0, $type = 0, $rang = -1, $special_code = 0, $label = '', $fk_unit = null, $pu_ht_devise = 0, $date_start_fill = 0, $date_end_fill = 0, $fk_fournprice = null, $pa_ht = 0)
823
	{
824
	    global $mysoc;
825
826
		$facid = $this->id;
827
828
		dol_syslog(get_class($this)."::addline facid=$facid,desc=$desc,pu_ht=$pu_ht,qty=$qty,txtva=$txtva,txlocaltax1=$txlocaltax1,txlocaltax2=$txlocaltax2,fk_product=$fk_product,remise_percent=$remise_percent,info_bits=$info_bits,fk_remise_except=$fk_remise_except,price_base_type=$price_base_type,pu_ttc=$pu_ttc,type=$type,fk_unit=$fk_unit,pu_ht_devise=$pu_ht_devise,date_start_fill=$date_start_fill,date_end_fill=$date_end_fill,pa_ht=$pa_ht", LOG_DEBUG);
829
		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
830
831
		// Check parameters
832
		if ($type < 0) return -1;
833
834
		$localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
835
836
		// Clean vat code
837
		$vat_src_code = '';
838
		if (preg_match('/\((.*)\)/', $txtva, $reg))
839
		{
840
			$vat_src_code = $reg[1];
841
			$txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
842
		}
843
844
		if ($this->brouillon)
845
		{
846
			// Clean parameters
847
			$remise_percent = price2num($remise_percent);
848
			if (empty($remise_percent)) $remise_percent = 0;
849
			$qty = price2num($qty);
850
			$pu_ht = price2num($pu_ht);
851
			$pu_ttc = price2num($pu_ttc);
852
			$txtva = price2num($txtva);
853
			$txlocaltax1 = price2num($txlocaltax1);
854
			$txlocaltax2 = price2num($txlocaltax2);
855
			if (empty($txtva)) $txtva = 0;
856
			if (empty($txlocaltax1)) $txlocaltax1 = 0;
857
			if (empty($txlocaltax2)) $txlocaltax2 = 0;
858
			if (empty($info_bits)) $info_bits = 0;
859
860
			if ($price_base_type == 'HT')
861
			{
862
				$pu = $pu_ht;
863
			}
864
			else
865
			{
866
				$pu = $pu_ttc;
867
			}
868
869
			// Calcul du total TTC et de la TVA pour la ligne a partir de
870
			// qty, pu, remise_percent et txtva
871
			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
872
			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
873
874
			$tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
875
			$total_ht  = $tabprice[0];
876
			$total_tva = $tabprice[1];
877
			$total_ttc = $tabprice[2];
878
			$total_localtax1 = $tabprice[9];
879
			$total_localtax2 = $tabprice[10];
880
			$pu_ht = $tabprice[3];
881
882
			// MultiCurrency
883
			$multicurrency_total_ht  = $tabprice[16];
884
			$multicurrency_total_tva = $tabprice[17];
885
			$multicurrency_total_ttc = $tabprice[18];
886
			$pu_ht_devise = $tabprice[19];
887
888
			$product_type = $type;
889
			if ($fk_product)
890
			{
891
				$product = new Product($this->db);
892
				$result = $product->fetch($fk_product);
893
				$product_type = $product->type;
894
			}
895
896
			$sql = "INSERT INTO ".MAIN_DB_PREFIX."facturedet_rec (";
897
			$sql .= "fk_facture";
898
			$sql .= ", label";
899
			$sql .= ", description";
900
			$sql .= ", price";
901
			$sql .= ", qty";
902
			$sql .= ", tva_tx";
903
			$sql .= ", vat_src_code";
904
			$sql .= ", localtax1_tx";
905
			$sql .= ", localtax1_type";
906
			$sql .= ", localtax2_tx";
907
			$sql .= ", localtax2_type";
908
			$sql .= ", fk_product";
909
			$sql .= ", product_type";
910
			$sql .= ", remise_percent";
911
			$sql .= ", subprice";
912
			$sql .= ", remise";
913
			$sql .= ", total_ht";
914
			$sql .= ", total_tva";
915
			$sql .= ", total_localtax1";
916
			$sql .= ", total_localtax2";
917
			$sql .= ", total_ttc";
918
			$sql .= ", date_start_fill";
919
			$sql .= ", date_end_fill";
920
			$sql .= ", fk_product_fournisseur_price";
921
			$sql .= ", buy_price_ht";
922
			$sql .= ", info_bits";
923
			$sql .= ", rang";
924
			$sql .= ", special_code";
925
			$sql .= ", fk_unit";
926
			$sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
927
			$sql .= ") VALUES (";
928
			$sql .= "'".$facid."'";
929
			$sql .= ", ".(!empty($label) ? "'".$this->db->escape($label)."'" : "null");
930
			$sql .= ", '".$this->db->escape($desc)."'";
931
			$sql .= ", ".price2num($pu_ht);
932
			$sql .= ", ".price2num($qty);
933
			$sql .= ", ".price2num($txtva);
934
			$sql .= ", '".$this->db->escape($vat_src_code)."'";
935
			$sql .= ", ".price2num($txlocaltax1);
936
			$sql .= ", '".$this->db->escape($localtaxes_type[0])."'";
937
			$sql .= ", ".price2num($txlocaltax2);
938
			$sql .= ", '".$this->db->escape($localtaxes_type[2])."'";
939
			$sql .= ", ".(!empty($fk_product) ? "'".$fk_product."'" : "null");
940
			$sql .= ", ".$product_type;
941
			$sql .= ", ".price2num($remise_percent);
942
			$sql .= ", ".price2num($pu_ht);
943
			$sql .= ", null";
944
			$sql .= ", ".price2num($total_ht);
945
			$sql .= ", ".price2num($total_tva);
946
			$sql .= ", ".price2num($total_localtax1);
947
			$sql .= ", ".price2num($total_localtax2);
948
			$sql .= ", ".price2num($total_ttc);
949
			$sql .= ", ".(int) $date_start_fill;
950
			$sql .= ", ".(int) $date_end_fill;
951
			$sql .= ", ".($fk_fournprice > 0 ? $fk_fournprice : 'null');
952
			$sql .= ", ".($pa_ht ? price2num($pa_ht) : 0);
953
			$sql .= ", ".$info_bits;
954
			$sql .= ", ".$rang;
955
			$sql .= ", ".$special_code;
956
			$sql .= ", ".($fk_unit ? "'".$this->db->escape($fk_unit)."'" : "null");
957
			$sql .= ", ".(int) $this->fk_multicurrency;
958
			$sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
959
			$sql .= ", ".price2num($pu_ht_devise);
960
			$sql .= ", ".price2num($multicurrency_total_ht);
961
			$sql .= ", ".price2num($multicurrency_total_tva);
962
			$sql .= ", ".price2num($multicurrency_total_ttc);
963
			$sql .= ")";
964
965
			dol_syslog(get_class($this)."::addline", LOG_DEBUG);
966
			if ($this->db->query($sql))
967
			{
968
				$lineId = $this->db->last_insert_id(MAIN_DB_PREFIX."facturedet_rec");
969
				$this->id = $facid;
970
				$this->update_price();
971
				return $lineId;
972
			}
973
			else
974
			{
975
				$this->error = $this->db->lasterror();
976
				return -1;
977
			}
978
		}
979
	}
980
981
	/**
982
	 * 	Update a line to invoice
983
	 *
984
	 *  @param     	int			$rowid           	Id of line to update
985
	 *	@param    	string		$desc            	Description de la ligne
986
	 *	@param    	double		$pu_ht              Prix unitaire HT (> 0 even for credit note)
987
	 *	@param    	double		$qty             	Quantite
988
	 *	@param    	double		$txtva           	Taux de tva force, sinon -1
989
	 * 	@param		double		$txlocaltax1		Local tax 1 rate (deprecated)
990
	 *  @param		double		$txlocaltax2		Local tax 2 rate (deprecated)
991
	 *	@param    	int			$fk_product      	Product/Service ID predefined
992
	 *	@param    	double		$remise_percent  	Percentage discount of the line
993
	 *	@param		string		$price_base_type	HT or TTC
994
	 *	@param    	int			$info_bits			Bits of type of lines
995
	 *	@param    	int			$fk_remise_except	Id remise
996
	 *	@param    	double		$pu_ttc             Prix unitaire TTC (> 0 even for credit note)
997
	 *	@param		int			$type				Type of line (0=product, 1=service)
998
	 *	@param      int			$rang               Position of line
999
	 *	@param		int			$special_code		Special code
1000
	 *	@param		string		$label				Label of the line
1001
	 *	@param		string		$fk_unit			Unit
1002
	 * 	@param		double		$pu_ht_devise		Unit price in currency
1003
	 * 	@param		int			$notrigger			disable line update trigger
1004
	 *  @param		int			$date_start_fill	1=Flag to fill start date when generating invoice
1005
	 *  @param		int			$date_end_fill		1=Flag to fill end date when generating invoice
1006
	 * 	@param		int			$fk_fournprice		Id of origin supplier price
1007
	 * 	@param		int			$pa_ht				Price (without tax) of product for margin calculation
1008
	 *	@return    	int             				<0 if KO, Id of line if OK
1009
	 */
1010
	public function updateline($rowid, $desc, $pu_ht, $qty, $txtva, $txlocaltax1 = 0, $txlocaltax2 = 0, $fk_product = 0, $remise_percent = 0, $price_base_type = 'HT', $info_bits = 0, $fk_remise_except = '', $pu_ttc = 0, $type = 0, $rang = -1, $special_code = 0, $label = '', $fk_unit = null, $pu_ht_devise = 0, $notrigger = 0, $date_start_fill = 0, $date_end_fill = 0, $fk_fournprice = null, $pa_ht = 0)
1011
	{
1012
	    global $mysoc;
1013
1014
	    $facid = $this->id;
1015
1016
	    dol_syslog(get_class($this)."::updateline facid=".$facid." rowid=$rowid, desc=$desc, pu_ht=$pu_ht, qty=$qty, txtva=$txtva, txlocaltax1=$txlocaltax1, txlocaltax2=$txlocaltax2, fk_product=$fk_product, remise_percent=$remise_percent, info_bits=$info_bits, fk_remise_except=$fk_remise_except, price_base_type=$price_base_type, pu_ttc=$pu_ttc, type=$type, fk_unit=$fk_unit, pu_ht_devise=$pu_ht_devise", LOG_DEBUG);
1017
	    include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1018
1019
	    // Clean parameters
1020
	    if (empty($remise_percent)) $remise_percent = 0;
1021
1022
	    // Check parameters
1023
	    if ($type < 0) return -1;
1024
1025
	    if ($this->brouillon)
1026
	    {
1027
	        // Clean parameters
1028
	        $remise_percent = price2num($remise_percent);
1029
	        $qty = price2num($qty);
1030
	        if (empty($info_bits)) $info_bits = 0;
1031
	        $pu_ht          = price2num($pu_ht);
1032
	        $pu_ttc         = price2num($pu_ttc);
1033
	        $pu_ht_devise = price2num($pu_ht_devise);
1034
	        $txtva = price2num($txtva);
1035
		    $txlocaltax1	= price2num($txlocaltax1);
1036
		    $txlocaltax2	= price2num($txlocaltax2);
1037
		    if (empty($txlocaltax1)) $txlocaltax1 = 0;
1038
		    if (empty($txlocaltax2)) $txlocaltax2 = 0;
1039
1040
		    if (empty($this->multicurrency_subprice)) $this->multicurrency_subprice = 0;
1041
		    if (empty($this->multicurrency_total_ht)) $this->multicurrency_total_ht = 0;
1042
		    if (empty($this->multicurrency_total_tva)) $this->multicurrency_total_tva = 0;
1043
		    if (empty($this->multicurrency_total_ttc)) $this->multicurrency_total_ttc = 0;
1044
1045
	        if ($price_base_type == 'HT')
1046
	        {
1047
	            $pu = $pu_ht;
1048
	        }
1049
	        else
1050
	        {
1051
	            $pu = $pu_ttc;
1052
	        }
1053
1054
	        // Calculate total with, without tax and tax from qty, pu, remise_percent and txtva
1055
	        // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1056
	        // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1057
1058
	        $localtaxes_type = getLocalTaxesFromRate($txtva, 0, $this->thirdparty, $mysoc);
1059
1060
	        // Clean vat code
1061
	        $vat_src_code = '';
1062
	        if (preg_match('/\((.*)\)/', $txtva, $reg))
1063
	        {
1064
	        	$vat_src_code = $reg[1];
1065
	        	$txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
1066
	        }
1067
1068
	        $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $mysoc, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
1069
1070
	        $total_ht  = $tabprice[0];
1071
	        $total_tva = $tabprice[1];
1072
	        $total_ttc = $tabprice[2];
1073
		    $total_localtax1 = $tabprice[9];
1074
		    $total_localtax2 = $tabprice[10];
1075
		    $pu_ht  = $tabprice[3];
1076
		    $pu_tva = $tabprice[4];
1077
		    $pu_ttc = $tabprice[5];
1078
1079
		    // MultiCurrency
1080
		    $multicurrency_total_ht  = $tabprice[16];
1081
		    $multicurrency_total_tva = $tabprice[17];
1082
		    $multicurrency_total_ttc = $tabprice[18];
1083
		    $pu_ht_devise = $tabprice[19];
1084
1085
	        $product_type = $type;
1086
	        if ($fk_product)
1087
	        {
1088
	            $product = new Product($this->db);
1089
	            $result = $product->fetch($fk_product);
1090
	            $product_type = $product->type;
1091
	        }
1092
1093
	        $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet_rec SET ";
1094
	        $sql .= "fk_facture = '".$facid."'";
1095
	        $sql .= ", label=".(!empty($label) ? "'".$this->db->escape($label)."'" : "null");
1096
	        $sql .= ", description='".$this->db->escape($desc)."'";
1097
	        $sql .= ", price=".price2num($pu_ht);
1098
	        $sql .= ", qty=".price2num($qty);
1099
	        $sql .= ", tva_tx=".price2num($txtva);
1100
	        $sql .= ", vat_src_code='".$this->db->escape($vat_src_code)."'";
1101
		    $sql .= ", localtax1_tx=".$txlocaltax1;
1102
		    $sql .= ", localtax1_type='".$this->db->escape($localtaxes_type[0])."'";
1103
		    $sql .= ", localtax2_tx=".$txlocaltax2;
1104
		    $sql .= ", localtax2_type='".$this->db->escape($localtaxes_type[2])."'";
1105
	        $sql .= ", fk_product=".(!empty($fk_product) ? "'".$fk_product."'" : "null");
1106
	        $sql .= ", product_type=".$product_type;
1107
	        $sql .= ", remise_percent='".price2num($remise_percent)."'";
1108
	        $sql .= ", subprice='".price2num($pu_ht)."'";
1109
	        $sql .= ", total_ht='".price2num($total_ht)."'";
1110
	        $sql .= ", total_tva='".price2num($total_tva)."'";
1111
	        $sql .= ", total_localtax1='".price2num($total_localtax1)."'";
1112
	        $sql .= ", total_localtax2='".price2num($total_localtax2)."'";
1113
	        $sql .= ", total_ttc='".price2num($total_ttc)."'";
1114
	        $sql .= ", date_start_fill=".((int) $date_start_fill);
1115
	        $sql .= ", date_end_fill=".((int) $date_end_fill);
1116
	        $sql .= ", fk_product_fournisseur_price=".($fk_fournprice > 0 ? $fk_fournprice : 'null');
1117
	        $sql .= ", buy_price_ht=".($pa_ht ? price2num($pa_ht) : 0);
1118
	        $sql .= ", info_bits=".$info_bits;
1119
	        $sql .= ", rang=".$rang;
1120
	        $sql .= ", special_code=".$special_code;
1121
	        $sql .= ", fk_unit=".($fk_unit ? "'".$this->db->escape($fk_unit)."'" : "null");
1122
	        $sql .= ', multicurrency_subprice = '.$pu_ht_devise;
1123
	        $sql .= ', multicurrency_total_ht = '.$multicurrency_total_ht;
1124
	        $sql .= ', multicurrency_total_tva = '.$multicurrency_total_tva;
1125
	        $sql .= ', multicurrency_total_ttc = '.$multicurrency_total_ttc;
1126
	        $sql .= " WHERE rowid = ".$rowid;
1127
1128
	        dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
1129
	        if ($this->db->query($sql))
1130
	        {
1131
	            $this->id = $facid;
1132
	            $this->update_price();
1133
	            return 1;
1134
	        }
1135
	        else
1136
	        {
1137
	            $this->error = $this->db->lasterror();
1138
	            return -1;
1139
	        }
1140
	    }
1141
	}
1142
1143
1144
	/**
1145
	 * Return the next date of
1146
	 *
1147
	 * @return  int|false   false if KO, timestamp if OK
1148
	 */
1149
	public function getNextDate()
1150
	{
1151
		if (empty($this->date_when)) return false;
1152
		return dol_time_plus_duree($this->date_when, $this->frequency, $this->unit_frequency);
1153
	}
1154
1155
	/**
1156
	 * Return if maximum number of generation is reached
1157
	 *
1158
	 * @return	boolean			False by default, True if maximum number of generation is reached
1159
	 */
1160
	public function isMaxNbGenReached()
1161
	{
1162
		$ret = false;
1163
		if ($this->nb_gen_max > 0 && ($this->nb_gen_done >= $this->nb_gen_max)) $ret = true;
1164
		return $ret;
1165
	}
1166
1167
	/**
1168
	 * Format string to output with by striking the string if max number of generation was reached
1169
	 *
1170
	 * @param	string		$ret	Default value to output
1171
	 * @return	boolean				False by default, True if maximum number of generation is reached
1172
	 */
1173
	public function strikeIfMaxNbGenReached($ret)
1174
	{
1175
		// Special case to strike the date
1176
		return ($this->isMaxNbGenReached() ? '<strike>' : '').$ret.($this->isMaxNbGenReached() ? '</strike>' : '');
1177
	}
1178
1179
	/**
1180
	 *  Create all recurrents invoices (for all entities if multicompany is used).
1181
	 *  A result may also be provided into this->output.
1182
	 *
1183
	 *  WARNING: This method change temporarly context $conf->entity to be in correct context for each recurring invoice found.
1184
	 *
1185
	 *  @param	int		$restrictioninvoiceid		0=All qualified template invoices found. > 0 = restrict action on invoice ID
1186
	 *  @param	int		$forcevalidation		1=Force validation of invoice whatever is template auto_validate flag.
1187
	 *  @return	int								0 if OK, < 0 if KO (this function is used also by cron so only 0 is OK)
1188
	 */
1189
	public function createRecurringInvoices($restrictioninvoiceid = 0, $forcevalidation = 0)
1190
	{
1191
		global $conf, $langs, $db, $user, $hookmanager;
1192
1193
		$error = 0;
1194
		$nb_create = 0;
1195
1196
		// Load translation files required by the page
1197
		$langs->loadLangs(array("main", "bills"));
1198
1199
		$now = dol_now();
1200
		$tmparray = dol_getdate($now);
1201
		$today = dol_mktime(23, 59, 59, $tmparray['mon'], $tmparray['mday'], $tmparray['year']); // Today is last second of current day
1202
1203
		dol_syslog("createRecurringInvoices restrictioninvoiceid=".$restrictioninvoiceid." forcevalidation=".$forcevalidation);
1204
1205
		$sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.'facture_rec';
1206
		$sql .= ' WHERE frequency > 0'; // A recurring invoice is an invoice with a frequency
1207
		$sql .= " AND (date_when IS NULL OR date_when <= '".$db->idate($today)."')";
1208
		$sql .= ' AND (nb_gen_done < nb_gen_max OR nb_gen_max = 0)';
1209
		$sql .= ' AND suspended = 0';
1210
		$sql .= ' AND entity = '.$conf->entity; // MUST STAY = $conf->entity here
1211
		if ($restrictioninvoiceid > 0)
1212
			$sql .= ' AND rowid = '.$restrictioninvoiceid;
1213
		$sql .= $db->order('entity', 'ASC');
1214
		//print $sql;exit;
1215
		$parameters = array(
1216
			'restrictioninvoiceid' => $restrictioninvoiceid,
1217
			'forcevalidation' => $forcevalidation,
1218
		);
1219
		$reshook = $hookmanager->executeHooks('beforeCreationOfRecurringInvoices', $parameters, $sql); // note that $sql might be modified by hooks
1220
1221
		$resql = $db->query($sql);
1222
		if ($resql)
1223
		{
1224
			$i = 0;
1225
			$num = $db->num_rows($resql);
1226
1227
			if ($num)
1228
				$this->output .= $langs->trans("FoundXQualifiedRecurringInvoiceTemplate", $num)."\n";
1229
			else
1230
				$this->output .= $langs->trans("NoQualifiedRecurringInvoiceTemplateFound");
1231
1232
			$saventity = $conf->entity;
1233
1234
			while ($i < $num)     // Loop on each template invoice. If $num = 0, test is false at first pass.
1235
			{
1236
				$line = $db->fetch_object($resql);
1237
1238
				$db->begin();
1239
1240
				$invoiceidgenerated = 0;
1241
1242
				$facture = null;
1243
				$facturerec = new FactureRec($db);
1244
				$facturerec->fetch($line->rowid);
1245
1246
				if ($facturerec->id > 0)
1247
				{
1248
					// Set entity context
1249
					$conf->entity = $facturerec->entity;
1250
1251
					dol_syslog("createRecurringInvoices Process invoice template id=".$facturerec->id.", ref=".$facturerec->ref.", entity=".$facturerec->entity);
1252
1253
					$facture = new Facture($db);
1254
					$facture->fac_rec = $facturerec->id; // We will create $facture from this recurring invoice
1255
					$facture->fk_fac_rec_source = $facturerec->id; // We will create $facture from this recurring invoice
1256
1257
					$facture->type = self::TYPE_STANDARD;
1258
					$facture->brouillon = 1;
1259
					$facture->date = (empty($facturerec->date_when) ? $now : $facturerec->date_when); // We could also use dol_now here but we prefer date_when so invoice has real date when we would like even if we generate later.
1260
					$facture->socid = $facturerec->socid;
1261
1262
					$invoiceidgenerated = $facture->create($user);
1263
					if ($invoiceidgenerated <= 0)
1264
					{
1265
						$this->errors = $facture->errors;
1266
						$this->error = $facture->error;
1267
						$error++;
1268
					}
1269
					if (!$error && ($facturerec->auto_validate || $forcevalidation))
1270
					{
1271
						$result = $facture->validate($user);
1272
						if ($result <= 0)
1273
						{
1274
							$this->errors = $facture->errors;
1275
							$this->error = $facture->error;
1276
							$error++;
1277
						}
1278
					}
1279
					if (!$error && $facturerec->generate_pdf)
1280
					{
1281
						// We refresh the object in order to have all necessary data (like date_lim_reglement)
1282
						$facture->fetch($facture->id);
1283
						$result = $facture->generateDocument($facturerec->modelpdf, $langs);
1284
						if ($result <= 0)
1285
						{
1286
							$this->errors = $facture->errors;
1287
							$this->error = $facture->error;
1288
							$error++;
1289
						}
1290
					}
1291
				}
1292
				else
1293
				{
1294
					$error++;
1295
					$this->error = "Failed to load invoice template with id=".$line->rowid.", entity=".$conf->entity."\n";
1296
					$this->errors[] = "Failed to load invoice template with id=".$line->rowid.", entity=".$conf->entity;
1297
					dol_syslog("createRecurringInvoices Failed to load invoice template with id=".$line->rowid.", entity=".$conf->entity);
1298
				}
1299
1300
				if (!$error && $invoiceidgenerated >= 0)
1301
				{
1302
					$db->commit("createRecurringInvoices Process invoice template id=".$facturerec->id.", ref=".$facturerec->ref);
1303
					dol_syslog("createRecurringInvoices Process invoice template ".$facturerec->ref." is finished with a success generation");
1304
					$nb_create++;
1305
					$this->output .= $langs->trans("InvoiceGeneratedFromTemplate", $facture->ref, $facturerec->ref)."\n";
1306
				}
1307
				else
1308
				{
1309
					$db->rollback("createRecurringInvoices Process invoice template id=".$facturerec->id.", ref=".$facturerec->ref);
1310
				}
1311
1312
				$parameters = array(
1313
					'cpt'        => $i,
1314
					'total'      => $num,
1315
					'errorCount' => $error,
1316
					'invoiceidgenerated' => $invoiceidgenerated,
1317
					'facturerec' => $facturerec, // it's an object which PHP passes by "reference", so modifiable by hooks.
1318
					'this'       => $this, // it's an object which PHP passes by "reference", so modifiable by hooks.
1319
				);
1320
				$reshook = $hookmanager->executeHooks('afterCreationOfRecurringInvoice', $parameters, $facture); // note: $facture can be modified by hooks (warning: $facture can be null)
1321
1322
				$i++;
1323
			}
1324
1325
			$conf->entity = $saventity; // Restore entity context
1326
		}
1327
		else dol_print_error($db);
1328
1329
		$this->output = trim($this->output);
1330
1331
		return $error ? $error : 0;
1332
	}
1333
1334
	/**
1335
	 *	Return clicable name (with picto eventually)
1336
	 *
1337
	 * @param	int		$withpicto       			Add picto into link
1338
	 * @param  string	$option          			Where point the link
1339
	 * @param  int		$max             			Maxlength of ref
1340
	 * @param  int		$short           			1=Return just URL
1341
	 * @param  string   $moretitle       			Add more text to title tooltip
1342
     * @param	int  	$notooltip		 			1=Disable tooltip
1343
     * @param  int		$save_lastsearch_value    	-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1344
	 * @return string 			         			String with URL
1345
	 */
1346
	public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = '', $save_lastsearch_value = -1)
1347
	{
1348
		global $langs;
1349
1350
		$result = '';
1351
1352
		$label = '<u>'.$langs->trans("ShowInvoice").'</u>';
1353
		if (!empty($this->ref)) {
1354
			$label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1355
		}
1356
		if ($this->frequency > 0) {
1357
			$label .= '<br><b>'.$langs->trans('Frequency').':</b> '.$langs->trans('FrequencyPer_'.$this->unit_frequency, $this->frequency);
1358
		}
1359
		if (!empty($this->date_last_gen)) {
1360
			$label .= '<br><b>'.$langs->trans('DateLastGeneration').':</b> '.dol_print_date($this->date_last_gen, 'dayhour');
1361
		}
1362
		if ($this->frequency > 0) {
1363
			if (!empty($this->date_when)) {
1364
				$label .= '<br><b>'.$langs->trans('NextDateToExecution').':</b> ';
1365
				$label .= (empty($this->suspended) ? '' : '<strike>').dol_print_date($this->date_when, 'day').(empty($this->suspended) ? '' : '</strike>'); // No hour for this property
1366
				if (!empty($this->suspended)) $label .= ' ('.$langs->trans("Disabled").')';
1367
			}
1368
		}
1369
1370
        $url = DOL_URL_ROOT.'/compta/facture/card-rec.php?facid='.$this->id;
1371
1372
        if ($short) return $url;
1373
1374
        if ($option != 'nolink')
1375
        {
1376
        	// Add param to save lastsearch_values or not
1377
        	$add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1378
        	if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values = 1;
1379
        	if ($add_save_lastsearch_values) $url .= '&save_lastsearch_values=1';
1380
        }
1381
1382
		$linkstart = '<a href="'.$url.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
1383
		$linkend = '</a>';
1384
1385
		$result .= $linkstart;
1386
		if ($withpicto) $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
1387
		if ($withpicto != 2) $result .= $this->ref;
1388
		$result .= $linkend;
1389
1390
		return $result;
1391
	}
1392
1393
	/**
1394
	 *  Return label of object status
1395
	 *
1396
	 *  @param      int		$mode			0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=short label + picto, 6=Long label + picto
1397
	 *  @param      integer	$alreadypaid    Not used on recurring invoices
1398
	 *  @return     string			        Label of status
1399
	 */
1400
	public function getLibStatut($mode = 0, $alreadypaid = -1)
1401
	{
1402
		return $this->LibStatut($this->frequency ? 1 : 0, $this->suspended, $mode, $alreadypaid, empty($this->type) ? 0 : $this->type);
1403
	}
1404
1405
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1406
	/**
1407
	 *	Return label of a status
1408
	 *
1409
	 *	@param    	int  	$recur         	Is it a recurring invoice ?
1410
	 *	@param      int		$status        	Id status (suspended or not)
1411
	 *	@param      int		$mode          	0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=short label + picto, 6=long label + picto
1412
	 *	@param		integer	$alreadypaid	Not used for recurring invoices
1413
	 *	@param		int		$type			Type invoice
1414
	 *	@return     string        			Label of status
1415
	 */
1416
	public function LibStatut($recur, $status, $mode = 0, $alreadypaid = -1, $type = 0)
1417
	{
1418
        // phpcs:enable
1419
		global $langs;
1420
		$langs->load('bills');
1421
1422
		$labelStatus = $langs->trans('Active');
1423
		$statusType = 'status0';
1424
1425
		//print "$recur,$status,$mode,$alreadypaid,$type";
1426
		if ($mode == 0)
1427
		{
1428
			$prefix = '';
1429
			if ($recur)
1430
			{
1431
				if ($status == self::STATUS_SUSPENDED) {
1432
					$labelStatus = $langs->trans('Disabled');
1433
				}
1434
				else {
1435
					$labelStatus = $langs->trans('Active');
1436
				}
1437
			}
1438
			else
1439
			{
1440
				if ($status == self::STATUS_SUSPENDED) {
1441
					$labelStatus = $langs->trans('Disabled');
1442
				}
1443
				else {
1444
					$labelStatus = $langs->trans("Draft");
1445
				}
1446
			}
1447
		}
1448
		elseif ($mode == 1)
1449
		{
1450
			$prefix = 'Short';
1451
			if ($recur)
1452
			{
1453
				if ($status == self::STATUS_SUSPENDED) {
1454
					$labelStatus = $langs->trans('Disabled');
1455
				}
1456
				else {
1457
					$labelStatus = $langs->trans('Active');
1458
				}
1459
			}
1460
			else
1461
			{
1462
				if ($status == self::STATUS_SUSPENDED) {
1463
					$labelStatus = $langs->trans('Disabled');
1464
				}
1465
				else {
1466
					$labelStatus = $langs->trans("Draft");
1467
				}
1468
			}
1469
		}
1470
		elseif ($mode == 2)
1471
		{
1472
			if ($recur)
1473
			{
1474
				if ($status == self::STATUS_SUSPENDED) {
1475
					$statusType = 'status6';
1476
					$labelStatus = $langs->trans('Disabled');
1477
				}
1478
				else {
1479
					$statusType = 'status4';
1480
					$labelStatus = $langs->trans('Active');
1481
				}
1482
			}
1483
			else
1484
			{
1485
				if ($status == self::STATUS_SUSPENDED) {
1486
					$statusType = 'status6';
1487
					$labelStatus = $langs->trans('Disabled');
1488
				}
1489
				else {
1490
					$statusType = 'status0';
1491
					$labelStatus = $langs->trans('Draft');
1492
				}
1493
			}
1494
		}
1495
		elseif ($mode == 3)
1496
		{
1497
			if ($recur)
1498
			{
1499
				$prefix = 'Short';
1500
				if ($status == self::STATUS_SUSPENDED) {
1501
					$statusType = 'status6';
1502
					$labelStatus = $langs->trans('Disabled');
1503
				}
1504
				else {
1505
					$statusType = 'status4';
1506
					$labelStatus = $langs->trans('Active');
1507
				}
1508
			}
1509
			else
1510
			{
1511
				if ($status == self::STATUS_SUSPENDED) {
1512
					$statusType = 'status6';
1513
					$labelStatus = $langs->trans('Disabled');
1514
				}
1515
				else {
1516
					$statusType = 'status0';
1517
					$labelStatus = $langs->trans('Draft');
1518
				}
1519
			}
1520
		}
1521
		elseif ($mode == 4)
1522
		{
1523
			$prefix = '';
1524
			if ($recur)
1525
			{
1526
				if ($status == self::STATUS_SUSPENDED) {
1527
					$statusType = 'status6';
1528
					$labelStatus = $langs->trans('Disabled');
1529
				}
1530
				else {
1531
					$statusType = 'status4';
1532
					$labelStatus = $langs->trans('Active');
1533
				}
1534
			}
1535
			else
1536
			{
1537
				if ($status == self::STATUS_SUSPENDED) {
1538
					$statusType = 'status6';
1539
					$labelStatus = $langs->trans('Disabled');
1540
				}
1541
				else {
1542
					$statusType = 'status0';
1543
					$labelStatus = $langs->trans('Draft');
1544
				}
1545
			}
1546
		}
1547
		elseif ($mode == 5 || $mode == 6)
1548
		{
1549
			$prefix = '';
1550
			if ($mode == 5) $prefix = 'Short';
1551
			if ($recur)
1552
			{
1553
				if ($status == self::STATUS_SUSPENDED) {
1554
					$statusType = 'status6';
1555
					$labelStatus = $langs->trans('Disabled');
1556
				}
1557
				else {
1558
					$statusType = 'status4';
1559
					$labelStatus = $langs->trans('Active');
1560
				}
1561
			}
1562
			else
1563
			{
1564
				if ($status == self::STATUS_SUSPENDED) {
1565
					$statusType = 'status6';
1566
					$labelStatus = $langs->trans('Disabled');
1567
				}
1568
				else {
1569
					$statusType = 'status0';
1570
					$labelStatus = $langs->trans('Draft');
1571
				}
1572
			}
1573
		}
1574
1575
		if (empty($labelStatusShort)) $labelStatusShort = $labelStatus;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $labelStatusShort does not exist. Did you maybe mean $labelStatus?
Loading history...
1576
1577
		return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1578
	}
1579
1580
	/**
1581
	 *  Initialise an instance with random values.
1582
	 *  Used to build previews or test instances.
1583
	 *	id must be 0 if object instance is a specimen.
1584
	 *
1585
	 *	@param	string		$option		''=Create a specimen invoice with lines, 'nolines'=No lines
1586
	 *  @return	void
1587
	 */
1588
	public function initAsSpecimen($option = '')
1589
	{
1590
		global $user, $langs, $conf;
1591
1592
		$now = dol_now();
1593
		$arraynow = dol_getdate($now);
1594
		$nownotime = dol_mktime(0, 0, 0, $arraynow['mon'], $arraynow['mday'], $arraynow['year']);
1595
1596
        // Load array of products prodids
1597
		$num_prods = 0;
1598
		$prodids = array();
1599
1600
		$sql = "SELECT rowid";
1601
		$sql .= " FROM ".MAIN_DB_PREFIX."product";
1602
		$sql .= " WHERE entity IN (".getEntity('product').")";
1603
		$resql = $this->db->query($sql);
1604
		if ($resql)
1605
		{
1606
			$num_prods = $this->db->num_rows($resql);
1607
			$i = 0;
1608
			while ($i < $num_prods)
1609
			{
1610
				$i++;
1611
				$row = $this->db->fetch_row($resql);
1612
				$prodids[$i] = $row[0];
1613
			}
1614
		}
1615
1616
		// Initialize parameters
1617
		$this->id = 0;
1618
		$this->ref = 'SPECIMEN';
1619
		$this->specimen = 1;
1620
		$this->socid = 1;
1621
		$this->date = $nownotime;
1622
		$this->date_lim_reglement = $nownotime + 3600 * 24 * 30;
1623
		$this->cond_reglement_id   = 1;
1624
		$this->cond_reglement_code = 'RECEP';
1625
		$this->date_lim_reglement = $this->calculate_date_lim_reglement();
1626
		$this->mode_reglement_id   = 0; // Not forced to show payment mode CHQ + VIR
1627
		$this->mode_reglement_code = ''; // Not forced to show payment mode CHQ + VIR
1628
		$this->note_public = 'This is a comment (public)';
1629
		$this->note_private = 'This is a comment (private)';
1630
		$this->note = 'This is a comment (private)';
1631
		$this->fk_incoterms = 0;
1632
		$this->location_incoterms = '';
1633
1634
		if (empty($option) || $option != 'nolines')
1635
		{
1636
			// Lines
1637
			$nbp = 5;
1638
			$xnbp = 0;
1639
			while ($xnbp < $nbp)
1640
			{
1641
				$line = new FactureLigne($this->db);
1642
				$line->desc = $langs->trans("Description")." ".$xnbp;
1643
				$line->qty = 1;
1644
				$line->subprice = 100;
1645
				$line->tva_tx = 19.6;
1646
				$line->localtax1_tx = 0;
1647
				$line->localtax2_tx = 0;
1648
				$line->remise_percent = 0;
1649
				if ($xnbp == 1)        // Qty is negative (product line)
1650
				{
1651
					$prodid = mt_rand(1, $num_prods);
1652
					$line->fk_product = $prodids[$prodid];
1653
					$line->qty = -1;
1654
					$line->total_ht = -100;
1655
					$line->total_ttc = -119.6;
1656
					$line->total_tva = -19.6;
1657
				}
1658
				elseif ($xnbp == 2)    // UP is negative (free line)
1659
				{
1660
					$line->subprice = -100;
1661
					$line->total_ht = -100;
1662
					$line->total_ttc = -119.6;
1663
					$line->total_tva = -19.6;
1664
					$line->remise_percent = 0;
1665
				}
1666
				elseif ($xnbp == 3)    // Discount is 50% (product line)
1667
				{
1668
					$prodid = mt_rand(1, $num_prods);
1669
					$line->fk_product = $prodids[$prodid];
1670
					$line->total_ht = 50;
1671
					$line->total_ttc = 59.8;
1672
					$line->total_tva = 9.8;
1673
					$line->remise_percent = 50;
1674
				}
1675
				else    // (product line)
1676
				{
1677
					$prodid = mt_rand(1, $num_prods);
1678
					$line->fk_product = $prodids[$prodid];
1679
					$line->total_ht = 100;
1680
					$line->total_ttc = 119.6;
1681
					$line->total_tva = 19.6;
1682
					$line->remise_percent = 00;
1683
				}
1684
1685
				$this->lines[$xnbp] = $line;
1686
				$xnbp++;
1687
1688
				$this->total_ht       += $line->total_ht;
1689
				$this->total_tva      += $line->total_tva;
1690
				$this->total_ttc      += $line->total_ttc;
1691
			}
1692
			$this->revenuestamp = 0;
1693
1694
			// Add a line "offered"
1695
			$line = new FactureLigne($this->db);
1696
			$line->desc = $langs->trans("Description")." (offered line)";
1697
			$line->qty = 1;
1698
			$line->subprice = 100;
1699
			$line->tva_tx = 19.6;
1700
			$line->localtax1_tx = 0;
1701
			$line->localtax2_tx = 0;
1702
			$line->remise_percent = 100;
1703
			$line->total_ht = 0;
1704
			$line->total_ttc = 0; // 90 * 1.196
1705
			$line->total_tva = 0;
1706
			$prodid = mt_rand(1, $num_prods);
1707
			$line->fk_product = $prodids[$prodid];
1708
1709
			$this->lines[$xnbp] = $line;
1710
			$xnbp++;
1711
		}
1712
1713
		$this->usenewprice = 0;
1714
	}
1715
1716
	/**
1717
	 * Function used to replace a thirdparty id with another one.
1718
	 *
1719
	 * @param DoliDB $db Database handler
1720
	 * @param int $origin_id Old thirdparty id
1721
	 * @param int $dest_id New thirdparty id
1722
	 * @return bool
1723
	 */
1724
	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
1725
	{
1726
		$tables = array(
1727
			'facture_rec'
1728
		);
1729
1730
		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
1731
	}
1732
1733
	/**
1734
     *	Update frequency and unit
1735
     *
1736
     *	@param     	int		$frequency		value of frequency
1737
     *	@param     	string	$unit 			unit of frequency  (d, m, y)
1738
     *	@return		int						<0 if KO, >0 if OK
1739
     */
1740
    public function setFrequencyAndUnit($frequency, $unit)
1741
    {
1742
        if (!$this->table_element) {
1743
            dol_syslog(get_class($this)."::setFrequencyAndUnit was called on objet with property table_element not defined", LOG_ERR);
1744
            return -1;
1745
        }
1746
1747
        if (!empty($frequency) && empty($unit)) {
1748
            dol_syslog(get_class($this)."::setFrequencyAndUnit was called on objet with params frequency defined but unit not defined", LOG_ERR);
1749
            return -2;
1750
        }
1751
1752
        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1753
        $sql .= ' SET frequency = '.($frequency ? $this->db->escape($frequency) : 'null');
1754
        if (!empty($unit)) {
1755
        	$sql .= ', unit_frequency = \''.$this->db->escape($unit).'\'';
1756
		}
1757
        $sql .= ' WHERE rowid = '.$this->id;
1758
1759
        dol_syslog(get_class($this)."::setFrequencyAndUnit", LOG_DEBUG);
1760
        if ($this->db->query($sql)) {
1761
            $this->frequency = $frequency;
1762
            if (!empty($unit)) $this->unit_frequency = $unit;
1763
            return 1;
1764
        }
1765
        else
1766
        {
1767
            dol_print_error($this->db);
1768
            return -1;
1769
        }
1770
    }
1771
1772
	/**
1773
     *	Update the next date of execution
1774
     *
1775
     *	@param     	datetime	$date					date of execution
1776
     *	@param     	int			$increment_nb_gen_done	0 do nothing more, >0 increment nb_gen_done
1777
     *	@return		int									<0 if KO, >0 if OK
1778
     */
1779
    public function setNextDate($date, $increment_nb_gen_done = 0)
1780
    {
1781
        if (!$this->table_element)
1782
        {
1783
            dol_syslog(get_class($this)."::setNextDate was called on objet with property table_element not defined", LOG_ERR);
1784
            return -1;
1785
        }
1786
        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1787
        $sql .= " SET date_when = ".($date ? "'".$this->db->idate($date)."'" : "null");
1788
        if ($increment_nb_gen_done > 0) $sql .= ', nb_gen_done = nb_gen_done + 1';
1789
        $sql .= ' WHERE rowid = '.$this->id;
1790
1791
        dol_syslog(get_class($this)."::setNextDate", LOG_DEBUG);
1792
        if ($this->db->query($sql))
1793
        {
1794
            $this->date_when = $date;
1795
            if ($increment_nb_gen_done > 0) $this->nb_gen_done++;
1796
            return 1;
1797
        }
1798
        else
1799
        {
1800
            dol_print_error($this->db);
1801
            return -1;
1802
        }
1803
    }
1804
1805
	/**
1806
     *	Update the maximum period
1807
     *
1808
     *	@param     	int		$nb		number of maximum period
1809
     *	@return		int				<0 if KO, >0 if OK
1810
     */
1811
    public function setMaxPeriod($nb)
1812
    {
1813
        if (!$this->table_element)
1814
        {
1815
            dol_syslog(get_class($this)."::setMaxPeriod was called on objet with property table_element not defined", LOG_ERR);
1816
            return -1;
1817
        }
1818
1819
        if (empty($nb)) $nb = 0;
1820
1821
        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1822
        $sql .= ' SET nb_gen_max = '.$nb;
1823
        $sql .= ' WHERE rowid = '.$this->id;
1824
1825
        dol_syslog(get_class($this)."::setMaxPeriod", LOG_DEBUG);
1826
        if ($this->db->query($sql))
1827
        {
1828
            $this->nb_gen_max = $nb;
1829
            return 1;
1830
        }
1831
        else
1832
        {
1833
            dol_print_error($this->db);
1834
            return -1;
1835
        }
1836
    }
1837
1838
	/**
1839
     *	Update the auto validate flag of invoice
1840
     *
1841
     *	@param     	int		$validate		0 to create in draft, 1 to create and validate invoice
1842
     *	@return		int						<0 if KO, >0 if OK
1843
     */
1844
    public function setAutoValidate($validate)
1845
    {
1846
        if (!$this->table_element)
1847
        {
1848
            dol_syslog(get_class($this)."::setAutoValidate was called on objet with property table_element not defined", LOG_ERR);
1849
            return -1;
1850
        }
1851
1852
        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1853
        $sql .= ' SET auto_validate = '.$validate;
1854
        $sql .= ' WHERE rowid = '.$this->id;
1855
1856
        dol_syslog(get_class($this)."::setAutoValidate", LOG_DEBUG);
1857
        if ($this->db->query($sql))
1858
        {
1859
            $this->auto_validate = $validate;
1860
            return 1;
1861
        }
1862
        else
1863
        {
1864
            dol_print_error($this->db);
1865
            return -1;
1866
        }
1867
    }
1868
1869
    /**
1870
     *	Update the auto generate documents
1871
     *
1872
     *	@param     	int		$validate		0 no document, 1 to generate document
1873
     *	@return		int						<0 if KO, >0 if OK
1874
     */
1875
    public function setGeneratePdf($validate)
1876
    {
1877
        if (!$this->table_element)
1878
        {
1879
            dol_syslog(get_class($this)."::setGeneratePdf was called on objet with property table_element not defined", LOG_ERR);
1880
            return -1;
1881
        }
1882
1883
        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1884
        $sql .= ' SET generate_pdf = '.$validate;
1885
        $sql .= ' WHERE rowid = '.$this->id;
1886
1887
        dol_syslog(get_class($this)."::setGeneratePdf", LOG_DEBUG);
1888
        if ($this->db->query($sql))
1889
        {
1890
            $this->generate_pdf = $validate;
1891
            return 1;
1892
        }
1893
        else
1894
        {
1895
            dol_print_error($this->db);
1896
            return -1;
1897
        }
1898
    }
1899
1900
    /**
1901
     *  Update the model for documents
1902
     *
1903
     *  @param     	string		$model		model of document generator
1904
     *  @return		int						<0 if KO, >0 if OK
1905
     */
1906
    public function setModelPdf($model)
1907
    {
1908
        if (!$this->table_element)
1909
        {
1910
            dol_syslog(get_class($this)."::setModelPdf was called on objet with property table_element not defined", LOG_ERR);
1911
            return -1;
1912
        }
1913
1914
        $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1915
        $sql .= ' SET modelpdf = "'.$model.'"';
1916
        $sql .= ' WHERE rowid = '.$this->id;
1917
1918
        dol_syslog(get_class($this)."::setModelPdf", LOG_DEBUG);
1919
        if ($this->db->query($sql))
1920
        {
1921
            $this->modelpdf = $model;
1922
            return 1;
1923
        }
1924
        else
1925
        {
1926
            dol_print_error($this->db);
1927
            return -1;
1928
        }
1929
    }
1930
}
1931
1932
1933
1934
/**
1935
 *	Class to manage invoice lines of templates.
1936
 *  Saved into database table llx_facturedet_rec
1937
 */
1938
class FactureLigneRec extends CommonInvoiceLine
1939
{
1940
	/**
1941
	 * @var string ID to identify managed object
1942
	 */
1943
	public $element = 'facturedetrec';
1944
1945
	/**
1946
	 * @var string Name of table without prefix where object is stored
1947
	 */
1948
	public $table_element = 'facturedet_rec';
1949
1950
	public $date_start_fill;
1951
	public $date_end_fill;
1952
1953
1954
    /**
1955
     * 	Delete line in database
1956
     *
1957
     *  @param		User	$user		Object user
1958
     *  @param		int		$notrigger	Disable triggers
1959
     *	@return		int					<0 if KO, >0 if OK
1960
     */
1961
    public function delete(User $user, $notrigger = false)
1962
    {
1963
    	$error = 0;
1964
1965
	    $this->db->begin();
1966
1967
	    if (!$error) {
1968
	        if (!$notrigger) {
1969
	            // Call triggers
1970
	            $result = $this->call_trigger('LINEBILLREC_DELETE', $user);
1971
	            if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail
1972
	            // End call triggers
1973
	        }
1974
	    }
1975
1976
		if (!$error)
1977
        {
1978
            $result = $this->deleteExtraFields();
1979
            if ($result < 0) {
1980
                $error++;
1981
            }
1982
        }
1983
1984
	    if (!$error)
1985
	    {
1986
    		$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
1987
1988
    		$res = $this->db->query($sql);
1989
    		if ($res === false) {
0 ignored issues
show
introduced by
The condition $res === false is always false.
Loading history...
1990
    		    $error++;
1991
    		    $this->errors[] = $this->db->lasterror();
1992
    		}
1993
	    }
1994
1995
    	// Commit or rollback
1996
		if ($error) {
1997
		    $this->db->rollback();
1998
		    return -1;
1999
		} else {
2000
		    $this->db->commit();
2001
		    return 1;
2002
		}
2003
    }
2004
2005
2006
    /**
2007
     *	Get line of template invoice
2008
     *
2009
     *	@param		int 	$rowid		Id of invoice
2010
     *	@return     int         		1 if OK, < 0 if KO
2011
     */
2012
    public function fetch($rowid)
2013
    {
2014
    	$sql = 'SELECT l.rowid, l.fk_facture ,l.fk_product, l.product_type, l.label as custom_label, l.description, l.product_type, l.price, l.qty, l.vat_src_code, l.tva_tx,';
2015
    	$sql .= ' l.localtax1_tx, l.localtax2_tx, l.localtax1_type, l.localtax2_type, l.remise, l.remise_percent, l.subprice,';
2016
    	$sql .= ' l.date_start_fill, l.date_end_fill, l.info_bits, l.total_ht, l.total_tva, l.total_ttc,';
2017
    	$sql .= ' l.rang, l.special_code,';
2018
    	$sql .= ' l.fk_unit, l.fk_contract_line,';
2019
    	$sql .= ' p.ref as product_ref, p.fk_product_type as fk_product_type, p.label as product_label, p.description as product_desc';
2020
    	$sql .= ' FROM '.MAIN_DB_PREFIX.'facturedet_rec as l';
2021
    	$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON l.fk_product = p.rowid';
2022
    	$sql .= ' WHERE l.rowid = '.$rowid;
2023
    	$sql .= ' ORDER BY l.rang';
2024
2025
    	dol_syslog('FactureRec::fetch', LOG_DEBUG);
2026
    	$result = $this->db->query($sql);
2027
    	if ($result)
2028
    	{
2029
    		$objp = $this->db->fetch_object($result);
2030
2031
    		$this->id = $objp->rowid;
2032
    		$this->label            = $objp->custom_label; // Label line
2033
    		$this->desc             = $objp->description; // Description line
2034
    		$this->description      = $objp->description; // Description line
2035
    		$this->product_type     = $objp->product_type; // Type of line
2036
    		$this->ref              = $objp->product_ref; // Ref product
2037
    		$this->product_ref      = $objp->product_ref; // Ref product
2038
    		$this->libelle          = $objp->product_label; // deprecated
2039
    		$this->product_label = $objp->product_label; // Label product
2040
    		$this->product_desc     = $objp->product_desc; // Description product
2041
    		$this->fk_product_type  = $objp->fk_product_type; // Type of product
2042
    		$this->qty              = $objp->qty;
2043
    		$this->price = $objp->price;
2044
    		$this->subprice         = $objp->subprice;
2045
    		$this->fk_facture = $objp->fk_facture;
2046
    		$this->vat_src_code     = $objp->vat_src_code;
2047
    		$this->tva_tx           = $objp->tva_tx;
2048
    		$this->localtax1_tx     = $objp->localtax1_tx;
2049
    		$this->localtax2_tx     = $objp->localtax2_tx;
2050
    		$this->localtax1_type   = $objp->localtax1_type;
2051
    		$this->localtax2_type   = $objp->localtax2_type;
2052
    		$this->remise_percent   = $objp->remise_percent;
2053
    		$this->fk_remise_except = $objp->fk_remise_except;
2054
    		$this->fk_product       = $objp->fk_product;
2055
    		$this->date_start_fill  = $objp->date_start_fill;
2056
    		$this->date_end_fill    = $objp->date_end_fill;
2057
    		$this->info_bits        = $objp->info_bits;
2058
    		$this->total_ht         = $objp->total_ht;
2059
    		$this->total_tva        = $objp->total_tva;
2060
    		$this->total_ttc        = $objp->total_ttc;
2061
    		$this->code_ventilation = $objp->fk_code_ventilation;
2062
    		$this->rang = $objp->rang;
2063
    		$this->special_code = $objp->special_code;
2064
    		$this->fk_unit          = $objp->fk_unit;
2065
    		$this->fk_contract_line = $objp->fk_contract_line;
2066
2067
2068
    		$this->db->free($result);
2069
    		return 1;
2070
    	}
2071
    	else
2072
    	{
2073
    		$this->error = $this->db->lasterror();
2074
    		return -3;
2075
    	}
2076
    }
2077
2078
2079
    /**
2080
     * 	Update a line to invoice_rec.
2081
     *
2082
     *  @param		User	$user					User
2083
     *  @param		int		$notrigger				No trigger
2084
     *	@return    	int             				<0 if KO, Id of line if OK
2085
     */
2086
    public function update(User $user, $notrigger = 0)
2087
    {
2088
    	global $conf;
2089
2090
    	$error = 0;
2091
2092
    	include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2093
2094
    	$sql = "UPDATE ".MAIN_DB_PREFIX."facturedet_rec SET";
2095
    	$sql .= " fk_facture = ".$this->fk_facture;
2096
    	$sql .= ", label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
2097
    	$sql .= ", description='".$this->db->escape($this->desc)."'";
2098
    	$sql .= ", price=".price2num($this->price);
2099
    	$sql .= ", qty=".price2num($this->qty);
2100
    	$sql .= ", tva_tx=".price2num($this->tva_tx);
2101
    	$sql .= ", vat_src_code='".$this->db->escape($this->vat_src_code)."'";
2102
    	$sql .= ", localtax1_tx=".price2num($this->localtax1_tx);
2103
    	$sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
2104
    	$sql .= ", localtax2_tx=".price2num($this->localtax2_tx);
2105
    	$sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
2106
    	$sql .= ", fk_product=".($this->fk_product > 0 ? $this->fk_product : "null");
2107
    	$sql .= ", product_type=".$this->product_type;
2108
    	$sql .= ", remise_percent='".price2num($this->remise_percent)."'";
2109
    	$sql .= ", subprice='".price2num($this->subprice)."'";
2110
    	$sql .= ", info_bits='".price2num($this->info_bits)."'";
2111
    	$sql .= ", date_start_fill=".(int) $this->date_start_fill;
2112
    	$sql .= ", date_end_fill=".(int) $this->date_end_fill;
2113
    	if (empty($this->skip_update_total)) {
2114
    		$sql .= ", total_ht=".price2num($this->total_ht);
2115
	    	$sql .= ", total_tva=".price2num($this->total_tva);
2116
	    	$sql .= ", total_localtax1=".price2num($this->total_localtax1);
2117
	    	$sql .= ", total_localtax2=".price2num($this->total_localtax2);
2118
	    	$sql .= ", total_ttc=".price2num($this->total_ttc);
2119
    	}
2120
    	$sql .= ", rang=".$this->rang;
2121
    	$sql .= ", special_code=".$this->special_code;
2122
    	$sql .= ", fk_unit=".($this->fk_unit ? "'".$this->db->escape($this->fk_unit)."'" : "null");
2123
    	$sql .= ", fk_contract_line=".($this->fk_contract_line ? $this->fk_contract_line : "null");
2124
    	$sql .= " WHERE rowid = ".$this->id;
2125
2126
    	dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
2127
    	$resql = $this->db->query($sql);
2128
        if ($resql)
2129
        {
2130
    		if (!$error)
2131
    		{
2132
    			$result = $this->insertExtraFields();
2133
    			if ($result < 0)
2134
    			{
2135
    				$error++;
2136
    			}
2137
    		}
2138
2139
    		if (!$error && !$notrigger)
2140
    		{
2141
    			// Call trigger
2142
    			$result = $this->call_trigger('LINEBILLREC_UPDATE', $user);
2143
    			if ($result < 0)
2144
    			{
2145
    				$this->db->rollback();
2146
    				return -2;
2147
    			}
2148
    			// End call triggers
2149
    		}
2150
            $this->db->commit();
2151
            return 1;
2152
        }
2153
        else
2154
        {
2155
            $this->error = $this->db->lasterror();
2156
            $this->db->rollback();
2157
            return -2;
2158
        }
2159
    }
2160
}
2161