1<?php
2/* Copyright (C) 2002-2004	Rodolphe Quiedeville	<rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2012	Laurent Destailleur		<eldy@users.sourceforge.net>
4 * Copyright (C) 2004		Christophe Combelles	<ccomb@free.fr>
5 * Copyright (C) 2005		Marc Barilley			<marc@ocebo.com>
6 * Copyright (C) 2005-2012	Regis Houssin			<regis.houssin@inodbox.com>
7 * Copyright (C) 2010-2020	Juanjo Menent			<jmenent@2byte.es>
8 * Copyright (C) 2013-2019	Philippe Grand			<philippe.grand@atoo-net.com>
9 * Copyright (C) 2013		Florian Henry			<florian.henry@open-concept.pro>
10 * Copyright (C) 2014-2016	Marcos García			<marcosgdf@gmail.com>
11 * Copyright (C) 2015		Bahfir Abbes			<bafbes@gmail.com>
12 * Copyright (C) 2015-2019	Ferran Marcet			<fmarcet@2byte.es>
13 * Copyright (C) 2016		Alexandre Spangaro		<aspangaro@open-dsi.fr>
14 * Copyright (C) 2018       Nicolas ZABOURI			<info@inovea-conseil.com>
15 * Copyright (C) 2018-2020  Frédéric France         <frederic.france@netlogic.fr>
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <https://www.gnu.org/licenses/>.
29 */
30
31/**
32 *  \file       htdocs/fourn/class/fournisseur.facture.class.php
33 *  \ingroup    fournisseur,facture
34 *  \brief      File of class to manage suppliers invoices
35 */
36
37require_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
38require_once DOL_DOCUMENT_ROOT.'/multicurrency/class/multicurrency.class.php';
39require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
40
41if (!empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT.'/core/class/html.formaccounting.class.php';
42if (!empty($conf->accounting->enabled)) require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
43
44/**
45 *	Class to manage suppliers invoices
46 */
47class FactureFournisseur extends CommonInvoice
48{
49	/**
50	 * @var string ID to identify managed object
51	 */
52	public $element = 'invoice_supplier';
53
54	/**
55	 * @var string Name of table without prefix where object is stored
56	 */
57	public $table_element = 'facture_fourn';
58
59	/**
60	 * @var string    Name of subtable line
61	 */
62	public $table_element_line = 'facture_fourn_det';
63
64	/**
65	 * @var string Field with ID of parent key if this field has a parent
66	 */
67	public $fk_element = 'fk_facture_fourn';
68
69	/**
70	 * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
71	 */
72	public $picto = 'supplier_invoice';
73
74	/**
75	 * 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
76	 * @var int
77	 */
78	public $ismultientitymanaged = 1;
79
80	/**
81	 * 0=Default, 1=View may be restricted to sales representative only if no permission to see all or to company of external user if external user
82	 * @var integer
83	 */
84	public $restrictiononfksoc = 1;
85
86	/**
87	 * {@inheritdoc}
88	 */
89	protected $table_ref_field = 'ref';
90
91	/**
92	 * @var int ID
93	 */
94	public $rowid;
95
96	/**
97	 * @var string Ref
98	 */
99	public $ref;
100
101	/**
102	 * @var string Ref supplier
103	 */
104	public $ref_supplier;
105
106	/**
107	 * @var string Label of invoice
108	 */
109	public $label;
110
111	public $socid;
112
113	//Check constants for types
114	public $type = self::TYPE_STANDARD;
115
116	/**
117	 * Supplier invoice status
118	 * @var int
119	 * @see FactureFournisseur::STATUS_DRAFT, FactureFournisseur::STATUS_VALIDATED, FactureFournisseur::STATUS_PAID, FactureFournisseur::STATUS_ABANDONED
120	 */
121	public $statut;
122
123	/**
124	 * Set to 1 if the invoice is completely paid, otherwise is 0
125	 * @var int
126	 */
127	public $paye;
128
129	public $author;
130
131	/**
132	 * Date creation record (datec)
133	 *
134	 * @var integer
135	 */
136	public $datec;
137
138	/**
139	 * Date modification record (tms)
140	 *
141	 * @var integer
142	 */
143	public $tms;
144
145	/**
146	 * Invoice date (date)
147	 *
148	 * @var integer
149	 */
150	public $date;
151
152	/**
153	 * Max payment date (date_echeance)
154	 *
155	 * @var integer
156	 */
157	public $date_echeance;
158
159	public $amount = 0;
160	public $remise = 0;
161	public $tva = 0;
162	public $localtax1;
163	public $localtax2;
164	public $total_ht = 0;
165	public $total_tva = 0;
166	public $total_localtax1 = 0;
167	public $total_localtax2 = 0;
168	public $total_ttc = 0;
169
170	/**
171	 * @deprecated
172	 * @see $note_private, $note_public
173	 */
174	public $note;
175
176	public $note_private;
177	public $note_public;
178	public $propalid;
179
180	public $cond_reglement_id;
181	public $cond_reglement_code;
182	public $cond_reglement_label;
183	public $cond_reglement_doc;
184
185	/**
186	 * @var int ID
187	 */
188	public $fk_account;
189
190	public $mode_reglement_id;
191	public $mode_reglement_code;
192
193	public $extraparams = array();
194
195	/**
196	 * Invoice lines
197	 * @var SupplierInvoiceLine[]
198	 */
199	public $lines = array();
200
201	/**
202	 * @deprecated
203	 */
204	public $fournisseur;
205
206	// Multicurrency
207	/**
208	 * @var int ID
209	 */
210	public $fk_multicurrency;
211
212	public $multicurrency_code;
213	public $multicurrency_tx;
214	public $multicurrency_total_ht;
215	public $multicurrency_total_tva;
216	public $multicurrency_total_ttc;
217	//! id of source var_dump($$this);invoice if replacement invoice or credit note
218	/**
219	 * @var int ID
220	 */
221	public $fk_facture_source;
222
223
224	public $fields = array(
225		'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
226		'ref' =>array('type'=>'varchar(255)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'showoncombobox'=>1, 'position'=>15),
227		'ref_supplier' =>array('type'=>'varchar(255)', 'label'=>'RefSupplier', 'enabled'=>1, 'visible'=>-1, 'position'=>20),
228		'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>25, 'index'=>1),
229		'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'RefExt', 'enabled'=>1, 'visible'=>0, 'position'=>30),
230		'type' =>array('type'=>'smallint(6)', 'label'=>'Type', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>35),
231		'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>40),
232		'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>45),
233		'datef' =>array('type'=>'date', 'label'=>'Date', 'enabled'=>1, 'visible'=>-1, 'position'=>50),
234		'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>55),
235		'libelle' =>array('type'=>'varchar(255)', 'label'=>'Label', 'enabled'=>1, 'visible'=>-1, 'position'=>60),
236		'paye' =>array('type'=>'smallint(6)', 'label'=>'Paye', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>65),
237		'amount' =>array('type'=>'double(24,8)', 'label'=>'Amount', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>70),
238		'remise' =>array('type'=>'double(24,8)', 'label'=>'Discount', 'enabled'=>1, 'visible'=>-1, 'position'=>75),
239		'close_code' =>array('type'=>'varchar(16)', 'label'=>'CloseCode', 'enabled'=>1, 'visible'=>-1, 'position'=>80),
240		'close_note' =>array('type'=>'varchar(128)', 'label'=>'CloseNote', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
241		'tva' =>array('type'=>'double(24,8)', 'label'=>'Tva', 'enabled'=>1, 'visible'=>-1, 'position'=>90),
242		'localtax1' =>array('type'=>'double(24,8)', 'label'=>'Localtax1', 'enabled'=>1, 'visible'=>-1, 'position'=>95),
243		'localtax2' =>array('type'=>'double(24,8)', 'label'=>'Localtax2', 'enabled'=>1, 'visible'=>-1, 'position'=>100),
244		'total_ht' =>array('type'=>'double(24,8)', 'label'=>'TotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>105),
245		'total_tva' =>array('type'=>'double(24,8)', 'label'=>'TotalVAT', 'enabled'=>1, 'visible'=>-1, 'position'=>110),
246		'total_ttc' =>array('type'=>'double(24,8)', 'label'=>'TotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>115),
247		'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
248		'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>130),
249		'fk_user_valid' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>135),
250		'fk_facture_source' =>array('type'=>'integer', 'label'=>'Fk facture source', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
251		'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label'=>'Project', 'enabled'=>1, 'visible'=>-1, 'position'=>145),
252		'fk_account' =>array('type'=>'integer', 'label'=>'Account', 'enabled'=>1, 'visible'=>-1, 'position'=>150),
253		'fk_cond_reglement' =>array('type'=>'integer', 'label'=>'PaymentTerm', 'enabled'=>1, 'visible'=>-1, 'position'=>155),
254		'fk_mode_reglement' =>array('type'=>'integer', 'label'=>'PaymentMode', 'enabled'=>1, 'visible'=>-1, 'position'=>160),
255		'date_lim_reglement' =>array('type'=>'date', 'label'=>'DateLimReglement', 'enabled'=>1, 'visible'=>-1, 'position'=>165),
256		'note_private' =>array('type'=>'text', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>170),
257		'note_public' =>array('type'=>'text', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>175),
258		'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'ModelPdf', 'enabled'=>1, 'visible'=>0, 'position'=>180),
259		'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>190),
260		'fk_incoterms' =>array('type'=>'integer', 'label'=>'IncotermCode', 'enabled'=>1, 'visible'=>-1, 'position'=>195),
261		'location_incoterms' =>array('type'=>'varchar(255)', 'label'=>'IncotermLocation', 'enabled'=>1, 'visible'=>-1, 'position'=>200),
262		'fk_multicurrency' =>array('type'=>'integer', 'label'=>'MulticurrencyId', 'enabled'=>1, 'visible'=>-1, 'position'=>205),
263		'multicurrency_code' =>array('type'=>'varchar(255)', 'label'=>'MulticurrencyCode', 'enabled'=>1, 'visible'=>-1, 'position'=>210),
264		'multicurrency_tx' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyRate', 'enabled'=>1, 'visible'=>-1, 'position'=>215),
265		'multicurrency_total_ht' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyTotalHT', 'enabled'=>1, 'visible'=>-1, 'position'=>220),
266		'multicurrency_total_tva' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyTotalVAT', 'enabled'=>1, 'visible'=>-1, 'position'=>225),
267		'multicurrency_total_ttc' =>array('type'=>'double(24,8)', 'label'=>'MulticurrencyTotalTTC', 'enabled'=>1, 'visible'=>-1, 'position'=>230),
268		'date_pointoftax' =>array('type'=>'date', 'label'=>'Date pointoftax', 'enabled'=>1, 'visible'=>-1, 'position'=>235),
269		'date_valid' =>array('type'=>'date', 'label'=>'DateValidation', 'enabled'=>1, 'visible'=>-1, 'position'=>240),
270		'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'Last main doc', 'enabled'=>1, 'visible'=>-1, 'position'=>245),
271		'fk_statut' =>array('type'=>'smallint(6)', 'label'=>'Status', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>500),
272		'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>900),
273	);
274
275
276	/**
277	 * Standard invoice
278	 */
279	const TYPE_STANDARD = 0;
280
281	/**
282	 * Replacement invoice
283	 */
284	const TYPE_REPLACEMENT = 1;
285
286	/**
287	 * Credit note invoice
288	 */
289	const TYPE_CREDIT_NOTE = 2;
290
291	/**
292	 * Deposit invoice
293	 */
294	const TYPE_DEPOSIT = 3;
295
296	/**
297	 * Draft
298	 */
299	const STATUS_DRAFT = 0;
300
301	/**
302	 * Validated (need to be paid)
303	 */
304	const STATUS_VALIDATED = 1;
305
306	/**
307	 * Classified paid.
308	 * If paid partially, $this->close_code can be:
309	 * - CLOSECODE_DISCOUNTVAT
310	 * - CLOSECODE_BADDEBT
311	 * If paid completelly, this->close_code will be null
312	 */
313	const STATUS_CLOSED = 2;
314
315	/**
316	 * Classified abandoned and no payment done.
317	 * $this->close_code can be:
318	 * - CLOSECODE_BADDEBT
319	 * - CLOSECODE_ABANDONED
320	 * - CLOSECODE_REPLACED
321	 */
322	const STATUS_ABANDONED = 3;
323
324	const CLOSECODE_DISCOUNTVAT = 'discount_vat';
325	const CLOSECODE_BADCREDIT = 'badsupplier';
326	const CLOSECODE_ABANDONED = 'abandon';
327	const CLOSECODE_REPLACED = 'replaced';
328
329	/**
330	 *	Constructor
331	 *
332	 *  @param		DoliDB		$db      Database handler
333	 */
334	public function __construct($db)
335	{
336		$this->db = $db;
337	}
338
339	/**
340	 *    Create supplier invoice into database
341	 *
342	 *    @param      User		$user       user object that creates
343	 *    @return     int    	     		Id invoice created if OK, < 0 if KO
344	 */
345	public function create($user)
346	{
347		global $langs, $conf, $hookmanager;
348
349		$error = 0;
350		$now = dol_now();
351
352		// Clean parameters
353		if (isset($this->ref_supplier)) $this->ref_supplier = trim($this->ref_supplier);
354		if (empty($this->type)) $this->type = self::TYPE_STANDARD;
355		if (empty($this->date)) $this->date = $now;
356
357		$socid = $this->socid;
358		$ref_supplier = $this->ref_supplier;
359		$amount = $this->amount;
360		$remise = $this->remise;
361
362		// Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate)
363		if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date);
364		else $this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code);
365		if (empty($this->fk_multicurrency))
366		{
367			$this->multicurrency_code = $conf->currency;
368			$this->fk_multicurrency = 0;
369			$this->multicurrency_tx = 1;
370		}
371
372		$this->db->begin();
373
374		if (!$remise) $remise = 0;
375
376		$sql = "INSERT INTO ".MAIN_DB_PREFIX."facture_fourn (";
377		$sql .= "ref";
378		$sql .= ", ref_supplier";
379		$sql .= ", entity";
380		$sql .= ", type";
381		$sql .= ", libelle";
382		$sql .= ", fk_soc";
383		$sql .= ", datec";
384		$sql .= ", datef";
385		$sql .= ", fk_projet";
386		$sql .= ", fk_cond_reglement";
387		$sql .= ", fk_mode_reglement";
388		$sql .= ", fk_account";
389		$sql .= ", note_private";
390		$sql .= ", note_public";
391		$sql .= ", fk_user_author";
392		$sql .= ", date_lim_reglement";
393		$sql .= ", fk_incoterms, location_incoterms";
394		$sql .= ", fk_multicurrency";
395		$sql .= ", multicurrency_code";
396		$sql .= ", multicurrency_tx";
397		$sql .= ", fk_facture_source";
398		$sql .= ")";
399		$sql .= " VALUES (";
400		$sql .= "'(PROV)'";
401		$sql .= ", '".$this->db->escape($this->ref_supplier)."'";
402		$sql .= ", ".$conf->entity;
403		$sql .= ", '".$this->db->escape($this->type)."'";
404		$sql .= ", '".$this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : ''))."'";
405		$sql .= ", ".$this->socid;
406		$sql .= ", '".$this->db->idate($now)."'";
407		$sql .= ", '".$this->db->idate($this->date)."'";
408		$sql .= ", ".($this->fk_project > 0 ? $this->fk_project : "null");
409		$sql .= ", ".($this->cond_reglement_id > 0 ? $this->cond_reglement_id : "null");
410		$sql .= ", ".($this->mode_reglement_id > 0 ? $this->mode_reglement_id : "null");
411		$sql .= ", ".($this->fk_account > 0 ? $this->fk_account : 'NULL');
412		$sql .= ", '".$this->db->escape($this->note_private)."'";
413		$sql .= ", '".$this->db->escape($this->note_public)."'";
414		$sql .= ", ".$user->id.",";
415		$sql .= $this->date_echeance != '' ? "'".$this->db->idate($this->date_echeance)."'" : "null";
416		$sql .= ", ".(int) $this->fk_incoterms;
417		$sql .= ", '".$this->db->escape($this->location_incoterms)."'";
418		$sql .= ", ".(int) $this->fk_multicurrency;
419		$sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
420		$sql .= ", ".(double) $this->multicurrency_tx;
421		$sql .= ", ".(isset($this->fk_facture_source) ? $this->fk_facture_source : "NULL");
422		$sql .= ")";
423
424		dol_syslog(get_class($this)."::create", LOG_DEBUG);
425		$resql = $this->db->query($sql);
426		if ($resql)
427		{
428			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn');
429
430			// Update ref with new one
431			$this->ref = '(PROV'.$this->id.')';
432			$sql = 'UPDATE '.MAIN_DB_PREFIX."facture_fourn SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".$this->id;
433
434			dol_syslog(get_class($this)."::create", LOG_DEBUG);
435			$resql = $this->db->query($sql);
436			if (!$resql) $error++;
437
438			if (!empty($this->linkedObjectsIds) && empty($this->linked_objects))	// To use new linkedObjectsIds instead of old linked_objects
439			{
440				$this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
441			}
442
443			// Add object linked
444			if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects))
445			{
446				foreach ($this->linked_objects as $origin => $tmp_origin_id)
447				{
448					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, ...))
449					{
450						foreach ($tmp_origin_id as $origin_id)
451						{
452							$ret = $this->add_object_linked($origin, $origin_id);
453							if (!$ret)
454							{
455								dol_print_error($this->db);
456								$error++;
457							}
458						}
459					} else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
460					{
461						$origin_id = $tmp_origin_id;
462						$ret = $this->add_object_linked($origin, $origin_id);
463						if (!$ret)
464						{
465							dol_print_error($this->db);
466							$error++;
467						}
468					}
469				}
470			}
471
472			if (count($this->lines) && is_object($this->lines[0]))	// If this->lines is array of InvoiceLines (preferred mode)
473			{
474				dol_syslog("There is ".count($this->lines)." lines that are invoice lines objects");
475				foreach ($this->lines as $i => $val)
476				{
477					$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code)';
478					$sql .= ' VALUES ('.$this->id.','.intval($this->lines[$i]->special_code).')';
479
480					$resql_insert = $this->db->query($sql);
481					if ($resql_insert)
482					{
483						$idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
484
485						$this->updateline(
486							$idligne,
487							$this->lines[$i]->description,
488							$this->lines[$i]->pu_ht,
489							$this->lines[$i]->tva_tx.($this->lines[$i]->vat_src_code ? ' ('.$this->lines[$i]->vat_src_code.')' : ''),
490							$this->lines[$i]->localtax1_tx,
491							$this->lines[$i]->localtax2_tx,
492							$this->lines[$i]->qty,
493							$this->lines[$i]->fk_product,
494							'HT',
495							(!empty($this->lines[$i]->info_bits) ? $this->lines[$i]->info_bits : ''),
496							$this->lines[$i]->product_type,
497							$this->lines[$i]->remise_percent,
498							false,
499							$this->lines[$i]->date_start,
500							$this->lines[$i]->date_end,
501							$this->lines[$i]->array_options,
502							$this->lines[$i]->fk_unit,
503							$this->lines[$i]->multicurrency_subprice,
504							$this->lines[$i]->ref_supplier
505						);
506					} else {
507						$this->error = $this->db->lasterror();
508						$this->db->rollback();
509						return -5;
510					}
511				}
512			} else // If this->lines is an array of invoice line arrays
513			{
514				dol_syslog("There is ".count($this->lines)." lines that are array lines");
515				foreach ($this->lines as $i => $val)
516				{
517					$line = $this->lines[$i];
518
519					// Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
520					//if (! is_object($line)) $line=json_decode(json_encode($line), false);  // convert recursively array into object.
521					if (!is_object($line)) $line = (object) $line;
522
523					$sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facture_fourn_det (fk_facture_fourn, special_code)';
524					$sql .= ' VALUES ('.$this->id.','.intval($this->lines[$i]->special_code).')';
525
526					$resql_insert = $this->db->query($sql);
527					if ($resql_insert)
528					{
529						$idligne = $this->db->last_insert_id(MAIN_DB_PREFIX.'facture_fourn_det');
530
531						$this->updateline(
532							$idligne,
533							$line->description,
534							$line->pu_ht,
535							$line->tva_tx,
536							$line->localtax1_tx,
537							$line->localtax2_tx,
538							$line->qty,
539							$line->fk_product,
540							'HT',
541							(!empty($line->info_bits) ? $line->info_bits : ''),
542							$line->product_type,
543							$line->remise_percent,
544							0,
545							$line->date_start,
546							$line->date_end,
547							$line->array_options,
548							$line->fk_unit,
549							$line->multicurrency_subprice,
550							$line->ref_supplier
551						);
552					} else {
553						$this->error = $this->db->lasterror();
554						$this->db->rollback();
555						return -5;
556					}
557				}
558			}
559
560			// Update total price
561			$result = $this->update_price();
562			if ($result > 0)
563			{
564				// Actions on extra fields
565				if (!$error)
566				{
567					$result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found
568					if ($result < 0)
569					{
570						$error++;
571					}
572				}
573
574				if (!$error)
575				{
576					// Call trigger
577					$result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user);
578					if ($result < 0) $error++;
579					// End call triggers
580				}
581
582				if (!$error)
583				{
584					$this->db->commit();
585					return $this->id;
586				} else {
587					$this->db->rollback();
588					return -4;
589				}
590			} else {
591				$this->error = $langs->trans('FailedToUpdatePrice');
592				$this->db->rollback();
593				return -3;
594			}
595		} else {
596			if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
597			{
598				$this->error = $langs->trans('ErrorRefAlreadyExists');
599				$this->db->rollback();
600				return -1;
601			} else {
602				$this->error = $this->db->lasterror();
603				$this->db->rollback();
604				return -2;
605			}
606		}
607	}
608
609	/**
610	 *    Load object in memory from database
611	 *
612	 *    @param	int		$id         Id supplier invoice
613	 *    @param	string	$ref		Ref supplier invoice
614	 *    @return   int        			<0 if KO, >0 if OK, 0 if not found
615	 */
616	public function fetch($id = '', $ref = '')
617	{
618		global $langs;
619
620		$sql = "SELECT";
621		$sql .= " t.rowid,";
622		$sql .= " t.ref,";
623		$sql .= " t.ref_supplier,";
624		$sql .= " t.entity,";
625		$sql .= " t.type,";
626		$sql .= " t.fk_soc,";
627		$sql .= " t.datec,";
628		$sql .= " t.datef,";
629		$sql .= " t.tms,";
630		$sql .= " t.libelle as label,";
631		$sql .= " t.paye,";
632		$sql .= " t.amount,";
633		$sql .= " t.remise,";
634		$sql .= " t.close_code,";
635		$sql .= " t.close_note,";
636		$sql .= " t.tva,";
637		$sql .= " t.localtax1,";
638		$sql .= " t.localtax2,";
639		$sql .= " t.total_ht,";
640		$sql .= " t.total_tva,";
641		$sql .= " t.total_ttc,";
642		$sql .= " t.fk_statut,";
643		$sql .= " t.fk_user_author,";
644		$sql .= " t.fk_user_valid,";
645		$sql .= " t.fk_facture_source,";
646		$sql .= " t.fk_projet as fk_project,";
647		$sql .= " t.fk_cond_reglement,";
648		$sql .= " t.fk_account,";
649		$sql .= " t.fk_mode_reglement,";
650		$sql .= " t.date_lim_reglement,";
651		$sql .= " t.note_private,";
652		$sql .= " t.note_public,";
653		$sql .= " t.model_pdf,";
654		$sql .= " t.import_key,";
655		$sql .= " t.extraparams,";
656		$sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,";
657		$sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,";
658		$sql .= ' s.nom as socnom, s.rowid as socid,';
659		$sql .= ' t.fk_incoterms, t.location_incoterms,';
660		$sql .= " i.libelle as label_incoterms,";
661		$sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc';
662		$sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as t';
663		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON (t.fk_soc = s.rowid)";
664		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_payment_term as cr ON t.fk_cond_reglement = cr.rowid";
665		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as p ON t.fk_mode_reglement = p.id";
666		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_incoterms as i ON t.fk_incoterms = i.rowid';
667		if ($id)  $sql .= " WHERE t.rowid=".$id;
668		if ($ref) $sql .= " WHERE t.ref='".$this->db->escape($ref)."' AND t.entity IN (".getEntity('supplier_invoice').")";
669
670		dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
671		$resql = $this->db->query($sql);
672		if ($resql)
673		{
674			if ($this->db->num_rows($resql))
675			{
676				$obj = $this->db->fetch_object($resql);
677
678				$this->id = $obj->rowid;
679				$this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility
680
681				$this->ref_supplier = $obj->ref_supplier;
682				$this->entity				= $obj->entity;
683				$this->type					= empty($obj->type) ? self::TYPE_STANDARD : $obj->type;
684				$this->fk_soc				= $obj->fk_soc;
685				$this->datec				= $this->db->jdate($obj->datec);
686				$this->date					= $this->db->jdate($obj->datef);
687				$this->datep				= $this->db->jdate($obj->datef);
688				$this->tms = $this->db->jdate($obj->tms);
689				$this->libelle = $obj->label; // deprecated
690				$this->label				= $obj->label;
691				$this->paye					= $obj->paye;
692				$this->paid					= $obj->paye;
693				$this->amount				= $obj->amount;
694				$this->remise				= $obj->remise;
695				$this->close_code			= $obj->close_code;
696				$this->close_note			= $obj->close_note;
697				$this->tva = $obj->tva;
698				$this->total_localtax1		= $obj->localtax1;
699				$this->total_localtax2		= $obj->localtax2;
700				$this->total_ht				= $obj->total_ht;
701				$this->total_tva			= $obj->total_tva;
702				$this->total_ttc			= $obj->total_ttc;
703				$this->fk_statut			= $obj->fk_statut;
704				$this->statut				= $obj->fk_statut;
705				$this->fk_user_author = $obj->fk_user_author;
706				$this->author				= $obj->fk_user_author;
707				$this->fk_user_valid = $obj->fk_user_valid;
708				$this->fk_facture_source	= $obj->fk_facture_source;
709				$this->fk_project = $obj->fk_project;
710				$this->cond_reglement_id	= $obj->fk_cond_reglement;
711				$this->cond_reglement_code = $obj->cond_reglement_code;
712				$this->cond_reglement = $obj->cond_reglement_label;			// deprecated
713				$this->cond_reglement_label = $obj->cond_reglement_label;
714				$this->cond_reglement_doc = $obj->cond_reglement_doc;
715				$this->fk_account = $obj->fk_account;
716				$this->mode_reglement_id = $obj->fk_mode_reglement;
717				$this->mode_reglement_code = $obj->mode_reglement_code;
718				$this->mode_reglement = $obj->mode_reglement_label;
719				$this->date_echeance		= $this->db->jdate($obj->date_lim_reglement);
720				$this->note = $obj->note_private; // deprecated
721				$this->note_private			= $obj->note_private;
722				$this->note_public = $obj->note_public;
723				$this->model_pdf = $obj->model_pdf;
724				$this->modelpdf = $obj->model_pdf; // deprecated
725				$this->import_key = $obj->import_key;
726
727				//Incoterms
728				$this->fk_incoterms = $obj->fk_incoterms;
729				$this->location_incoterms = $obj->location_incoterms;
730				$this->label_incoterms = $obj->label_incoterms;
731
732				// Multicurrency
733				$this->fk_multicurrency 		= $obj->fk_multicurrency;
734				$this->multicurrency_code = $obj->multicurrency_code;
735				$this->multicurrency_tx 		= $obj->multicurrency_tx;
736				$this->multicurrency_total_ht = $obj->multicurrency_total_ht;
737				$this->multicurrency_total_tva 	= $obj->multicurrency_total_tva;
738				$this->multicurrency_total_ttc 	= $obj->multicurrency_total_ttc;
739
740				$this->extraparams = (array) json_decode($obj->extraparams, true);
741
742				$this->socid  = $obj->socid;
743				$this->socnom = $obj->socnom;
744
745				// Retrieve all extrafield
746				// fetch optionals attributes and labels
747				$this->fetch_optionals();
748
749				if ($this->statut == self::STATUS_DRAFT) $this->brouillon = 1;
750
751				$result = $this->fetch_lines();
752				if ($result < 0)
753				{
754					$this->error = $this->db->lasterror();
755					return -3;
756				}
757			} else {
758				$this->error = 'Bill with id '.$id.' not found';
759				dol_syslog(get_class($this).'::fetch '.$this->error);
760				return 0;
761			}
762
763			$this->db->free($resql);
764			return 1;
765		} else {
766			$this->error = "Error ".$this->db->lasterror();
767			return -1;
768		}
769	}
770
771
772	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
773	/**
774	 *	Load this->lines
775	 *
776	 *  @return     int         1 si ok, < 0 si erreur
777	 */
778	public function fetch_lines()
779	{
780		// phpcs:enable
781		$this->lines = array();
782
783		$sql = 'SELECT f.rowid, f.ref as ref_supplier, f.description, f.date_start, f.date_end, f.pu_ht, f.pu_ttc, f.qty, f.remise_percent, f.vat_src_code, f.tva_tx';
784		$sql .= ', f.localtax1_tx, f.localtax2_tx, f.localtax1_type, f.localtax2_type, f.total_localtax1, f.total_localtax2, f.fk_facture_fourn ';
785		$sql .= ', f.total_ht, f.tva as total_tva, f.total_ttc, f.fk_product, f.product_type, f.info_bits, f.rang, f.special_code, f.fk_parent_line, f.fk_unit';
786		$sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as label, p.description as product_desc';
787		$sql .= ', f.fk_code_ventilation, f.fk_multicurrency, f.multicurrency_code, f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, f.multicurrency_total_ttc';
788		$sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
789		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
790		$sql .= ' WHERE fk_facture_fourn='.$this->id;
791		$sql .= ' ORDER BY f.rang, f.rowid';
792
793		dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
794		$resql_rows = $this->db->query($sql);
795		if ($resql_rows)
796		{
797			$num_rows = $this->db->num_rows($resql_rows);
798			if ($num_rows)
799			{
800				$i = 0;
801				while ($i < $num_rows)
802				{
803					$obj = $this->db->fetch_object($resql_rows);
804
805					$line = new SupplierInvoiceLine($this->db);
806
807					$line->id = $obj->rowid;
808					$line->rowid = $obj->rowid;
809					$line->description = $obj->description;
810					$line->date_start = $obj->date_start;
811					$line->date_end = $obj->date_end;
812
813					$line->product_ref = $obj->product_ref;
814					$line->ref = $obj->product_ref;
815					$line->ref_supplier		= $obj->ref_supplier;
816					$line->libelle			= $obj->label;
817					$line->label  			= $obj->label;
818					$line->product_desc		= $obj->product_desc;
819					$line->subprice = $obj->pu_ht;
820					$line->pu_ht = $obj->pu_ht;
821					$line->pu_ttc			= $obj->pu_ttc;
822
823					$line->vat_src_code = $obj->vat_src_code;
824					$line->tva_tx			= $obj->tva_tx;
825					$line->localtax1_tx		= $obj->localtax1_tx;
826					$line->localtax2_tx		= $obj->localtax2_tx;
827					$line->localtax1_type	= $obj->localtax1_type;
828					$line->localtax2_type	= $obj->localtax2_type;
829					$line->qty				= $obj->qty;
830					$line->remise_percent = $obj->remise_percent;
831					$line->tva				= $obj->total_tva; // deprecated
832					$line->total_ht			= $obj->total_ht;
833					$line->total_ttc		= $obj->total_ttc;
834					$line->total_tva		= $obj->total_tva;
835					$line->total_localtax1	= $obj->total_localtax1;
836					$line->total_localtax2	= $obj->total_localtax2;
837					$line->fk_facture_fourn = $obj->fk_facture_fourn;
838					$line->fk_product = $obj->fk_product;
839					$line->product_type		= $obj->product_type;
840					$line->product_label	= $obj->label;
841					$line->info_bits = $obj->info_bits;
842					$line->fk_parent_line   = $obj->fk_parent_line;
843					$line->special_code		= $obj->special_code;
844					$line->rang = $obj->rang;
845					$line->fk_unit          = $obj->fk_unit;
846
847					// Accountancy
848					$line->code_ventilation = $obj->fk_code_ventilation;
849					$line->fk_accounting_account = $obj->fk_code_ventilation;
850
851					// Multicurrency
852					$line->fk_multicurrency = $obj->fk_multicurrency;
853					$line->multicurrency_code = $obj->multicurrency_code;
854					$line->multicurrency_subprice = $obj->multicurrency_subprice;
855					$line->multicurrency_total_ht = $obj->multicurrency_total_ht;
856					$line->multicurrency_total_tva = $obj->multicurrency_total_tva;
857					$line->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
858
859					// Extra fields
860					$line->fetch_optionals();
861
862					$this->lines[$i] = $line;
863
864					$i++;
865				}
866			}
867			$this->db->free($resql_rows);
868			return 1;
869		} else {
870			$this->error = $this->db->error();
871			return -3;
872		}
873	}
874
875
876	/**
877	 *  Update database
878	 *
879	 *  @param	User	$user            User that modify
880	 *  @param  int		$notrigger       0=launch triggers after, 1=disable triggers
881	 *  @return int 			         <0 if KO, >0 if OK
882	 */
883	public function update($user = null, $notrigger = 0)
884	{
885		global $conf, $langs;
886		$error = 0;
887
888		// Clean parameters
889		if (empty($this->type)) $this->type = self::TYPE_STANDARD;
890		if (isset($this->ref)) $this->ref = trim($this->ref);
891		if (isset($this->ref_supplier)) $this->ref_supplier = trim($this->ref_supplier);
892		if (isset($this->entity)) $this->entity = trim($this->entity);
893		if (isset($this->type)) $this->type = trim($this->type);
894		if (isset($this->fk_soc)) $this->fk_soc = trim($this->fk_soc);
895		if (isset($this->label)) $this->label = trim($this->label);
896		if (isset($this->libelle)) $this->libelle = trim($this->libelle); // deprecated
897		if (isset($this->paye)) $this->paye = trim($this->paye);
898		if (isset($this->amount)) $this->amount = trim($this->amount);
899		if (isset($this->remise)) $this->remise = trim($this->remise);
900		if (isset($this->close_code)) $this->close_code = trim($this->close_code);
901		if (isset($this->close_note)) $this->close_note = trim($this->close_note);
902		if (isset($this->tva)) $this->tva = trim($this->tva);
903		if (isset($this->localtax1)) $this->localtax1 = trim($this->localtax1);
904		if (isset($this->localtax2)) $this->localtax2 = trim($this->localtax2);
905		if (empty($this->total_ht)) $this->total_ht = 0;
906		if (empty($this->total_tva)) $this->total_tva = 0;
907		//	if (isset($this->total_localtax1)) $this->total_localtax1=trim($this->total_localtax1);
908		//	if (isset($this->total_localtax2)) $this->total_localtax2=trim($this->total_localtax2);
909		if (isset($this->total_ttc)) $this->total_ttc = trim($this->total_ttc);
910		if (isset($this->statut)) $this->statut = (int) $this->statut;
911		if (isset($this->author)) $this->author = trim($this->author);
912		if (isset($this->fk_user_valid)) $this->fk_user_valid = trim($this->fk_user_valid);
913		if (isset($this->fk_facture_source)) $this->fk_facture_source = trim($this->fk_facture_source);
914		if (isset($this->fk_project)) $this->fk_project = trim($this->fk_project);
915		if (isset($this->cond_reglement_id)) $this->cond_reglement_id = trim($this->cond_reglement_id);
916		if (isset($this->note_private)) $this->note = trim($this->note_private);
917		if (isset($this->note_public)) $this->note_public = trim($this->note_public);
918		if (isset($this->model_pdf)) $this->model_pdf = trim($this->model_pdf);
919		if (isset($this->import_key)) $this->import_key = trim($this->import_key);
920
921
922		// Check parameters
923		// Put here code to add control on parameters values
924
925		// Update request
926		$sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn SET";
927		$sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
928		$sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
929		$sql .= " entity=".(isset($this->entity) ? $this->entity : "null").",";
930		$sql .= " type=".(isset($this->type) ? $this->type : "null").",";
931		$sql .= " fk_soc=".(isset($this->fk_soc) ? $this->fk_soc : "null").",";
932		$sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
933		$sql .= " datef=".(dol_strlen($this->date) != 0 ? "'".$this->db->idate($this->date)."'" : 'null').",";
934		if (dol_strlen($this->tms) != 0) $sql .= " tms=".(dol_strlen($this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
935		$sql .= " libelle=".(isset($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
936		$sql .= " paye=".(isset($this->paye) ? $this->paye : "null").",";
937		$sql .= " amount=".(isset($this->amount) ? $this->amount : "null").",";
938		$sql .= " remise=".(isset($this->remise) ? $this->remise : "null").",";
939		$sql .= " close_code=".(isset($this->close_code) ? "'".$this->db->escape($this->close_code)."'" : "null").",";
940		$sql .= " close_note=".(isset($this->close_note) ? "'".$this->db->escape($this->close_note)."'" : "null").",";
941		$sql .= " tva=".(isset($this->tva) ? $this->tva : "null").",";
942		$sql .= " localtax1=".(isset($this->localtax1) ? $this->localtax1 : "null").",";
943		$sql .= " localtax2=".(isset($this->localtax2) ? $this->localtax2 : "null").",";
944		$sql .= " total_ht=".(isset($this->total_ht) ? $this->total_ht : "null").",";
945		$sql .= " total_tva=".(isset($this->total_tva) ? $this->total_tva : "null").",";
946		$sql .= " total_ttc=".(isset($this->total_ttc) ? $this->total_ttc : "null").",";
947		$sql .= " fk_statut=".(isset($this->statut) ? $this->statut : "null").",";
948		$sql .= " fk_user_author=".(isset($this->author) ? $this->author : "null").",";
949		$sql .= " fk_user_valid=".(isset($this->fk_user_valid) ? $this->fk_user_valid : "null").",";
950		$sql .= " fk_facture_source=".(isset($this->fk_facture_source) ? $this->fk_facture_source : "null").",";
951		$sql .= " fk_projet=".(isset($this->fk_project) ? $this->fk_project : "null").",";
952		$sql .= " fk_cond_reglement=".(isset($this->cond_reglement_id) ? $this->cond_reglement_id : "null").",";
953		$sql .= " date_lim_reglement=".(dol_strlen($this->date_echeance) != 0 ? "'".$this->db->idate($this->date_echeance)."'" : 'null').",";
954		$sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
955		$sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
956		$sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
957		$sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null")."";
958		$sql .= " WHERE rowid=".$this->id;
959
960		$this->db->begin();
961
962		dol_syslog(get_class($this)."::update", LOG_DEBUG);
963		$resql = $this->db->query($sql);
964
965		if (!$resql) {
966			$error++;
967
968			if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
969				$this->errors[] = $langs->trans('ErrorRefAlreadyExists');
970			} else {
971				$this->errors[] = "Error ".$this->db->lasterror();
972			}
973		}
974
975		if (!$error)
976		{
977			$result = $this->insertExtraFields();
978			if ($result < 0)
979			{
980				$error++;
981			}
982		}
983
984		if (!$error)
985		{
986			if (!$notrigger)
987			{
988				// Call trigger
989				$result = $this->call_trigger('BILL_SUPPLIER_UPDATE', $user);
990				if ($result < 0) $error++;
991				// End call triggers
992			}
993		}
994
995		// Commit or rollback
996		if ($error)
997		{
998			foreach ($this->errors as $errmsg)
999			{
1000				dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1001				$this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1002			}
1003			$this->db->rollback();
1004			return -1 * $error;
1005		} else {
1006			$this->db->commit();
1007			return 1;
1008		}
1009	}
1010
1011	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1012	/**
1013	 *    Add a discount line into an invoice (as an invoice line) using an existing absolute discount (Consume the discount)
1014	 *
1015	 *    @param     int	$idremise	Id of absolute discount
1016	 *    @return    int          		>0 if OK, <0 if KO
1017	 */
1018	public function insert_discount($idremise)
1019	{
1020		// phpcs:enable
1021		global $langs;
1022
1023		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1024		include_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
1025
1026		$this->db->begin();
1027
1028		$remise = new DiscountAbsolute($this->db);
1029		$result = $remise->fetch($idremise);
1030
1031		if ($result > 0)
1032		{
1033			if ($remise->fk_invoice_supplier)	// Protection against multiple submission
1034			{
1035				$this->error = $langs->trans("ErrorDiscountAlreadyUsed");
1036				$this->db->rollback();
1037				return -5;
1038			}
1039
1040			$facligne = new SupplierInvoiceLine($this->db);
1041			$facligne->fk_facture_fourn = $this->id;
1042			$facligne->fk_remise_except = $remise->id;
1043			$facligne->desc = $remise->description; // Description ligne
1044			$facligne->vat_src_code = $remise->vat_src_code;
1045			$facligne->tva_tx = $remise->tva_tx;
1046			$facligne->subprice = -$remise->amount_ht;
1047			$facligne->fk_product = 0; // Id produit predefini
1048			$facligne->product_type = 0;
1049			$facligne->qty = 1;
1050			$facligne->remise_percent = 0;
1051			$facligne->rang = -1;
1052			$facligne->info_bits = 2;
1053
1054			// Get buy/cost price of invoice that is source of discount
1055			if ($remise->fk_invoice_supplier_source > 0)
1056			{
1057				$srcinvoice = new FactureFournisseur($this->db);
1058				$srcinvoice->fetch($remise->fk_invoice_supplier_source);
1059				$totalcostpriceofinvoice = 0;
1060				include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php'; // TODO Move this into commonobject
1061				$formmargin = new FormMargin($this->db);
1062				$arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false);
1063				$facligne->pa_ht = $arraytmp['pa_total'];
1064			}
1065
1066			$facligne->total_ht  = -$remise->amount_ht;
1067			$facligne->total_tva = -$remise->amount_tva;
1068			$facligne->total_ttc = -$remise->amount_ttc;
1069
1070			$facligne->multicurrency_subprice = -$remise->multicurrency_subprice;
1071			$facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht;
1072			$facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva;
1073			$facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc;
1074
1075			$lineid = $facligne->insert();
1076			if ($lineid > 0)
1077			{
1078				$result = $this->update_price(1);
1079				if ($result > 0)
1080				{
1081					// Create link between discount and invoice line
1082					$result = $remise->link_to_invoice($lineid, 0, 'supplier');
1083					if ($result < 0)
1084					{
1085						$this->error = $remise->error;
1086						$this->db->rollback();
1087						return -4;
1088					}
1089
1090					$this->db->commit();
1091					return 1;
1092				} else {
1093					$this->error = $facligne->error;
1094					$this->db->rollback();
1095					return -1;
1096				}
1097			} else {
1098				$this->error = $facligne->error;
1099				$this->db->rollback();
1100				return -2;
1101			}
1102		} else {
1103			$this->db->rollback();
1104			return -3;
1105		}
1106	}
1107
1108
1109	/**
1110	 *	Delete invoice from database
1111	 *
1112	 *  @param      User	$user		    User object
1113	 *	@param	    int		$notrigger	    1=Does not execute triggers, 0= execute triggers
1114	 *	@return		int						<0 if KO, >0 if OK
1115	 */
1116	public function delete(User $user, $notrigger = 0)
1117	{
1118		global $langs, $conf;
1119
1120		$rowid = $this->id;
1121
1122		dol_syslog("FactureFournisseur::delete rowid=".$rowid, LOG_DEBUG);
1123
1124		// TODO Test if there is at least on payment. If yes, refuse to delete.
1125
1126		$error = 0;
1127		$this->db->begin();
1128
1129		if (!$error && !$notrigger)
1130		{
1131			// Call trigger
1132			$result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user);
1133			if ($result < 0)
1134			{
1135				$this->db->rollback();
1136				return -1;
1137			}
1138			// Fin appel triggers
1139		}
1140
1141		if (!$error) {
1142			// If invoice was converted into a discount not yet consumed, we remove discount
1143			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.'societe_remise_except';
1144			$sql .= ' WHERE fk_invoice_supplier_source = '.$rowid;
1145			$sql .= ' AND fk_invoice_supplier_line IS NULL';
1146			$resql = $this->db->query($sql);
1147
1148			// If invoice has consumned discounts
1149			$this->fetch_lines();
1150			$list_rowid_det = array();
1151			foreach ($this->lines as $key => $invoiceline) {
1152				$list_rowid_det[] = $invoiceline->rowid;
1153			}
1154
1155			// Consumned discounts are freed
1156			if (count($list_rowid_det)) {
1157				$sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
1158				$sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL';
1159				$sql .= ' WHERE fk_invoice_supplier_line IN ('.join(',', $list_rowid_det).')';
1160
1161				dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1162				if (!$this->db->query($sql)) {
1163					$error++;
1164				}
1165			}
1166		}
1167
1168		if (!$error)
1169		{
1170			$main = MAIN_DB_PREFIX.'facture_fourn_det';
1171			$ef = $main."_extrafields";
1172			$sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_facture_fourn = $rowid)";
1173			$resqlef = $this->db->query($sqlef);
1174			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det WHERE fk_facture_fourn = '.$rowid.';';
1175			dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1176			$resql = $this->db->query($sql);
1177			if ($resqlef && $resql)
1178			{
1179				$sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn WHERE rowid = '.$rowid;
1180				dol_syslog(get_class($this)."::delete", LOG_DEBUG);
1181				$resql2 = $this->db->query($sql);
1182				if (!$resql2) {
1183					$error++;
1184				}
1185			} else {
1186				$error++;
1187			}
1188		}
1189
1190		if (!$error)
1191		{
1192			// Delete linked object
1193			$res = $this->deleteObjectLinked();
1194			if ($res < 0) $error++;
1195		}
1196
1197		if (!$error)
1198		{
1199			// Delete linked object
1200			$res = $this->deleteObjectLinked();
1201			if ($res < 0) $error++;
1202		}
1203
1204		if (!$error)
1205		{
1206			// Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive
1207			$this->deleteEcmFiles();
1208
1209			// We remove directory
1210			if ($conf->fournisseur->facture->dir_output)
1211			{
1212				include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1213
1214				$ref = dol_sanitizeFileName($this->ref);
1215				$dir = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$ref;
1216				$file = $dir."/".$ref.".pdf";
1217				if (file_exists($file))
1218				{
1219					if (!dol_delete_file($file, 0, 0, 0, $this)) // For triggers
1220					{
1221						$this->error = 'ErrorFailToDeleteFile';
1222						$error++;
1223					}
1224				}
1225				if (file_exists($dir))
1226				{
1227					$res = @dol_delete_dir_recursive($dir);
1228
1229					if (!$res)
1230					{
1231						$this->error = 'ErrorFailToDeleteDir';
1232						$error++;
1233					}
1234				}
1235			}
1236		}
1237
1238		// Remove extrafields
1239		if (!$error)
1240		{
1241			$result = $this->deleteExtraFields();
1242			if ($result < 0)
1243			{
1244				$error++;
1245				dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
1246			}
1247		}
1248
1249		if (!$error)
1250		{
1251			dol_syslog(get_class($this)."::delete $this->id by $user->id", LOG_DEBUG);
1252			$this->db->commit();
1253			return 1;
1254		} else {
1255			$this->error = $this->db->lasterror();
1256			$this->db->rollback();
1257			return -$error;
1258		}
1259	}
1260
1261
1262	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1263	/**
1264	 *  Tag invoice as a paid invoice
1265	 *
1266	 *	@param  User	$user       Object user
1267	 *	@param  string	$close_code	Code indicates whether the class has paid in full while payment is incomplete. Not implementd yet.
1268	 *	@param  string	$close_note	Comment informs if the class has been paid while payment is incomplete. Not implementd yet.
1269	 *	@return int         		<0 si ko, >0 si ok
1270	 */
1271	public function set_paid($user, $close_code = '', $close_note = '')
1272	{
1273		// phpcs:enable
1274		global $conf, $langs;
1275		$error = 0;
1276
1277		$this->db->begin();
1278
1279		$sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1280		$sql .= ' SET paye = 1, fk_statut = '.self::STATUS_CLOSED;
1281		$sql .= ' WHERE rowid = '.$this->id;
1282
1283		dol_syslog("FactureFournisseur::set_paid", LOG_DEBUG);
1284		$resql = $this->db->query($sql);
1285		if ($resql)
1286		{
1287			// Call trigger
1288			$result = $this->call_trigger('BILL_SUPPLIER_PAYED', $user);
1289			if ($result < 0) $error++;
1290			// End call triggers
1291		} else {
1292			$error++;
1293			$this->error = $this->db->error();
1294			dol_print_error($this->db);
1295		}
1296
1297		if (!$error)
1298		{
1299			$this->db->commit();
1300			return 1;
1301		} else {
1302			$this->db->rollback();
1303			return -1;
1304		}
1305	}
1306
1307
1308	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1309	/**
1310	 *	Tag the invoice as not fully paid + trigger call BILL_UNPAYED
1311	 *	Function used when a direct debit payment is refused,
1312	 *	or when the invoice was canceled and reopened.
1313	 *
1314	 *	@param      User	$user       Object user that change status
1315	 *	@return     int         		<0 si ok, >0 si ok
1316	 */
1317	public function set_unpaid($user)
1318	{
1319		// phpcs:enable
1320		global $conf, $langs;
1321		$error = 0;
1322
1323		$this->db->begin();
1324
1325		$sql = 'UPDATE '.MAIN_DB_PREFIX.'facture_fourn';
1326		$sql .= ' SET paye=0, fk_statut=1, close_code=null, close_note=null';
1327		$sql .= ' WHERE rowid = '.$this->id;
1328
1329		dol_syslog("FactureFournisseur::set_unpaid", LOG_DEBUG);
1330		$resql = $this->db->query($sql);
1331		if ($resql)
1332		{
1333			// Call trigger
1334			$result = $this->call_trigger('BILL_SUPPLIER_UNPAYED', $user);
1335			if ($result < 0) $error++;
1336			// End call triggers
1337		} else {
1338			$error++;
1339			$this->error = $this->db->lasterror();
1340			dol_syslog("FactureFournisseur::set_unpaid ".$this->error);
1341		}
1342
1343		if (!$error)
1344		{
1345			$this->db->commit();
1346			return 1;
1347		} else {
1348			$this->db->rollback();
1349			return -1;
1350		}
1351	}
1352
1353	/**
1354	 *	Tag invoice as validated + call trigger BILL_VALIDATE
1355	 *
1356	 *	@param	User	$user           Object user that validate
1357	 *	@param  string	$force_number   Reference to force on invoice
1358	 *	@param	int		$idwarehouse	Id of warehouse for stock change
1359	 *  @param	int		$notrigger		1=Does not execute triggers, 0= execute triggers
1360	 *	@return int 			        <0 if KO, =0 if nothing to do, >0 if OK
1361	 */
1362	public function validate($user, $force_number = '', $idwarehouse = 0, $notrigger = 0)
1363	{
1364		global $conf, $langs;
1365
1366		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1367
1368		$now = dol_now();
1369
1370		$error = 0;
1371		dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number.', idwarehouse='.$idwarehouse);
1372
1373		// Force to have object complete for checks
1374		$this->fetch_thirdparty();
1375		$this->fetch_lines();
1376
1377		// Check parameters
1378		if ($this->statut > self::STATUS_DRAFT)	// This is to avoid to validate twice (avoid errors on logs and stock management)
1379		{
1380			dol_syslog(get_class($this)."::validate no draft status", LOG_WARNING);
1381			return 0;
1382		}
1383		if (preg_match('/^'.preg_quote($langs->trans("CopyOf").' ').'/', $this->ref_supplier))
1384		{
1385			$langs->load("errors");
1386			$this->error = $langs->trans("ErrorFieldFormat", $langs->transnoentities("RefSupplier")).'. '.$langs->trans('RemoveString', $langs->transnoentitiesnoconv("CopyOf"));
1387			return -1;
1388		}
1389		if (count($this->lines) <= 0)
1390		{
1391			$langs->load("errors");
1392			$this->error = $langs->trans("ErrorObjectMustHaveLinesToBeValidated", $this->ref);
1393			return -1;
1394		}
1395
1396		$this->db->begin();
1397
1398		// Define new ref
1399		if ($force_number)
1400		{
1401			$num = $force_number;
1402		} elseif (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref)) // empty should not happened, but when it occurs, the test save life
1403		{
1404			$num = $this->getNextNumRef($this->thirdparty);
1405		} else {
1406			$num = $this->ref;
1407		}
1408		$this->newref = dol_sanitizeFileName($num);
1409
1410		$sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1411		$sql .= " SET ref='".$num."', fk_statut = 1, fk_user_valid = ".$user->id.", date_valid = '".$this->db->idate($now)."'";
1412		$sql .= " WHERE rowid = ".$this->id;
1413
1414		dol_syslog(get_class($this)."::validate", LOG_DEBUG);
1415		$resql = $this->db->query($sql);
1416		if ($resql)
1417		{
1418			// Si on incrémente le produit principal et ses composants à la validation de facture fournisseur
1419			if (!$error && !empty($conf->stock->enabled) && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL))
1420			{
1421				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1422				$langs->load("agenda");
1423
1424				$cpt = count($this->lines);
1425				for ($i = 0; $i < $cpt; $i++)
1426				{
1427					if ($this->lines[$i]->fk_product > 0)
1428					{
1429						$this->line = $this->lines[$i];
1430						$mouvP = new MouvementStock($this->db);
1431						$mouvP->origin = &$this;
1432						// We increase stock for product
1433						$up_ht_disc = $this->lines[$i]->pu_ht;
1434						if (!empty($this->lines[$i]->remise_percent) && empty($conf->global->STOCK_EXCLUDE_DISCOUNT_FOR_PMP)) $up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU');
1435						if ($this->type == FactureFournisseur::TYPE_CREDIT_NOTE) $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1436						else $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num));
1437						if ($result < 0) { $error++; }
1438						unset($this->line);
1439					}
1440				}
1441			}
1442
1443			// Triggers call
1444			if (!$error && empty($notrigger))
1445			{
1446				// Call trigger
1447				$result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user);
1448				if ($result < 0) $error++;
1449				// End call triggers
1450			}
1451
1452			if (!$error)
1453			{
1454				$this->oldref = $this->ref;
1455
1456				// Rename directory if dir was a temporary ref
1457				if (preg_match('/^[\(]?PROV/i', $this->ref))
1458				{
1459					// Now we rename also files into index
1460					$sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->newref)."'";
1461					$sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'fournisseur/facture/".get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1462					$resql = $this->db->query($sql);
1463					if (!$resql) { $error++; $this->error = $this->db->lasterror(); }
1464
1465					// We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1466					$oldref = dol_sanitizeFileName($this->ref);
1467					$newref = dol_sanitizeFileName($num);
1468					$dirsource = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$oldref;
1469					$dirdest = $conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$newref;
1470					if (!$error && file_exists($dirsource))
1471					{
1472						dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
1473
1474						if (@rename($dirsource, $dirdest))
1475						{
1476							dol_syslog("Rename ok");
1477							// Rename docs starting with $oldref with $newref
1478							$listoffiles = dol_dir_list($conf->fournisseur->facture->dir_output.'/'.get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier').$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1479							foreach ($listoffiles as $fileentry)
1480							{
1481								$dirsource = $fileentry['name'];
1482								$dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1483								$dirsource = $fileentry['path'].'/'.$dirsource;
1484								$dirdest = $fileentry['path'].'/'.$dirdest;
1485								@rename($dirsource, $dirdest);
1486							}
1487						}
1488					}
1489				}
1490			}
1491
1492			// Set new ref and define current statut
1493			if (!$error)
1494			{
1495				$this->ref = $num;
1496				$this->statut = self::STATUS_VALIDATED;
1497				//$this->date_validation=$now; this is stored into log table
1498			}
1499
1500			if (!$error)
1501			{
1502				$this->db->commit();
1503				return 1;
1504			} else {
1505				$this->db->rollback();
1506				return -1;
1507			}
1508		} else {
1509			$this->error = $this->db->error();
1510			$this->db->rollback();
1511			return -1;
1512		}
1513	}
1514
1515	/**
1516	 *	Set draft status
1517	 *
1518	 *	@param	User	$user			Object user that modify
1519	 *	@param	int		$idwarehouse	Id warehouse to use for stock change.
1520	 *	@return	int						<0 if KO, >0 if OK
1521	 */
1522	public function setDraft($user, $idwarehouse = -1)
1523	{
1524		// phpcs:enable
1525		global $conf, $langs;
1526
1527		$error = 0;
1528
1529		if ($this->statut == self::STATUS_DRAFT)
1530		{
1531			dol_syslog(__METHOD__." already draft status", LOG_WARNING);
1532			return 0;
1533		}
1534
1535		dol_syslog(__METHOD__, LOG_DEBUG);
1536
1537		$this->db->begin();
1538
1539		$sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn";
1540		$sql .= " SET fk_statut = ".self::STATUS_DRAFT;
1541		$sql .= " WHERE rowid = ".$this->id;
1542
1543		$result = $this->db->query($sql);
1544		if ($result)
1545		{
1546			if (!$error)
1547			{
1548				$this->oldcopy = clone $this;
1549			}
1550
1551			// Si on incremente le produit principal et ses composants a la validation de facture fournisseur, on decremente
1552			if ($result >= 0 && !empty($conf->stock->enabled) && !empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_BILL))
1553			{
1554				require_once DOL_DOCUMENT_ROOT.'/product/stock/class/mouvementstock.class.php';
1555				$langs->load("agenda");
1556
1557				$cpt = count($this->lines);
1558				for ($i = 0; $i < $cpt; $i++)
1559				{
1560					if ($this->lines[$i]->fk_product > 0)
1561					{
1562						$mouvP = new MouvementStock($this->db);
1563						$mouvP->origin = &$this;
1564						// We increase stock for product
1565						if ($this->type == FactureFournisseur::TYPE_CREDIT_NOTE) $result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
1566						else $result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref));
1567					}
1568				}
1569			}
1570			// Triggers call
1571			if (!$error && empty($notrigger))
1572			{
1573				// Call trigger
1574				$result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user);
1575				if ($result < 0) $error++;
1576				// End call triggers
1577			}
1578			if ($error == 0)
1579			{
1580				$this->db->commit();
1581				return 1;
1582			} else {
1583				$this->db->rollback();
1584				return -1;
1585			}
1586		} else {
1587			$this->error = $this->db->error();
1588			$this->db->rollback();
1589			return -1;
1590		}
1591	}
1592
1593
1594	/**
1595	 *	Adds an invoice line (associated with no predefined product/service)
1596	 *	The parameters are already supposed to be correct and with final values when calling
1597	 *	this method. Also, for the VAT rate, it must already have been defined by the caller by
1598	 *	by the get_default_tva method(vendor_company, buying company, idprod) and the desc must
1599	 *	already have the right value (the caller has to manage the multilanguage).
1600	 *
1601	 *	@param    	string	$desc            	Description of the line
1602	 *	@param    	double	$pu              	Unit price (HT or TTC according to price_base_type, > 0 even for credit note)
1603	 *	@param    	double	$txtva           	Force Vat rate to use, -1 for auto.
1604	 *	@param		double	$txlocaltax1		LocalTax1 Rate
1605	 *	@param		double	$txlocaltax2		LocalTax2 Rate
1606	 *	@param    	double	$qty             	Quantity
1607	 *	@param    	int		$fk_product      	Product/Service ID predefined
1608	 *	@param    	double	$remise_percent  	Percentage discount of the line
1609	 *	@param    	integer	$date_start      	Service start date
1610	 * 	@param    	integer	$date_end        	Service expiry date
1611	 * 	@param    	string	$ventil          	Accounting breakdown code
1612	 *	@param    	int		$info_bits			Line type bits
1613	 *	@param    	string	$price_base_type 	HT or TTC
1614	 *	@param		int		$type				Type of line (0=product, 1=service)
1615	 *  @param      int		$rang            	Position of line
1616	 *  @param		int		$notrigger			Disable triggers
1617	 *  @param		array	$array_options		extrafields array
1618	 * 	@param 		string	$fk_unit 			Code of the unit to use. Null to use the default one
1619	 *  @param      int     $origin_id          id origin document
1620	 *  @param		double	$pu_ht_devise		Amount in currency
1621	 *  @param		string	$ref_supplier		Supplier ref
1622	 *  @param      string  $special_code       Special code
1623	 *  @param		int		$fk_parent_line		Parent line id
1624	 *	@return    	int             			>0 if OK, <0 if KO
1625	 */
1626	public function addline($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty, $fk_product = 0, $remise_percent = 0, $date_start = '', $date_end = '', $ventil = 0, $info_bits = '', $price_base_type = 'HT', $type = 0, $rang = -1, $notrigger = false, $array_options = 0, $fk_unit = null, $origin_id = 0, $pu_ht_devise = 0, $ref_supplier = '', $special_code = '', $fk_parent_line = 0)
1627	{
1628		global $langs, $mysoc, $conf;
1629
1630		dol_syslog(get_class($this)."::addline $desc,$pu,$qty,$txtva,$fk_product,$remise_percent,$date_start,$date_end,$ventil,$info_bits,$price_base_type,$type,$fk_unit", LOG_DEBUG);
1631		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1632
1633		if ($this->statut == self::STATUS_DRAFT)
1634		{
1635			// Clean parameters
1636			if (empty($remise_percent)) $remise_percent = 0;
1637			if (empty($qty)) $qty = 0;
1638			if (empty($info_bits)) $info_bits = 0;
1639			if (empty($rang)) $rang = 0;
1640			if (empty($ventil)) $ventil = 0;
1641			if (empty($txtva)) $txtva = 0;
1642			if (empty($txlocaltax1)) $txlocaltax1 = 0;
1643			if (empty($txlocaltax2)) $txlocaltax2 = 0;
1644
1645			$remise_percent = price2num($remise_percent);
1646			$qty = price2num($qty);
1647			$pu = price2num($pu);
1648			if (!preg_match('/\((.*)\)/', $txtva)) {
1649				$txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1'
1650			}
1651			$txlocaltax1 = price2num($txlocaltax1);
1652			$txlocaltax2 = price2num($txlocaltax2);
1653
1654			if ($date_start && $date_end && $date_start > $date_end) {
1655				$langs->load("errors");
1656				$this->error = $langs->trans('ErrorStartDateGreaterEnd');
1657				return -1;
1658			}
1659
1660			$this->db->begin();
1661
1662			if ($fk_product > 0)
1663			{
1664				if (!empty($conf->global->SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY))
1665				{
1666					// Check quantity is enough
1667					dol_syslog(get_class($this)."::addline we check supplier prices fk_product=".$fk_product." qty=".$qty." ref_supplier=".$ref_supplier);
1668					$prod = new Product($this->db, $fk_product);
1669					if ($prod->fetch($fk_product) > 0)
1670					{
1671						$product_type = $prod->type;
1672						$label = $prod->label;
1673						$fk_prod_fourn_price = 0;
1674
1675						// We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok.
1676						// If we want a dedicated supplier price, we must provide $fk_prod_fourn_price.
1677						$result = $prod->get_buyprice($fk_prod_fourn_price, $qty, $fk_product, 'none', ($this->fk_soc ? $this->fk_soc : $this->socid)); // Search on couple $fk_prod_fourn_price/$qty first, then on triplet $qty/$fk_product/$ref_supplier/$this->fk_soc
1678						if ($result > 0)
1679						{
1680							if (empty($pu)) $pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice
1681							$ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice
1682							// is remise percent not keyed but present for the product we add it
1683							if ($remise_percent == 0 && $prod->remise_percent != 0)
1684								$remise_percent = $prod->remise_percent;
1685						}
1686						if ($result == 0)                   // If result == 0, we failed to found the supplier reference price
1687						{
1688							$langs->load("errors");
1689							$this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1690							$this->db->rollback();
1691							dol_syslog(get_class($this)."::addline we did not found supplier price, so we can't guess unit price");
1692							//$pu    = $prod->fourn_pu;     // We do not overwrite unit price
1693							//$ref   = $prod->ref_fourn;    // We do not overwrite ref supplier price
1694							return -1;
1695						}
1696						if ($result == -1)
1697						{
1698							$langs->load("errors");
1699							$this->error = "Ref ".$prod->ref." ".$langs->trans("ErrorQtyTooLowForThisSupplier");
1700							$this->db->rollback();
1701							dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_DEBUG);
1702							return -1;
1703						}
1704						if ($result < -1)
1705						{
1706							$this->error = $prod->error;
1707							$this->db->rollback();
1708							dol_syslog(get_class($this)."::addline result=".$result." - ".$this->error, LOG_ERR);
1709							return -1;
1710						}
1711					} else {
1712						$this->error = $prod->error;
1713						$this->db->rollback();
1714						return -1;
1715					}
1716				}
1717			} else {
1718				$product_type = $type;
1719			}
1720
1721			if (!empty($conf->multicurrency->enabled) && $pu_ht_devise > 0) {
1722				$pu = 0;
1723			}
1724
1725			$localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty);
1726
1727			// Clean vat code
1728			$reg = array();
1729			$vat_src_code = '';
1730			if (preg_match('/\((.*)\)/', $txtva, $reg))
1731			{
1732				$vat_src_code = $reg[1];
1733				$txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
1734			}
1735
1736			// Calcul du total TTC et de la TVA pour la ligne a partir de
1737			// qty, pu, remise_percent et txtva
1738			// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1739			// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1740
1741			$tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
1742			$total_ht  = $tabprice[0];
1743			$total_tva = $tabprice[1];
1744			$total_ttc = $tabprice[2];
1745			$total_localtax1 = $tabprice[9];
1746			$total_localtax2 = $tabprice[10];
1747			$pu_ht = $tabprice[3];
1748
1749			// MultiCurrency
1750			$multicurrency_total_ht  = $tabprice[16];
1751			$multicurrency_total_tva = $tabprice[17];
1752			$multicurrency_total_ttc = $tabprice[18];
1753			$pu_ht_devise = $tabprice[19];
1754
1755			// Check parameters
1756			if ($type < 0) return -1;
1757
1758			if ($rang < 0)
1759			{
1760				$rangmax = $this->line_max();
1761				$rang = $rangmax + 1;
1762			}
1763
1764			// Insert line
1765			$this->line = new SupplierInvoiceLine($this->db);
1766
1767			$this->line->context = $this->context;
1768
1769			$this->line->fk_facture_fourn = $this->id;
1770			//$this->line->label=$label;	// deprecated
1771			$this->line->desc = $desc;
1772			$this->line->ref_supplier = $ref_supplier;
1773
1774			$this->line->qty = ($this->type == self::TYPE_CREDIT_NOTE ?abs($qty) : $qty); // For credit note, quantity is always positive and unit price negative
1775			$this->line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ?-abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise
1776
1777			$this->line->vat_src_code = $vat_src_code;
1778			$this->line->tva_tx = $txtva;
1779			$this->line->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0);
1780			$this->line->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0);
1781			$this->line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1782			$this->line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1783
1784			$this->line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_ht) : $total_ht); // For credit note and if qty is negative, total is negative
1785			$this->line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_tva) : $total_tva);
1786			$this->line->total_localtax1 = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_localtax1) : $total_localtax1);
1787			$this->line->total_localtax2 = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_localtax2) : $total_localtax2);
1788			$this->line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_ttc) : $total_ttc);
1789
1790			$this->line->fk_product = $fk_product;
1791			$this->line->product_type = $type;
1792			$this->line->remise_percent = $remise_percent;
1793			$this->line->date_start = $date_start;
1794			$this->line->date_end = $date_end;
1795			$this->line->ventil = $ventil;
1796			$this->line->rang = $rang;
1797			$this->line->info_bits = $info_bits;
1798
1799			$this->line->special_code = ((string) $special_code != '' ? $special_code : $this->special_code);
1800			$this->line->fk_parent_line = $fk_parent_line;
1801			$this->line->origin = $this->origin;
1802			$this->line->origin_id = $origin_id;
1803			$this->line->fk_unit = $fk_unit;
1804
1805			// Multicurrency
1806			$this->line->fk_multicurrency = $this->fk_multicurrency;
1807			$this->line->multicurrency_code = $this->multicurrency_code;
1808			$this->line->multicurrency_subprice		= $pu_ht_devise;
1809			$this->line->multicurrency_total_ht 	= $multicurrency_total_ht;
1810			$this->line->multicurrency_total_tva 	= $multicurrency_total_tva;
1811			$this->line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
1812
1813			if (is_array($array_options) && count($array_options) > 0) {
1814				$this->line->array_options = $array_options;
1815			}
1816
1817			$result = $this->line->insert($notrigger);
1818			if ($result > 0)
1819			{
1820				// Reorder if child line
1821				if (!empty($fk_parent_line)) $this->line_order(true, 'DESC');
1822
1823				// Mise a jour informations denormalisees au niveau de la facture meme
1824				$result = $this->update_price(1, 'auto', 0, $this->thirdparty); // The addline method is designed to add line from user input so total calculation with update_price must be done using 'auto' mode.
1825				if ($result > 0)
1826				{
1827					$this->db->commit();
1828					return $this->line->id;
1829				} else {
1830					$this->error = $this->db->error();
1831					$this->db->rollback();
1832					return -1;
1833				}
1834			} else {
1835				$this->error = $this->line->error;
1836				$this->errors = $this->line->errors;
1837				$this->db->rollback();
1838				return -2;
1839			}
1840		} else {
1841			return 0;
1842		}
1843	}
1844
1845	/**
1846	 * Update a line detail into database
1847	 *
1848	 * @param     	int			$id            		Id of line invoice
1849	 * @param     	string		$desc         		Description of line
1850	 * @param     	double		$pu          		Prix unitaire (HT ou TTC selon price_base_type)
1851	 * @param     	double		$vatrate       		VAT Rate (Can be '8.5', '8.5 (ABC)')
1852	 * @param		double		$txlocaltax1		LocalTax1 Rate
1853	 * @param		double		$txlocaltax2		LocalTax2 Rate
1854	 * @param     	double		$qty           		Quantity
1855	 * @param     	int			$idproduct			Id produit
1856	 * @param	  	double		$price_base_type	HT or TTC
1857	 * @param	  	int			$info_bits			Miscellaneous informations of line
1858	 * @param		int			$type				Type of line (0=product, 1=service)
1859	 * @param     	double		$remise_percent  	Percentage discount of the line
1860	 * @param		int			$notrigger			Disable triggers
1861	 * @param      	integer 	$date_start     	Date start of service
1862	 * @param      	integer     $date_end       	Date end of service
1863	 * @param		array		$array_options		extrafields array
1864	 * @param 		string		$fk_unit 			Code of the unit to use. Null to use the default one
1865	 * @param		double		$pu_ht_devise		Amount in currency
1866	 * @param		string		$ref_supplier		Supplier ref
1867	 * @return    	int           					<0 if KO, >0 if OK
1868	 */
1869	public function updateline($id, $desc, $pu, $vatrate, $txlocaltax1 = 0, $txlocaltax2 = 0, $qty = 1, $idproduct = 0, $price_base_type = 'HT', $info_bits = 0, $type = 0, $remise_percent = 0, $notrigger = false, $date_start = '', $date_end = '', $array_options = 0, $fk_unit = null, $pu_ht_devise = 0, $ref_supplier = '')
1870	{
1871		global $mysoc, $langs;
1872
1873		dol_syslog(get_class($this)."::updateline $id,$desc,$pu,$vatrate,$qty,$idproduct,$price_base_type,$info_bits,$type,$remise_percent,$notrigger,$date_start,$date_end,$fk_unit,$pu_ht_devise,$ref_supplier", LOG_DEBUG);
1874		include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
1875
1876		$pu = price2num($pu);
1877		$qty = price2num($qty);
1878		$remise_percent = price2num($remise_percent);
1879		$pu_ht_devise = price2num($pu_ht_devise);
1880
1881		// Check parameters
1882		//if (! is_numeric($pu) || ! is_numeric($qty)) return -1;
1883		if ($type < 0) return -1;
1884
1885		if ($date_start && $date_end && $date_start > $date_end) {
1886			$langs->load("errors");
1887			$this->error = $langs->trans('ErrorStartDateGreaterEnd');
1888			return -1;
1889		}
1890
1891		// Clean parameters
1892		if (empty($vatrate)) $vatrate = 0;
1893		if (empty($txlocaltax1)) $txlocaltax1 = 0;
1894		if (empty($txlocaltax2)) $txlocaltax2 = 0;
1895
1896		$txlocaltax1 = price2num($txlocaltax1);
1897		$txlocaltax2 = price2num($txlocaltax2);
1898
1899		// Calcul du total TTC et de la TVA pour la ligne a partir de
1900		// qty, pu, remise_percent et txtva
1901		// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1902		// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1903
1904		$localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
1905
1906		$reg = array();
1907
1908		// Clean vat code
1909		$vat_src_code = '';
1910		if (preg_match('/\((.*)\)/', $vatrate, $reg))
1911		{
1912			$vat_src_code = $reg[1];
1913			$vatrate = preg_replace('/\s*\(.*\)/', '', $vatrate); // Remove code into vatrate.
1914		}
1915
1916		$tabprice = calcul_price_total($qty, $pu, $remise_percent, $vatrate, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, $type, $this->thirdparty, $localtaxes_type, 100, $this->multicurrency_tx, $pu_ht_devise);
1917		$total_ht  = $tabprice[0];
1918		$total_tva = $tabprice[1];
1919		$total_ttc = $tabprice[2];
1920		$pu_ht  = $tabprice[3];
1921		$pu_tva = $tabprice[4];
1922		$pu_ttc = $tabprice[5];
1923		$total_localtax1 = $tabprice[9];
1924		$total_localtax2 = $tabprice[10];
1925
1926		// MultiCurrency
1927		$multicurrency_total_ht = $tabprice[16];
1928		$multicurrency_total_tva = $tabprice[17];
1929		$multicurrency_total_ttc = $tabprice[18];
1930		$pu_ht_devise = $tabprice[19];
1931
1932		if (empty($info_bits)) $info_bits = 0;
1933
1934		if ($idproduct)
1935		{
1936			$product = new Product($this->db);
1937			$result = $product->fetch($idproduct);
1938			$product_type = $product->type;
1939		} else {
1940			$product_type = $type;
1941		}
1942
1943		//Fetch current line from the database and then clone the object and set it in $oldline property
1944		$line = new SupplierInvoiceLine($this->db);
1945		$line->fetch($id);
1946		$line->fetch_optionals();
1947
1948		$staticline = clone $line;
1949
1950		$line->oldline = $staticline;
1951		$line->context = $this->context;
1952
1953		$line->description = $desc;
1954		$line->subprice = $pu_ht;
1955		$line->pu_ht = $pu_ht;
1956		$line->pu_ttc = $pu_ttc;
1957		$line->qty = $qty;
1958		$line->remise_percent = $remise_percent;
1959		$line->ref_supplier = $ref_supplier;
1960
1961		$line->date_start = $date_start;
1962		$line->date_end = $date_end;
1963
1964		$line->vat_src_code = $vat_src_code;
1965		$line->tva_tx = $vatrate;
1966		$line->localtax1_tx = $txlocaltax1;
1967		$line->localtax2_tx = $txlocaltax2;
1968		$line->localtax1_type = empty($localtaxes_type[0]) ? '' : $localtaxes_type[0];
1969		$line->localtax2_type = empty($localtaxes_type[2]) ? '' : $localtaxes_type[2];
1970		$line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_ht) : $total_ht);
1971		$line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_tva) : $total_tva);
1972		$line->total_localtax1 = $total_localtax1;
1973		$line->total_localtax2 = $total_localtax2;
1974		$line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ?-abs($total_ttc) : $total_ttc);
1975		$line->fk_product = $idproduct;
1976		$line->product_type = $product_type;
1977		$line->info_bits = $info_bits;
1978		$line->fk_unit = $fk_unit;
1979
1980		if (is_array($array_options) && count($array_options) > 0) {
1981			// We replace values in this->line->array_options only for entries defined into $array_options
1982			foreach ($array_options as $key => $value) {
1983				$line->array_options[$key] = $array_options[$key];
1984			}
1985		}
1986
1987		// Multicurrency
1988		$line->multicurrency_subprice = $pu_ht_devise;
1989		$line->multicurrency_total_ht = $multicurrency_total_ht;
1990		$line->multicurrency_total_tva 	= $multicurrency_total_tva;
1991		$line->multicurrency_total_ttc 	= $multicurrency_total_ttc;
1992
1993		$res = $line->update($notrigger);
1994
1995		if ($res < 1) {
1996			$this->errors[] = $line->error;
1997		} else {
1998			// Update total price into invoice record
1999			$res = $this->update_price('', 'auto', 0, $this->thirdparty);
2000		}
2001
2002		return $res;
2003	}
2004
2005	/**
2006	 * 	Delete a detail line from database
2007	 *
2008	 * 	@param  int		$rowid      	Id of line to delete
2009	 *	@param	int		$notrigger		1=Does not execute triggers, 0= execute triggers
2010	 * 	@return	int						<0 if KO, >0 if OK
2011	 */
2012	public function deleteline($rowid, $notrigger = 0)
2013	{
2014		if (!$rowid) {
2015			$rowid = $this->id;
2016		}
2017
2018		$this->db->begin();
2019
2020		// Libere remise liee a ligne de facture
2021		$sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
2022		$sql .= ' SET fk_invoice_supplier_line = NULL';
2023		$sql .= ' WHERE fk_invoice_supplier_line = '.$rowid;
2024
2025		dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
2026		$result = $this->db->query($sql);
2027		if (!$result)
2028		{
2029			$this->error = $this->db->error();
2030			$this->db->rollback();
2031			return -2;
2032		}
2033
2034		$line = new SupplierInvoiceLine($this->db);
2035
2036		if ($line->fetch($rowid) < 1) {
2037			return -1;
2038		}
2039
2040		$res = $line->delete($notrigger);
2041
2042		if ($res < 1) {
2043			$this->errors[] = $line->error;
2044			$this->db->rollback();
2045			return -3;
2046		} else {
2047			$res = $this->update_price();
2048
2049			if ($res > 0)
2050			{
2051				$this->db->commit();
2052				return 1;
2053			} else {
2054				$this->db->rollback();
2055				$this->error = $this->db->lasterror();
2056				return -4;
2057			}
2058		}
2059	}
2060
2061
2062	/**
2063	 *	Loads the info order information into the invoice object
2064	 *
2065	 *	@param  int		$id       	Id of the invoice to load
2066	 *	@return	void
2067	 */
2068	public function info($id)
2069	{
2070		$sql = 'SELECT c.rowid, datec, tms as datem, ';
2071		$sql .= ' fk_user_author, fk_user_modif, fk_user_valid';
2072		$sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as c';
2073		$sql .= ' WHERE c.rowid = '.$id;
2074
2075		$result = $this->db->query($sql);
2076		if ($result)
2077		{
2078			if ($this->db->num_rows($result))
2079			{
2080				$obj = $this->db->fetch_object($result);
2081				$this->id = $obj->rowid;
2082				if ($obj->fk_user_author)
2083				{
2084					$cuser = new User($this->db);
2085					$cuser->fetch($obj->fk_user_author);
2086					$this->user_creation = $cuser;
2087				}
2088				if ($obj->fk_user_valid)
2089				{
2090					$vuser = new User($this->db);
2091					$vuser->fetch($obj->fk_user_valid);
2092					$this->user_validation = $vuser;
2093				}
2094				if ($obj->fk_user_modif)
2095				{
2096					$muser = new User($this->db);
2097					$muser->fetch($obj->fk_user_modif);
2098					$this->user_modification = $muser;
2099				}
2100				$this->date_creation     = $this->db->idate($obj->datec);
2101				$this->date_modification = $this->db->idate($obj->datem);
2102				//$this->date_validation   = $obj->datev; // This field is not available. Should be store into log table and using this function should be replaced with showing content of log (like for supplier orders)
2103			}
2104			$this->db->free($result);
2105		} else {
2106			dol_print_error($this->db);
2107		}
2108	}
2109
2110	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2111	/**
2112	 *	Return list of replaceable invoices
2113	 *	Status valid or abandoned for other reason + not paid + no payment + not already replaced
2114	 *
2115	 *	@param      int		$socid		Thirdparty id
2116	 *	@return    	array|int			Table of invoices ('id'=>id, 'ref'=>ref, 'status'=>status, 'paymentornot'=>0/1)
2117	 *                                  <0 if error
2118	 */
2119	public function list_replacable_supplier_invoices($socid = 0)
2120	{
2121		// phpcs:enable
2122		global $conf;
2123
2124		$return = array();
2125
2126		$sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,";
2127		$sql .= " ff.rowid as rowidnext";
2128		$sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2129		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2130		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."facture_fourn as ff ON f.rowid = ff.fk_facture_source";
2131		$sql .= " WHERE (f.fk_statut = ".self::STATUS_VALIDATED." OR (f.fk_statut = ".self::STATUS_ABANDONED." AND f.close_code = '".self::CLOSECODE_ABANDONED."'))";
2132		$sql .= " AND f.entity = ".$conf->entity;
2133		$sql .= " AND f.paye = 0"; // Pas classee payee completement
2134		$sql .= " AND pf.fk_paiementfourn IS NULL"; // Aucun paiement deja fait
2135		$sql .= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de remplacement
2136		if ($socid > 0) $sql .= " AND f.fk_soc = ".$socid;
2137		$sql .= " ORDER BY f.ref";
2138
2139		dol_syslog(get_class($this)."::list_replacable_supplier_invoices", LOG_DEBUG);
2140		$resql = $this->db->query($sql);
2141		if ($resql)
2142		{
2143			while ($obj = $this->db->fetch_object($resql))
2144			{
2145				$return[$obj->rowid] = array(
2146					'id' => $obj->rowid,
2147					'ref' => $obj->ref,
2148					'status' => $obj->fk_statut
2149				);
2150			}
2151			//print_r($return);
2152			return $return;
2153		} else {
2154			$this->error = $this->db->error();
2155			return -1;
2156		}
2157	}
2158
2159	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2160	/**
2161	 *	Return list of qualifying invoices for correction by credit note
2162	 *	Invoices that respect the following rules are returned:
2163	 *	(validated + payment in progress) or classified (paid in full or paid in part) + not already replaced + not already having
2164	 *
2165	 *	@param		int		$socid		Thirdparty id
2166	 *	@return    	array|int			Table of invoices ($id => array('ref'=>,'paymentornot'=>,'status'=>,'paye'=>)
2167	 *                                  <0 if error
2168	 */
2169	public function list_qualified_avoir_supplier_invoices($socid = 0)
2170	{
2171		// phpcs:enable
2172		global $conf;
2173
2174		$return = array();
2175
2176		$sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.paye, pf.fk_paiementfourn";
2177		$sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2178		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn";
2179		$sql .= " WHERE f.entity = ".$conf->entity;
2180		$sql .= " AND f.fk_statut in (".self::STATUS_VALIDATED.",".self::STATUS_CLOSED.")";
2181		$sql .= " AND NOT EXISTS (SELECT rowid from ".MAIN_DB_PREFIX."facture_fourn as ff WHERE f.rowid = ff.fk_facture_source";
2182		$sql .= " AND ff.type=".self::TYPE_REPLACEMENT.")";
2183		$sql .= " AND f.type != ".self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir
2184		if ($socid > 0) $sql .= " AND f.fk_soc = ".$socid;
2185		$sql .= " ORDER BY f.ref";
2186
2187		dol_syslog(get_class($this)."::list_qualified_avoir_supplier_invoices", LOG_DEBUG);
2188		$resql = $this->db->query($sql);
2189		if ($resql)
2190		{
2191			while ($obj = $this->db->fetch_object($resql))
2192			{
2193				$qualified = 0;
2194				if ($obj->fk_statut == self::STATUS_VALIDATED) $qualified = 1;
2195				if ($obj->fk_statut == self::STATUS_CLOSED) $qualified = 1;
2196				if ($qualified)
2197				{
2198					$paymentornot = ($obj->fk_paiementfourn ? 1 : 0);
2199					$return[$obj->rowid] = array('ref'=>$obj->ref, 'status'=>$obj->fk_statut, 'type'=>$obj->type, 'paye'=>$obj->paye, 'paymentornot'=>$paymentornot);
2200				}
2201			}
2202
2203			return $return;
2204		} else {
2205			$this->error = $this->db->error();
2206			return -1;
2207		}
2208	}
2209
2210	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2211	/**
2212	 *	Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2213	 *
2214	 *	@param      User	$user       Object user
2215	 *	@return WorkboardResponse|int <0 if KO, WorkboardResponse if OK
2216	 */
2217	public function load_board($user)
2218	{
2219		// phpcs:enable
2220		global $conf, $langs;
2221
2222		$sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut';
2223		$sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn as ff';
2224		if (!$user->rights->societe->client->voir && !$user->socid) $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2225		$sql .= ' WHERE ff.paye=0';
2226		$sql .= ' AND ff.fk_statut > 0';
2227		$sql .= " AND ff.entity = ".$conf->entity;
2228		if ($user->socid) $sql .= ' AND ff.fk_soc = '.$user->socid;
2229		if (!$user->rights->societe->client->voir && !$user->socid) $sql .= " AND ff.fk_soc = sc.fk_soc AND sc.fk_user = ".$user->id;
2230
2231		$resql = $this->db->query($sql);
2232		if ($resql)
2233		{
2234			$langs->load("bills");
2235			$now = dol_now();
2236
2237			$response = new WorkboardResponse();
2238			$response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24;
2239			$response->label = $langs->trans("SupplierBillsToPay");
2240			$response->labelShort = $langs->trans("StatusToPay");
2241
2242			$response->url = DOL_URL_ROOT.'/fourn/facture/list.php?search_status=1&amp;mainmenu=billing&amp;leftmenu=suppliers_bills';
2243			$response->img = img_object($langs->trans("Bills"), "bill");
2244
2245			$facturestatic = new FactureFournisseur($this->db);
2246
2247			while ($obj = $this->db->fetch_object($resql))
2248			{
2249				$response->nbtodo++;
2250
2251				$facturestatic->date_echeance = $this->db->jdate($obj->datefin);
2252				$facturestatic->statut = $obj->fk_statut;
2253
2254				if ($facturestatic->hasDelay()) {
2255					$response->nbtodolate++;
2256					$response->url_late = DOL_URL_ROOT.'/fourn/facture/list.php?option=late&mainmenu=billing&leftmenu=suppliers_bills';
2257				}
2258			}
2259			$this->db->free($resql);
2260			return $response;
2261		} else {
2262			dol_print_error($this->db);
2263			$this->error = $this->db->error();
2264			return -1;
2265		}
2266	}
2267
2268
2269	/**
2270	 *	Return clicable name (with picto eventually)
2271	 *
2272	 *	@param		int		$withpicto					0=No picto, 1=Include picto into link, 2=Only picto
2273	 *	@param		string	$option						Where point the link
2274	 *	@param		int		$max						Max length of shown ref
2275	 *	@param		int		$short						1=Return just URL
2276	 *	@param		string	$moretitle					Add more text to title tooltip
2277	 *  @param	    int   	$notooltip					1=Disable tooltip
2278	 *  @param      int     $save_lastsearch_value		-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
2279	 *  @param		int		$addlinktonotes				Add link to show notes
2280	 * 	@return		string								String with URL
2281	 */
2282	public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0)
2283	{
2284		global $langs, $conf, $user;
2285
2286		$result = '';
2287
2288		if ($option == 'withdraw') $url = DOL_URL_ROOT.'/compta/facture/prelevement.php?facid='.$this->id.'&type=bank-transfer';
2289		elseif ($option == 'document')	$url = DOL_URL_ROOT.'/fourn/facture/document.php?facid='.$this->id;
2290		else $url = DOL_URL_ROOT.'/fourn/facture/card.php?facid='.$this->id;
2291
2292		if ($short) return $url;
2293
2294		if ($option !== 'nolink')
2295		{
2296			// Add param to save lastsearch_values or not
2297			$add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
2298			if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values = 1;
2299			if ($add_save_lastsearch_values) $url .= '&save_lastsearch_values=1';
2300		}
2301
2302		$picto = $this->picto;
2303		if ($this->type == self::TYPE_REPLACEMENT) $picto .= 'r'; // Replacement invoice
2304		if ($this->type == self::TYPE_CREDIT_NOTE) $picto .= 'a'; // Credit note
2305		if ($this->type == self::TYPE_DEPOSIT)     $picto .= 'd'; // Deposit invoice
2306
2307		$label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("SupplierInvoice").'</u>';
2308		if ($this->type == self::TYPE_REPLACEMENT) $label = '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("InvoiceReplace").'</u>';
2309		elseif ($this->type == self::TYPE_CREDIT_NOTE) $label = '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("CreditNote").'</u>';
2310		elseif ($this->type == self::TYPE_DEPOSIT)     $label = '<u class="paddingrightonly">'.$langs->transnoentitiesnoconv("Deposit").'</u>';
2311		if (isset($this->status)) {
2312			$alreadypaid = -1;
2313			$label .= ' '.$this->getLibStatut(5, $alreadypaid);
2314		}
2315		if (!empty($this->ref))
2316			$label .= '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
2317		if (!empty($this->ref_supplier))
2318			$label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2319		if (!empty($this->label))
2320			$label .= '<br><b>'.$langs->trans('Label').':</b> '.$this->label;
2321		if (!empty($this->date))
2322			$label .= '<br><b>'.$langs->trans('Date').':</b> '.dol_print_date($this->date, 'day');
2323		if (!empty($this->total_ht))
2324			$label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2325		if (!empty($this->total_tva))
2326			$label .= '<br><b>'.$langs->trans('AmountVAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2327		if (!empty($this->total_ttc))
2328			$label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2329		if ($moretitle) $label .= ' - '.$moretitle;
2330		if (isset($this->statut) && isset($this->alreadypaid)) {
2331			$label .= '<br><b>'.$langs->trans("Status").":</b> ".$this->getLibStatut(5, $this->alreadypaid);
2332		}
2333
2334		$ref = $this->ref;
2335		if (empty($ref)) $ref = $this->id;
2336
2337		$linkclose = '';
2338		if (empty($notooltip))
2339		{
2340			if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
2341			{
2342				$label = $langs->trans("ShowSupplierInvoice");
2343				$linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2344			}
2345			$linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
2346			$linkclose .= ' class="classfortooltip"';
2347		}
2348
2349		$linkstart = '<a href="'.$url.'"';
2350		$linkstart .= $linkclose.'>';
2351		$linkend = '</a>';
2352
2353		$result .= $linkstart;
2354		if ($withpicto) $result .= img_object(($notooltip ? '' : $label), $picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
2355		if ($withpicto != 2) $result .= ($max ?dol_trunc($ref, $max) : $ref);
2356		$result .= $linkend;
2357
2358		if ($addlinktonotes)
2359		{
2360			$txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private);
2361			if ($txttoshow)
2362			{
2363				$notetoshow = $langs->trans("ViewPrivateNote").':<br>'.dol_string_nohtmltag($txttoshow, 1);
2364				$result .= ' <span class="note inline-block">';
2365				$result .= '<a href="'.DOL_URL_ROOT.'/fourn/facture/note.php?id='.$this->id.'" class="classfortooltip" title="'.dol_escape_htmltag($notetoshow).'">';
2366				$result .= img_picto('', 'note');
2367				$result .= '</a>';
2368				//$result.=img_picto($langs->trans("ViewNote"),'object_generic');
2369				//$result.='</a>';
2370				$result .= '</span>';
2371			}
2372		}
2373
2374		return $result;
2375	}
2376
2377	 /**
2378	  *      Return next reference of supplier invoice not already used (or last reference)
2379	  *      according to numbering module defined into constant INVOICE_SUPPLIER_ADDON_NUMBER
2380	  *
2381	  *      @param	   Societe		$soc		Thirdparty object
2382	  *      @param    string		$mode		'next' for next value or 'last' for last value
2383	  *      @return   string					free ref or last ref
2384	  */
2385	public function getNextNumRef($soc, $mode = 'next')
2386	{
2387		global $db, $langs, $conf;
2388		$langs->load("orders");
2389
2390		// Clean parameters (if not defined or using deprecated value)
2391		if (empty($conf->global->INVOICE_SUPPLIER_ADDON_NUMBER)) $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus';
2392
2393		$mybool = false;
2394
2395		$file = $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER.".php";
2396		$classname = $conf->global->INVOICE_SUPPLIER_ADDON_NUMBER;
2397
2398		// Include file with class
2399		$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2400
2401		foreach ($dirmodels as $reldir) {
2402			$dir = dol_buildpath($reldir."core/modules/supplier_invoice/");
2403
2404			// Load file with numbering class (if found)
2405			$mybool |= @include_once $dir.$file;
2406		}
2407
2408		if ($mybool === false) {
2409			dol_print_error('', "Failed to include file ".$file);
2410			return '';
2411		}
2412
2413		$obj = new $classname();
2414		$numref = "";
2415		$numref = $obj->getNumRef($soc, $this, $mode);
2416
2417		if ($numref != "")
2418		{
2419			return $numref;
2420		} else {
2421			$this->error = $obj->error;
2422			return -1;
2423		}
2424	}
2425
2426
2427	/**
2428	 *  Initialise an instance with random values.
2429	 *  Used to build previews or test instances.
2430	 *	id must be 0 if object instance is a specimen.
2431	 *
2432	 *	@param	string		$option		''=Create a specimen invoice with lines, 'nolines'=No lines
2433	 *  @return	void
2434	 */
2435	public function initAsSpecimen($option = '')
2436	{
2437		global $langs, $conf;
2438		include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
2439
2440		$now = dol_now();
2441
2442		// Load array of products prodids
2443		$num_prods = 0;
2444		$prodids = array();
2445
2446		$sql = "SELECT rowid";
2447		$sql .= " FROM ".MAIN_DB_PREFIX."product";
2448		$sql .= " WHERE entity IN (".getEntity('product').")";
2449		$sql .= $this->db->plimit(100);
2450
2451		$resql = $this->db->query($sql);
2452		if ($resql)
2453		{
2454			$num_prods = $this->db->num_rows($resql);
2455			$i = 0;
2456			while ($i < $num_prods)
2457			{
2458				$i++;
2459				$row = $this->db->fetch_row($resql);
2460				$prodids[$i] = $row[0];
2461			}
2462		}
2463
2464		// Initialise parametres
2465		$this->id = 0;
2466		$this->ref = 'SPECIMEN';
2467		$this->ref_supplier = 'SUPPLIER_REF_SPECIMEN';
2468		$this->specimen = 1;
2469		$this->socid = 1;
2470		$this->date = $now;
2471		$this->date_lim_reglement = $this->date + 3600 * 24 * 30;
2472		$this->cond_reglement_code = 'RECEP';
2473		$this->mode_reglement_code = 'CHQ';
2474
2475		$this->note_public = 'This is a comment (public)';
2476		$this->note_private = 'This is a comment (private)';
2477
2478		$this->multicurrency_tx = 1;
2479		$this->multicurrency_code = $conf->currency;
2480
2481		if (empty($option) || $option != 'nolines')
2482		{
2483			// Lines
2484			$nbp = 5;
2485			$xnbp = 0;
2486			while ($xnbp < $nbp)
2487			{
2488				$line = new SupplierInvoiceLine($this->db);
2489				$line->desc = $langs->trans("Description")." ".$xnbp;
2490				$line->qty = 1;
2491				$line->subprice = 100;
2492				$line->pu_ht = 100; // the canelle template use pu_ht and not subprice
2493				$line->price = 100;
2494				$line->tva_tx = 19.6;
2495				$line->localtax1_tx = 0;
2496				$line->localtax2_tx = 0;
2497				if ($xnbp == 2)
2498				{
2499					$line->total_ht = 50;
2500					$line->total_ttc = 59.8;
2501					$line->total_tva = 9.8;
2502					$line->remise_percent = 50;
2503				} else {
2504					$line->total_ht = 100;
2505					$line->total_ttc = 119.6;
2506					$line->total_tva = 19.6;
2507					$line->remise_percent = 0;
2508				}
2509
2510				if ($num_prods > 0)
2511				{
2512					$prodid = mt_rand(1, $num_prods);
2513					$line->fk_product = $prodids[$prodid];
2514				}
2515				$line->product_type = 0;
2516
2517				$this->lines[$xnbp] = $line;
2518
2519				$this->total_ht       += $line->total_ht;
2520				$this->total_tva      += $line->total_tva;
2521				$this->total_ttc      += $line->total_ttc;
2522
2523				$xnbp++;
2524			}
2525		}
2526
2527		$this->amount_ht      = $xnbp * 100;
2528		$this->total_ht       = $xnbp * 100;
2529		$this->total_tva      = $xnbp * 19.6;
2530		$this->total_ttc      = $xnbp * 119.6;
2531	}
2532
2533	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2534	/**
2535	 *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2536	 *
2537	 *      @return         int     <0 if KO, >0 if OK
2538	 */
2539	public function load_state_board()
2540	{
2541		// phpcs:enable
2542		global $conf, $user;
2543
2544		$this->nb = array();
2545
2546		$clause = "WHERE";
2547
2548		$sql = "SELECT count(f.rowid) as nb";
2549		$sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f";
2550		$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON f.fk_soc = s.rowid";
2551		if (!$user->rights->societe->client->voir && !$user->socid)
2552		{
2553			$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2554			$sql .= " WHERE sc.fk_user = ".$user->id;
2555			$clause = "AND";
2556		}
2557		$sql .= " ".$clause." f.entity = ".$conf->entity;
2558
2559		$resql = $this->db->query($sql);
2560		if ($resql)
2561		{
2562			while ($obj = $this->db->fetch_object($resql))
2563			{
2564				$this->nb["supplier_invoices"] = $obj->nb;
2565			}
2566			$this->db->free($resql);
2567			return 1;
2568		} else {
2569			dol_print_error($this->db);
2570			$this->error = $this->db->error();
2571			return -1;
2572		}
2573	}
2574
2575	/**
2576	 *	Load an object from its id and create a new one in database
2577	 *
2578	 *	@param      User	$user        	User that clone
2579	 *	@param      int		$fromid     	Id of object to clone
2580	 *	@param		int		$invertdetail	Reverse sign of amounts for lines
2581	 * 	@return		int						New id of clone
2582	 */
2583	public function createFromClone(User $user, $fromid, $invertdetail = 0)
2584	{
2585		global $langs;
2586
2587		$error = 0;
2588
2589		$object = new FactureFournisseur($this->db);
2590
2591		$this->db->begin();
2592
2593		// Load source object
2594		$object->fetch($fromid);
2595		$object->id = 0;
2596		$object->statut = self::STATUS_DRAFT;
2597
2598		$object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor
2599
2600		// Clear fields
2601		$object->ref_supplier       = (empty($this->ref_supplier) ? $langs->trans("CopyOf").' '.$object->ref_supplier : $this->ref_supplier);
2602		$object->author             = $user->id;
2603		$object->user_valid         = '';
2604		$object->fk_facture_source  = 0;
2605		$object->date_creation      = '';
2606		$object->date_validation    = '';
2607		$object->date               = (empty($this->date) ? '' : $this->date);
2608		$object->date_echeance      = '';
2609		$object->ref_client         = '';
2610		$object->close_code         = '';
2611		$object->close_note         = '';
2612
2613		// Loop on each line of new invoice
2614		foreach ($object->lines as $i => $line)
2615		{
2616			if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02)	// We do not clone line of discounts
2617			{
2618				unset($object->lines[$i]);
2619			}
2620		}
2621
2622		// Create clone
2623		$object->context['createfromclone'] = 'createfromclone';
2624		$result = $object->create($user);
2625
2626		// Other options
2627		if ($result < 0)
2628		{
2629			$this->error = $object->error;
2630			$this->errors = $object->errors;
2631			$error++;
2632		}
2633
2634		if (!$error)
2635		{
2636		}
2637
2638		unset($object->context['createfromclone']);
2639
2640		// End
2641		if (!$error)
2642		{
2643			$this->db->commit();
2644			return $object->id;
2645		} else {
2646			$this->db->rollback();
2647			return -1;
2648		}
2649	}
2650
2651	/**
2652	 *	Create a document onto disk according to template model.
2653	 *
2654	 *	@param	    string		$modele			Force template to use ('' to not force)
2655	 *	@param		Translate	$outputlangs	Object lang a utiliser pour traduction
2656	 *  @param      int			$hidedetails    Hide details of lines
2657	 *  @param      int			$hidedesc       Hide description
2658	 *  @param      int			$hideref        Hide ref
2659	 *  @param   null|array  $moreparams     Array to provide more information
2660	 *  @return     int         				<0 if KO, 0 if nothing done, >0 if OK
2661	 */
2662	public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2663	{
2664		global $conf, $user, $langs;
2665
2666		$langs->load("suppliers");
2667		$outputlangs->load("products");
2668
2669		// Set the model on the model name to use
2670		if (empty($modele))
2671		{
2672			if (!empty($conf->global->INVOICE_SUPPLIER_ADDON_PDF))
2673			{
2674				$modele = $conf->global->INVOICE_SUPPLIER_ADDON_PDF;
2675			} else {
2676				$modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation
2677			}
2678		}
2679
2680		if (empty($modele))
2681		{
2682			return 0;
2683		} else {
2684			$modelpath = "core/modules/supplier_invoice/doc/";
2685
2686			return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2687		}
2688	}
2689
2690	/**
2691	 * Returns the rights used for this class
2692	 * @return stdClass
2693	 */
2694	public function getRights()
2695	{
2696		global $user;
2697
2698		return $user->rights->fournisseur->facture;
2699	}
2700
2701	/**
2702	 * Function used to replace a thirdparty id with another one.
2703	 *
2704	 * @param DoliDB $db Database handler
2705	 * @param int $origin_id Old thirdparty id
2706	 * @param int $dest_id New thirdparty id
2707	 * @return bool
2708	 */
2709	public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
2710	{
2711		$tables = array(
2712			'facture_fourn'
2713		);
2714
2715		return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
2716	}
2717
2718	/**
2719	 * Is the payment of the supplier invoice having a delay?
2720	 *
2721	 * @return bool
2722	 */
2723	public function hasDelay()
2724	{
2725		global $conf;
2726
2727		$now = dol_now();
2728
2729		if (!$this->date_echeance) {
2730			return false;
2731		}
2732
2733		return ($this->statut == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay));
2734	}
2735
2736	/**
2737	 * Is credit note used
2738	 *
2739	 * @return bool
2740	 */
2741	public function isCreditNoteUsed()
2742	{
2743		$isUsed = false;
2744
2745		$sql = "SELECT fk_invoice_supplier FROM ".MAIN_DB_PREFIX."societe_remise_except WHERE fk_invoice_supplier_source=".$this->id;
2746		$resql = $this->db->query($sql);
2747		if (!empty($resql)) {
2748			$obj = $this->db->fetch_object($resql);
2749			if (!empty($obj->fk_invoice_supplier)) $isUsed = true;
2750		}
2751
2752		return $isUsed;
2753	}
2754}
2755
2756
2757
2758/**
2759 *  Class to manage line invoices
2760 */
2761class SupplierInvoiceLine extends CommonObjectLine
2762{
2763	/**
2764	 * @var string ID to identify managed object
2765	 */
2766	public $element = 'facture_fourn_det';
2767
2768	/**
2769	 * @var string Name of table without prefix where object is stored
2770	 */
2771	public $table_element = 'facture_fourn_det';
2772
2773	public $oldline;
2774
2775	/**
2776	 * @deprecated
2777	 * @see $product_ref
2778	 */
2779	public $ref;
2780
2781	/**
2782	 * Internal ref
2783	 * @var string
2784	 */
2785	public $product_ref;
2786
2787	/**
2788	 * Supplier reference of price when we added the line. May have been changed after line was added.
2789	 * TODO Rename field ref to ref_supplier into table llx_facture_fourn_det and llx_commande_fournisseurdet and update fields into updateline
2790	 * @var string
2791	 */
2792	public $ref_supplier;
2793
2794	/**
2795	 * Product description
2796	 * @var string
2797	 */
2798	public $product_desc;
2799
2800	/**
2801	 * Unit price before taxes
2802	 * @var float
2803	 * @deprecated Use $subprice
2804	 * @see $subprice
2805	 */
2806	public $pu_ht;
2807
2808	/**
2809	 * Unit price excluded taxes
2810	 * @var float
2811	 */
2812	public $subprice;
2813
2814	/**
2815	 * Unit price included taxes
2816	 * @var float
2817	 */
2818	public $pu_ttc;
2819
2820
2821	/**
2822	 * Id of the corresponding supplier invoice
2823	 * @var int
2824	 */
2825	public $fk_facture_fourn;
2826
2827	/**
2828	 * This field may contains label of line (when invoice create from order)
2829	 * @var string
2830	 * @deprecated
2831	 */
2832	public $label;
2833
2834	/**
2835	 * Description of the line
2836	 * @var string
2837	 */
2838	public $description;
2839
2840	public $date_start;
2841	public $date_end;
2842
2843	public $skip_update_total; // Skip update price total for special lines
2844
2845	/**
2846	 * @var int Situation advance percentage
2847	 */
2848	public $situation_percent;
2849
2850	/**
2851	 * @var int Previous situation line id reference
2852	 */
2853	public $fk_prev_id;
2854
2855	/**
2856	 * VAT code
2857	 * @var string
2858	 */
2859	public $vat_src_code;
2860
2861	/**
2862	 * VAT %
2863	 * @var float
2864	 */
2865	public $tva_tx;
2866
2867	/**
2868	 * Local tax 1 %
2869	 * @var float
2870	 */
2871	public $localtax1_tx;
2872
2873	/**
2874	 * Local tax 2 %
2875	 * @var float
2876	 */
2877	public $localtax2_tx;
2878
2879	/**
2880	 * Quantity
2881	 * @var double
2882	 */
2883	public $qty;
2884
2885	/**
2886	 * Percent of discount
2887	 * @var float
2888	 */
2889	public $remise_percent;
2890
2891	/**
2892	 * Total amount without taxes
2893	 * @var float
2894	 */
2895	public $total_ht;
2896
2897	/**
2898	 * Total amount with taxes
2899	 * @var float
2900	 */
2901	public $total_ttc;
2902
2903	/**
2904	 * Total amount of taxes
2905	 * @var float
2906	 */
2907	public $total_tva;
2908
2909	/**
2910	 * Total local tax 1 amount
2911	 * @var float
2912	 */
2913	public $total_localtax1;
2914
2915	/**
2916	 * Total local tax 2 amount
2917	 * @var float
2918	 */
2919	public $total_localtax2;
2920
2921	/**
2922	 * @var int ID
2923	 */
2924	public $fk_product;
2925
2926	/**
2927	 * Type of the product. 0 for product 1 for service
2928	 * @var int
2929	 */
2930	public $product_type;
2931
2932	/**
2933	 * Label of the product
2934	 * @var string
2935	 */
2936	public $product_label;
2937
2938	/**
2939	 * List of cumulative options:
2940	 * Bit 0:	0 si TVA normal - 1 si TVA NPR
2941	 * Bit 1:	0 si ligne normal - 1 si bit discount (link to line into llx_remise_except)
2942	 * @var int
2943	 */
2944	public $info_bits;
2945
2946	/**
2947	 * @var int ID
2948	 */
2949	public $fk_parent_line;
2950
2951	public $special_code;
2952
2953	/**
2954	 * @var int rank of line
2955	 */
2956	public $rang;
2957
2958	/**
2959	 * Total local tax 1 amount
2960	 * @var float
2961	 */
2962	public $localtax1_type;
2963
2964	/**
2965	 * Total local tax 2 amount
2966	 * @var float
2967	 */
2968	public $localtax2_type;
2969
2970	// Multicurrency
2971	/**
2972	 * @var int ID
2973	 */
2974	public $fk_multicurrency;
2975
2976	public $multicurrency_code;
2977	public $multicurrency_subprice;
2978	public $multicurrency_total_ht;
2979	public $multicurrency_total_tva;
2980	public $multicurrency_total_ttc;
2981
2982
2983	/**
2984	 *	Constructor
2985	 *
2986	 *  @param		DoliDB		$db      Database handler
2987	 */
2988	public function __construct($db)
2989	{
2990		$this->db = $db;
2991	}
2992
2993	/**
2994	 * Retrieves a supplier invoice line
2995	 *
2996	 * @param    int    $rowid    Line id
2997	 * @return   int              <0 KO; 0 NOT FOUND; 1 OK
2998	 */
2999	public function fetch($rowid)
3000	{
3001		$sql = 'SELECT f.rowid, f.ref as ref_supplier, f.description, f.date_start, f.date_end, f.pu_ht, f.pu_ttc, f.qty, f.remise_percent, f.tva_tx';
3002		$sql .= ', f.localtax1_type, f.localtax2_type, f.localtax1_tx, f.localtax2_tx, f.total_localtax1, f.total_localtax2 ';
3003		$sql .= ', f.total_ht, f.tva as total_tva, f.total_ttc, f.fk_facture_fourn, f.fk_product, f.product_type, f.info_bits, f.rang, f.special_code, f.fk_parent_line, f.fk_unit';
3004		$sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.description as product_desc';
3005		$sql .= ', f.multicurrency_subprice, f.multicurrency_total_ht, f.multicurrency_total_tva, multicurrency_total_ttc';
3006		$sql .= ' FROM '.MAIN_DB_PREFIX.'facture_fourn_det as f';
3007		$sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON f.fk_product = p.rowid';
3008		$sql .= ' WHERE f.rowid = '.$rowid;
3009		$sql .= ' ORDER BY f.rang, f.rowid';
3010
3011		$query = $this->db->query($sql);
3012
3013		if (!$query) {
3014			$this->errors[] = $this->db->error();
3015			return -1;
3016		}
3017
3018		if (!$this->db->num_rows($query)) {
3019			return 0;
3020		}
3021
3022		$obj = $this->db->fetch_object($query);
3023
3024		$this->id = $obj->rowid;
3025		$this->rowid = $obj->rowid;
3026		$this->fk_facture_fourn = $obj->fk_facture_fourn;
3027		$this->description		= $obj->description;
3028		$this->date_start = $obj->date_start;
3029		$this->date_end = $obj->date_end;
3030		$this->product_ref		= $obj->product_ref;
3031		$this->ref_supplier		= $obj->ref_supplier;
3032		$this->product_desc		= $obj->product_desc;
3033
3034		$this->subprice 		= $obj->pu_ht;
3035		$this->pu_ht			= $obj->pu_ht;
3036		$this->pu_ttc			= $obj->pu_ttc;
3037		$this->tva_tx			= $obj->tva_tx;
3038		$this->localtax1_tx		= $obj->localtax1_tx;
3039		$this->localtax2_tx		= $obj->localtax2_tx;
3040		$this->localtax1_type	= $obj->localtax1_type;
3041		$this->localtax2_type	= $obj->localtax2_type;
3042
3043		$this->qty				= $obj->qty;
3044		$this->remise_percent = $obj->remise_percent;
3045		$this->tva				= $obj->total_tva; // deprecated
3046		$this->total_ht = $obj->total_ht;
3047		$this->total_tva			= $obj->total_tva;
3048		$this->total_localtax1	= $obj->total_localtax1;
3049		$this->total_localtax2	= $obj->total_localtax2;
3050		$this->total_ttc			= $obj->total_ttc;
3051		$this->fk_product		= $obj->fk_product;
3052		$this->product_type = $obj->product_type;
3053		$this->product_label		= $obj->product_label;
3054		$this->info_bits		    = $obj->info_bits;
3055		$this->tva_npr = ($obj->info_bits & 1 == 1) ? 1 : 0;
3056		$this->fk_parent_line    = $obj->fk_parent_line;
3057		$this->special_code = $obj->special_code;
3058		$this->rang = $obj->rang;
3059		$this->fk_unit           = $obj->fk_unit;
3060
3061		$this->multicurrency_subprice = $obj->multicurrency_subprice;
3062		$this->multicurrency_total_ht = $obj->multicurrency_total_ht;
3063		$this->multicurrency_total_tva = $obj->multicurrency_total_tva;
3064		$this->multicurrency_total_ttc = $obj->multicurrency_total_ttc;
3065
3066		$this->fetch_optionals();
3067
3068		return 1;
3069	}
3070
3071	/**
3072	 * Deletes a line
3073	 *
3074	 * @param     bool|int   $notrigger     1=Does not execute triggers, 0= execute triggers
3075	 * @return    int                       0 if KO, 1 if OK
3076	 */
3077	public function delete($notrigger = 0)
3078	{
3079		global $user, $conf;
3080
3081		dol_syslog(get_class($this)."::deleteline rowid=".$this->id, LOG_DEBUG);
3082
3083		$error = 0;
3084
3085		$this->db->begin();
3086
3087		if (!$notrigger) {
3088			if ($this->call_trigger('LINEBILL_SUPPLIER_DELETE', $user) < 0) {
3089				$error++;
3090			}
3091		}
3092
3093		$this->deleteObjectLinked();
3094
3095		// Remove extrafields
3096		if (!$error)
3097		{
3098			$result = $this->deleteExtraFields();
3099			if ($result < 0)
3100			{
3101				$error++;
3102				dol_syslog(get_class($this)."::delete error -4 ".$this->error, LOG_ERR);
3103			}
3104		}
3105
3106		if (!$error) {
3107			// Supprime ligne
3108			$sql = 'DELETE FROM '.MAIN_DB_PREFIX.'facture_fourn_det ';
3109			$sql .= ' WHERE rowid = '.$this->id;
3110			dol_syslog(get_class($this)."::delete", LOG_DEBUG);
3111			$resql = $this->db->query($sql);
3112			if (!$resql) {
3113				$error++;
3114				$this->error = $this->db->lasterror();
3115			}
3116		}
3117
3118		if (!$error)
3119		{
3120			$this->db->commit();
3121			return 1;
3122		} else {
3123			$this->db->rollback();
3124			return -1;
3125		}
3126	}
3127
3128	/**
3129	 * Update a supplier invoice line
3130	 *
3131	 * @param int $notrigger Disable triggers
3132	 * @return int <0 if KO, >0 if OK
3133	 */
3134	public function update($notrigger = 0)
3135	{
3136		global $conf;
3137
3138		$pu = price2num($this->pu_ht);
3139		$qty = price2num($this->qty);
3140
3141		// Check parameters
3142		if (empty($this->qty)) $this->qty = 0;
3143
3144		if ($this->product_type < 0) {
3145			return -1;
3146		}
3147
3148		// Clean parameters
3149		if (empty($this->remise_percent)) $this->remise_percent = 0;
3150		if (empty($this->tva_tx))  		  $this->tva_tx = 0;
3151		if (empty($this->localtax1_tx))   $this->localtax1_tx = 0;
3152		if (empty($this->localtax2_tx))   $this->localtax2_tx = 0;
3153
3154		$this->db->begin();
3155
3156		if (empty($this->fk_product))
3157		{
3158			$fk_product = "null";
3159		} else {
3160			$fk_product = $this->fk_product;
3161		}
3162
3163		if (empty($this->fk_unit)) {
3164			$fk_unit = "null";
3165		} else {
3166			$fk_unit = "'".$this->db->escape($this->fk_unit)."'";
3167		}
3168
3169		$sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
3170		$sql .= "  description ='".$this->db->escape($this->description)."'";
3171		$sql .= ", ref ='".$this->db->escape($this->ref_supplier ? $this->ref_supplier : $this->ref)."'";
3172		$sql .= ", date_start = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null");
3173		$sql .= ", date_end = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null");
3174		$sql .= ", pu_ht = ".price2num($this->pu_ht);
3175		$sql .= ", pu_ttc = ".price2num($this->pu_ttc);
3176		$sql .= ", qty = ".price2num($this->qty);
3177		$sql .= ", remise_percent = ".price2num($this->remise_percent);
3178		$sql .= ", vat_src_code = '".$this->db->escape(empty($this->vat_src_code) ? '' : $this->vat_src_code)."'";
3179		$sql .= ", tva_tx = ".price2num($this->tva_tx);
3180		$sql .= ", localtax1_tx = ".price2num($this->localtax1_tx);
3181		$sql .= ", localtax2_tx = ".price2num($this->localtax2_tx);
3182		$sql .= ", localtax1_type = '".$this->db->escape($this->localtax1_type)."'";
3183		$sql .= ", localtax2_type = '".$this->db->escape($this->localtax2_type)."'";
3184		$sql .= ", total_ht = ".price2num($this->total_ht);
3185		$sql .= ", tva= ".price2num($this->total_tva);
3186		$sql .= ", total_localtax1= ".price2num($this->total_localtax1);
3187		$sql .= ", total_localtax2= ".price2num($this->total_localtax2);
3188		$sql .= ", total_ttc = ".price2num($this->total_ttc);
3189		$sql .= ", fk_product = ".$fk_product;
3190		$sql .= ", product_type = ".$this->product_type;
3191		$sql .= ", info_bits = ".$this->info_bits;
3192		$sql .= ", fk_unit = ".$fk_unit;
3193
3194		// Multicurrency
3195		$sql .= " , multicurrency_subprice=".price2num($this->multicurrency_subprice)."";
3196		$sql .= " , multicurrency_total_ht=".price2num($this->multicurrency_total_ht)."";
3197		$sql .= " , multicurrency_total_tva=".price2num($this->multicurrency_total_tva)."";
3198		$sql .= " , multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc)."";
3199
3200		$sql .= " WHERE rowid = ".$this->id;
3201
3202		dol_syslog(get_class($this)."::update", LOG_DEBUG);
3203		$resql = $this->db->query($sql);
3204
3205		if (!$resql) {
3206			$this->db->rollback();
3207			$this->error = $this->db->lasterror();
3208			return -1;
3209		}
3210
3211		$this->rowid = $this->id;
3212		$error = 0;
3213
3214		if (!$error)
3215		{
3216			$result = $this->insertExtraFields();
3217			if ($result < 0)
3218			{
3219				$error++;
3220			}
3221		}
3222
3223		if (!$error && !$notrigger)
3224		{
3225			global $langs, $user;
3226
3227			// Call trigger
3228			if ($this->call_trigger('LINEBILL_SUPPLIER_UPDATE', $user) < 0) {
3229				$this->db->rollback();
3230				return -1;
3231			}
3232			// End call triggers
3233		}
3234
3235		if ($error) {
3236			$this->db->rollback();
3237			return -1;
3238		}
3239
3240		$this->db->commit();
3241		return 1;
3242	}
3243
3244	/**
3245	 *	Insert line into database
3246	 *
3247	 *	@param      int		$notrigger		1 no triggers
3248	 *	@return		int						<0 if KO, >0 if OK
3249	 */
3250	public function insert($notrigger = 0)
3251	{
3252		global $user, $conf;
3253
3254		$error = 0;
3255
3256		dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
3257
3258		// Clean parameters
3259		$this->desc = trim($this->desc);
3260		if (empty($this->tva_tx)) $this->tva_tx = 0;
3261		if (empty($this->localtax1_tx)) $this->localtax1_tx = 0;
3262		if (empty($this->localtax2_tx)) $this->localtax2_tx = 0;
3263		if (empty($this->localtax1_type)) $this->localtax1_type = '0';
3264		if (empty($this->localtax2_type)) $this->localtax2_type = '0';
3265		if (empty($this->total_tva)) $this->total_tva = 0;
3266		if (empty($this->total_localtax1)) $this->total_localtax1 = 0;
3267		if (empty($this->total_localtax2)) $this->total_localtax2 = 0;
3268		if (empty($this->rang)) $this->rang = 0;
3269		if (empty($this->remise_percent)) $this->remise_percent = 0;
3270		if (empty($this->info_bits)) $this->info_bits = 0;
3271		if (empty($this->subprice)) $this->subprice = 0;
3272		if (empty($this->special_code)) $this->special_code = 0;
3273		if (empty($this->fk_parent_line)) $this->fk_parent_line = 0;
3274		if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') $this->situation_percent = 100;
3275
3276		if (empty($this->pa_ht)) $this->pa_ht = 0;
3277		if (empty($this->multicurrency_subprice)) $this->multicurrency_subprice = 0;
3278		if (empty($this->multicurrency_total_ht)) $this->multicurrency_total_ht = 0;
3279		if (empty($this->multicurrency_total_tva)) $this->multicurrency_total_tva = 0;
3280		if (empty($this->multicurrency_total_ttc)) $this->multicurrency_total_ttc = 0;
3281
3282
3283		// Check parameters
3284		if ($this->product_type < 0)
3285		{
3286			$this->error = 'ErrorProductTypeMustBe0orMore';
3287			return -1;
3288		}
3289		if (!empty($this->fk_product))
3290		{
3291			// Check product exists
3292			$result = Product::isExistingObject('product', $this->fk_product);
3293			if ($result <= 0)
3294			{
3295				$this->error = 'ErrorProductIdDoesNotExists';
3296				return -1;
3297			}
3298		}
3299
3300		$this->db->begin();
3301
3302		// Insertion dans base de la ligne
3303		$sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
3304		$sql .= ' (fk_facture_fourn, fk_parent_line, label, description, ref, qty,';
3305		$sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
3306		$sql .= ' fk_product, product_type, remise_percent, pu_ht, pu_ttc,';
3307		$sql .= ' date_start, date_end, fk_code_ventilation, rang, special_code,';
3308		$sql .= ' info_bits, total_ht, tva, total_ttc, total_localtax1, total_localtax2, fk_unit';
3309		$sql .= ', fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3310		$sql .= ')';
3311		$sql .= " VALUES (".$this->fk_facture_fourn.",";
3312		$sql .= " ".($this->fk_parent_line > 0 ? "'".$this->db->escape($this->fk_parent_line)."'" : "null").",";
3313		$sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
3314		$sql .= " '".$this->db->escape($this->desc ? $this->desc : $this->description)."',";
3315		$sql .= " '".$this->db->escape($this->ref_supplier)."',";
3316		$sql .= " ".price2num($this->qty).",";
3317
3318		$sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
3319		$sql .= " ".price2num($this->tva_tx).",";
3320		$sql .= " ".price2num($this->localtax1_tx).",";
3321		$sql .= " ".price2num($this->localtax2_tx).",";
3322		$sql .= " '".$this->db->escape($this->localtax1_type)."',";
3323		$sql .= " '".$this->db->escape($this->localtax2_type)."',";
3324		$sql .= ' '.(!empty($this->fk_product) ? $this->fk_product : "null").',';
3325		$sql .= " ".$this->product_type.",";
3326		$sql .= " ".price2num($this->remise_percent).",";
3327		$sql .= " ".price2num($this->subprice).",";
3328		$sql .= " ".(!empty($this->qty) ?price2num($this->total_ttc / $this->qty) : price2num($this->total_ttc)).",";
3329		$sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").",";
3330		$sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").",";
3331		$sql .= ' '.(!empty($this->fk_code_ventilation) ? $this->fk_code_ventilation : 0).',';
3332		$sql .= ' '.$this->rang.',';
3333		$sql .= ' '.$this->special_code.',';
3334		$sql .= " '".$this->db->escape($this->info_bits)."',";
3335		$sql .= " ".price2num($this->total_ht).",";
3336		$sql .= " ".price2num($this->total_tva).",";
3337		$sql .= " ".price2num($this->total_ttc).",";
3338		$sql .= " ".price2num($this->total_localtax1).",";
3339		$sql .= " ".price2num($this->total_localtax2);
3340		$sql .= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
3341		$sql .= ", ".(int) $this->fk_multicurrency;
3342		$sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
3343		$sql .= ", ".price2num($this->multicurrency_subprice);
3344		$sql .= ", ".price2num($this->multicurrency_total_ht);
3345		$sql .= ", ".price2num($this->multicurrency_total_tva);
3346		$sql .= ", ".price2num($this->multicurrency_total_ttc);
3347		$sql .= ')';
3348
3349		$resql = $this->db->query($sql);
3350		if ($resql)
3351		{
3352			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
3353			$this->rowid = $this->id; // backward compatibility
3354
3355			if (!$error)
3356			{
3357				$result = $this->insertExtraFields();
3358				if ($result < 0)
3359				{
3360					$error++;
3361				}
3362			}
3363
3364			if (!$error && !$notrigger)
3365			{
3366				// Call trigger
3367				$result = $this->call_trigger('LINEBILL_SUPPLIER_CREATE', $user);
3368				if ($result < 0)
3369				{
3370					$this->db->rollback();
3371					return -2;
3372				}
3373				// End call triggers
3374			}
3375
3376			$this->db->commit();
3377			return $this->id;
3378		} else {
3379			$this->error = $this->db->error();
3380			$this->db->rollback();
3381			return -2;
3382		}
3383	}
3384
3385	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3386	/**
3387	 *  Mise a jour de l'objet ligne de commande en base
3388	 *
3389	 *  @return		int		<0 si ko, >0 si ok
3390	 */
3391	public function update_total()
3392	{
3393		// phpcs:enable
3394		$this->db->begin();
3395
3396		// Mise a jour ligne en base
3397		$sql = "UPDATE ".MAIN_DB_PREFIX."facture_fourn_det SET";
3398		$sql .= "  total_ht='".price2num($this->total_ht)."'";
3399		$sql .= ", tva='".price2num($this->total_tva)."'";
3400		$sql .= ", total_localtax1='".price2num($this->total_localtax1)."'";
3401		$sql .= ", total_localtax2='".price2num($this->total_localtax2)."'";
3402		$sql .= ", total_ttc='".price2num($this->total_ttc)."'";
3403		$sql .= " WHERE rowid = ".$this->rowid;
3404
3405		dol_syslog("FactureFournisseurLigne.class.php::update_total", LOG_DEBUG);
3406
3407		$resql = $this->db->query($sql);
3408		if ($resql)
3409		{
3410			$this->db->commit();
3411			return 1;
3412		} else {
3413			$this->error = $this->db->error();
3414			$this->db->rollback();
3415			return -2;
3416		}
3417	}
3418}
3419