Passed
Branch develop (356b3a)
by
unknown
98:06
created

FactureFournisseur::getTooltipContentArray()   C

Complexity

Conditions 17

Size

Total Lines 60
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
eloc 40
nop 1
dl 0
loc 60
rs 5.2166
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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