1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* Copyright (C) 2002-2004 Rodolphe Quiedeville <[email protected]> |
4
|
|
|
* Copyright (C) 2004-2012 Laurent Destailleur <[email protected]> |
5
|
|
|
* Copyright (C) 2004 Christophe Combelles <[email protected]> |
6
|
|
|
* Copyright (C) 2005 Marc Barilley <[email protected]> |
7
|
|
|
* Copyright (C) 2005-2012 Regis Houssin <[email protected]> |
8
|
|
|
* Copyright (C) 2010-2020 Juanjo Menent <[email protected]> |
9
|
|
|
* Copyright (C) 2013-2019 Philippe Grand <[email protected]> |
10
|
|
|
* Copyright (C) 2013 Florian Henry <[email protected]> |
11
|
|
|
* Copyright (C) 2014-2016 Marcos García <[email protected]> |
12
|
|
|
* Copyright (C) 2015 Bahfir Abbes <[email protected]> |
13
|
|
|
* Copyright (C) 2015-2022 Ferran Marcet <[email protected]> |
14
|
|
|
* Copyright (C) 2016-2023 Alexandre Spangaro <[email protected]> |
15
|
|
|
* Copyright (C) 2018 Nicolas ZABOURI <[email protected]> |
16
|
|
|
* Copyright (C) 2018-2024 Frédéric France <[email protected]> |
17
|
|
|
* Copyright (C) 2022 Gauthier VERDOL <[email protected]> |
18
|
|
|
* Copyright (C) 2023 Nick Fragoulis |
19
|
|
|
* Copyright (C) 2024 MDW <[email protected]> |
20
|
|
|
* Copyright (C) 2024 Rafael San José <[email protected]> |
21
|
|
|
* |
22
|
|
|
* This program is free software; you can redistribute it and/or modify |
23
|
|
|
* it under the terms of the GNU General Public License as published by |
24
|
|
|
* the Free Software Foundation; either version 3 of the License, or |
25
|
|
|
* (at your option) any later version. |
26
|
|
|
* |
27
|
|
|
* This program is distributed in the hope that it will be useful, |
28
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
29
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
30
|
|
|
* GNU General Public License for more details. |
31
|
|
|
* |
32
|
|
|
* You should have received a copy of the GNU General Public License |
33
|
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>. |
34
|
|
|
*/ |
35
|
|
|
|
36
|
|
|
namespace Dolibarr\Code\Fourn\Classes; |
37
|
|
|
|
38
|
|
|
use Dolibarr\Code\Core\Classes\CommonInvoice; |
39
|
|
|
use Dolibarr\Code\Core\Classes\CommonInvoiceLine; |
40
|
|
|
use Dolibarr\Code\Core\Classes\WorkboardResponse; |
41
|
|
|
use Dolibarr\Code\MultiCurrency\Classes\MultiCurrency; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* \file htdocs/fourn/class/fournisseur.facture.class.php |
45
|
|
|
* \ingroup fournisseur,facture |
46
|
|
|
* \brief File of class to manage suppliers invoices |
47
|
|
|
*/ |
48
|
|
|
|
49
|
|
|
use Dolibarr\Core\Base\CommonObjectLine; |
50
|
|
|
use DoliDB; |
51
|
|
|
|
52
|
|
|
if (isModEnabled('accounting')) { |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* Class to manage suppliers invoices |
57
|
|
|
*/ |
58
|
|
|
class FactureFournisseur extends CommonInvoice |
59
|
|
|
{ |
60
|
|
|
/** |
61
|
|
|
* @var string ID to identify managed object |
62
|
|
|
*/ |
63
|
|
|
public $element = 'invoice_supplier'; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* @var string Name of table without prefix where object is stored |
67
|
|
|
*/ |
68
|
|
|
public $table_element = 'facture_fourn'; |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @var string Name of subtable line |
72
|
|
|
*/ |
73
|
|
|
public $table_element_line = 'facture_fourn_det'; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* @var string Name of class line |
77
|
|
|
*/ |
78
|
|
|
public $class_element_line = 'SupplierInvoiceLine'; |
79
|
|
|
/** |
80
|
|
|
* @var string Field with ID of parent key if this field has a parent |
81
|
|
|
*/ |
82
|
|
|
public $fk_element = 'fk_facture_fourn'; |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png |
86
|
|
|
*/ |
87
|
|
|
public $picto = 'supplier_invoice'; |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* 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 |
91
|
|
|
* @var integer |
92
|
|
|
*/ |
93
|
|
|
public $restrictiononfksoc = 1; |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* {@inheritdoc} |
97
|
|
|
*/ |
98
|
|
|
protected $table_ref_field = 'ref'; |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* @var int ID |
102
|
|
|
*/ |
103
|
|
|
public $rowid; |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* @var string Ref |
107
|
|
|
*/ |
108
|
|
|
public $ref; |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @var string Ref supplier |
112
|
|
|
*/ |
113
|
|
|
public $ref_supplier; |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* @var string Label of invoice |
117
|
|
|
* @deprecated Use $label |
118
|
|
|
*/ |
119
|
|
|
public $libelle; |
120
|
|
|
/** |
121
|
|
|
* @var string Label of invoice |
122
|
|
|
*/ |
123
|
|
|
public $label; |
124
|
|
|
|
125
|
|
|
//Check constants for types |
126
|
|
|
public $type = self::TYPE_STANDARD; |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Supplier invoice status |
130
|
|
|
* @var int |
131
|
|
|
* @deprecated |
132
|
|
|
* @see $status |
133
|
|
|
*/ |
134
|
|
|
public $statut; |
135
|
|
|
|
136
|
|
|
/** |
137
|
|
|
* Supplier invoice status |
138
|
|
|
* @var int |
139
|
|
|
* @see FactureFournisseur::STATUS_DRAFT, FactureFournisseur::STATUS_VALIDATED, FactureFournisseur::STATUS_PAID, FactureFournisseur::STATUS_ABANDONED |
140
|
|
|
*/ |
141
|
|
|
public $status; |
142
|
|
|
|
143
|
|
|
/** |
144
|
|
|
* Supplier invoice status |
145
|
|
|
* @var int |
146
|
|
|
* @deprecated |
147
|
|
|
* @see $status |
148
|
|
|
*/ |
149
|
|
|
public $fk_statut; |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* Set to 1 if the invoice is completely paid, otherwise is 0 |
153
|
|
|
* @var int<0,1> |
154
|
|
|
* @deprecated Use $paid |
155
|
|
|
*/ |
156
|
|
|
public $paye; |
157
|
|
|
/** |
158
|
|
|
* Set to 1 if the invoice is completely paid, otherwise is 0 |
159
|
|
|
* @var int<0,1> |
160
|
|
|
*/ |
161
|
|
|
public $paid; |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* @var int |
165
|
|
|
* @deprecated Use $user_creation_id |
166
|
|
|
*/ |
167
|
|
|
public $author; |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* Date creation record (datec) |
171
|
|
|
* |
172
|
|
|
* @var integer |
173
|
|
|
*/ |
174
|
|
|
public $datec; |
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 See $total_ttc, $total_ht, $total_tva |
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 definition. it must stay null. |
201
|
|
|
// For example to avoid to have substitution done when object is generic and not yet defined. |
202
|
|
|
/** @var ?string */ |
203
|
|
|
public $localtax1; |
204
|
|
|
/** @var ?string */ |
205
|
|
|
public $localtax2; |
206
|
|
|
/** @var float */ |
207
|
|
|
public $total_ht; |
208
|
|
|
/** @var float */ |
209
|
|
|
public $total_tva; |
210
|
|
|
/** @var float */ |
211
|
|
|
public $total_localtax1; |
212
|
|
|
/** @var float */ |
213
|
|
|
public $total_localtax2; |
214
|
|
|
/** @var float */ |
215
|
|
|
public $total_ttc; |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* @deprecated |
219
|
|
|
* @see $note_private, $note_public |
220
|
|
|
* @var string |
221
|
|
|
*/ |
222
|
|
|
public $note; |
223
|
|
|
/** @var string */ |
224
|
|
|
public $note_private; |
225
|
|
|
/** @var string */ |
226
|
|
|
public $note_public; |
227
|
|
|
/** @var int */ |
228
|
|
|
public $propalid; |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* @var int ID |
232
|
|
|
*/ |
233
|
|
|
public $fk_account; // default bank account |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* @var int Transport mode id |
237
|
|
|
*/ |
238
|
|
|
public $transport_mode_id; |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* @var int<0,1> VAT reverse charge can be used on the invoice |
242
|
|
|
*/ |
243
|
|
|
public $vat_reverse_charge; |
244
|
|
|
|
245
|
|
|
public $extraparams = array(); |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Invoice lines |
249
|
|
|
* @var CommonInvoiceLine[] |
250
|
|
|
*/ |
251
|
|
|
public $lines = array(); |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* @deprecated |
255
|
|
|
* @var ?Fournisseur |
256
|
|
|
*/ |
257
|
|
|
public $fournisseur; |
258
|
|
|
|
259
|
|
|
//! id of source invoice if replacement invoice or credit note |
260
|
|
|
/** |
261
|
|
|
* @var int ID |
262
|
|
|
*/ |
263
|
|
|
public $fk_facture_source; |
264
|
|
|
|
265
|
|
|
/** @var int */ |
266
|
|
|
public $fac_rec; |
267
|
|
|
/** @var int */ |
268
|
|
|
public $fk_fac_rec_source; |
269
|
|
|
|
270
|
|
|
public $fields = array( |
271
|
|
|
'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10), |
272
|
|
|
'ref' => array('type' => 'varchar(255)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15), |
273
|
|
|
'ref_supplier' => array('type' => 'varchar(255)', 'label' => 'RefSupplier', 'enabled' => 1, 'visible' => -1, 'position' => 20), |
274
|
|
|
'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 25, 'index' => 1), |
275
|
|
|
'ref_ext' => array('type' => 'varchar(255)', 'label' => 'RefExt', 'enabled' => 1, 'visible' => 0, 'position' => 30), |
276
|
|
|
'type' => array('type' => 'smallint(6)', 'label' => 'Type', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 35), |
277
|
|
|
'subtype' => array('type' => 'smallint(6)', 'label' => 'InvoiceSubtype', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 36), |
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' => 'isModEnabled("bank")', '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 completely, 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
|
|
|
$this->ismultientitymanaged = 1; |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
/** |
389
|
|
|
* Create supplier invoice into database |
390
|
|
|
* |
391
|
|
|
* @param User $user user object that creates |
392
|
|
|
* @return int Id invoice created if OK, < 0 if KO |
393
|
|
|
*/ |
394
|
|
|
public function create($user) |
395
|
|
|
{ |
396
|
|
|
global $langs, $conf, $hookmanager; |
397
|
|
|
|
398
|
|
|
$error = 0; |
399
|
|
|
$now = dol_now(); |
400
|
|
|
|
401
|
|
|
// Clean parameters |
402
|
|
|
if (isset($this->ref_supplier)) { |
403
|
|
|
$this->ref_supplier = trim($this->ref_supplier); |
404
|
|
|
} |
405
|
|
|
if (empty($this->type)) { |
406
|
|
|
$this->type = self::TYPE_STANDARD; |
407
|
|
|
} |
408
|
|
|
if (empty($this->date)) { |
409
|
|
|
$this->date = $now; |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
// Multicurrency (test on $this->multicurrency_tx because we should take the default rate only if not using origin rate) |
413
|
|
|
if (!empty($this->multicurrency_code) && empty($this->multicurrency_tx)) { |
414
|
|
|
list($this->fk_multicurrency, $this->multicurrency_tx) = MultiCurrency::getIdAndTxFromCode($this->db, $this->multicurrency_code, $this->date); |
415
|
|
|
} else { |
416
|
|
|
$this->fk_multicurrency = MultiCurrency::getIdFromCode($this->db, $this->multicurrency_code); |
417
|
|
|
} |
418
|
|
|
if (empty($this->fk_multicurrency)) { |
419
|
|
|
$this->multicurrency_code = $conf->currency; |
420
|
|
|
$this->fk_multicurrency = 0; |
421
|
|
|
$this->multicurrency_tx = 1; |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
$this->db->begin(); |
425
|
|
|
|
426
|
|
|
// Create invoice from a template recurring invoice |
427
|
|
|
if ($this->fac_rec > 0) { |
428
|
|
|
$this->fk_fac_rec_source = $this->fac_rec; |
429
|
|
|
|
430
|
|
|
$_facrec = new FactureFournisseurRec($this->db); |
431
|
|
|
$result = $_facrec->fetch($this->fac_rec); |
432
|
|
|
$result = $_facrec->fetchObjectLinked(null, '', null, '', 'OR', 1, 'sourcetype', 0); // This load $_facrec->linkedObjectsIds |
433
|
|
|
|
434
|
|
|
// Define some dates |
435
|
|
|
if (!empty($_facrec->frequency)) { |
436
|
|
|
$originaldatewhen = $_facrec->date_when; |
437
|
|
|
$nextdatewhen = dol_time_plus_duree($originaldatewhen, $_facrec->frequency, $_facrec->unit_frequency); |
438
|
|
|
$previousdaynextdatewhen = dol_time_plus_duree($nextdatewhen, -1, 'd'); |
439
|
|
|
$this->socid = $_facrec->socid; |
440
|
|
|
} else { |
441
|
|
|
$originaldatewhen = 0; |
442
|
|
|
$nextdatewhen = 0; |
443
|
|
|
$previousdaynextdatewhen = 0; |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
$this->entity = $_facrec->entity; // Invoice created in same entity than template |
447
|
|
|
|
448
|
|
|
// Fields coming from GUI |
449
|
|
|
// @TODO Value of template should be used as default value on the form on the GUI, and we should here always use the value from GUI |
450
|
|
|
// set by posted page with $object->xxx = ... and this section should be removed. |
451
|
|
|
$this->fk_project = GETPOSTINT('projectid') > 0 ? (GETPOSTINT('projectid')) : $_facrec->fk_project; |
452
|
|
|
$this->note_public = GETPOST('note_public', 'restricthtml') ? GETPOST('note_public', 'restricthtml') : $_facrec->note_public; |
|
|
|
|
453
|
|
|
$this->note_private = GETPOST('note_private', 'restricthtml') ? GETPOST('note_private', 'restricthtml') : $_facrec->note_private; |
|
|
|
|
454
|
|
|
$this->model_pdf = GETPOST('model', 'alpha') ? GETPOST('model', 'alpha') : $_facrec->model_pdf; |
|
|
|
|
455
|
|
|
$this->cond_reglement_id = GETPOSTINT('cond_reglement_id') > 0 ? (GETPOSTINT('cond_reglement_id')) : $_facrec->cond_reglement_id; |
456
|
|
|
$this->mode_reglement_id = GETPOSTINT('mode_reglement_id') > 0 ? (GETPOSTINT('mode_reglement_id')) : $_facrec->mode_reglement_id; |
457
|
|
|
$this->fk_account = GETPOST('fk_account') > 0 ? ((int) GETPOST('fk_account')) : $_facrec->fk_account; |
458
|
|
|
|
459
|
|
|
// Set here to have this defined for substitution into notes, should be recalculated after adding lines to get same result |
460
|
|
|
$this->total_ht = $_facrec->total_ht; |
461
|
|
|
$this->total_ttc = $_facrec->total_ttc; |
462
|
|
|
|
463
|
|
|
// Fields always coming from template |
464
|
|
|
$this->fk_incoterms = $_facrec->fk_incoterms; |
465
|
|
|
$this->location_incoterms = $_facrec->location_incoterms; |
466
|
|
|
|
467
|
|
|
// Clean parameters |
468
|
|
|
if (! $this->type) { |
469
|
|
|
$this->type = self::TYPE_STANDARD; |
470
|
|
|
} |
471
|
|
|
if (!empty(GETPOST('ref_supplier'))) { |
472
|
|
|
$this->ref_supplier = trim($this->ref_supplier); |
473
|
|
|
} else { |
474
|
|
|
$this->ref_supplier = trim($this->ref_supplier . '_' . ($_facrec->nb_gen_done + 1)); |
475
|
|
|
} |
476
|
|
|
$this->note_public = trim($this->note_public); |
477
|
|
|
$this->note_private = trim($this->note_private); |
478
|
|
|
$this->note_private = dol_concatdesc($this->note_private, $langs->trans("GeneratedFromRecurringInvoice", $_facrec->title)); |
479
|
|
|
|
480
|
|
|
$this->array_options = $_facrec->array_options; |
481
|
|
|
|
482
|
|
|
if (! $this->mode_reglement_id) { |
483
|
|
|
$this->mode_reglement_id = 0; |
484
|
|
|
} |
485
|
|
|
$this->status = self::STATUS_DRAFT; |
486
|
|
|
$this->statut = self::STATUS_DRAFT; // deprecated |
487
|
|
|
|
488
|
|
|
$this->linked_objects = $_facrec->linkedObjectsIds; |
489
|
|
|
// We do not add link to template invoice or next invoice will be linked to all generated invoices |
490
|
|
|
//$this->linked_objects['facturerec'][0] = $this->fac_rec; |
491
|
|
|
|
492
|
|
|
$forceduedate = $this->calculate_date_lim_reglement(); |
493
|
|
|
|
494
|
|
|
// For recurring invoices, update date and number of last generation of recurring template invoice, before inserting new invoice |
495
|
|
|
if ($_facrec->frequency > 0) { |
496
|
|
|
dol_syslog("This is a recurring invoice so we set date_last_gen and next date_when"); |
497
|
|
|
if (empty($_facrec->date_when)) { |
498
|
|
|
$_facrec->date_when = $now; |
499
|
|
|
} |
500
|
|
|
$next_date = $_facrec->getNextDate(); // Calculate next date |
501
|
|
|
$result = $_facrec->setValueFrom('date_last_gen', $now, '', 0, 'date', '', $user, ''); |
502
|
|
|
//$_facrec->setValueFrom('nb_gen_done', $_facrec->nb_gen_done + 1); // Not required, +1 already included into setNextDate when second param is 1. |
503
|
|
|
$result = $_facrec->setNextDate($next_date, 1); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
// Define lang of customer |
507
|
|
|
$outputlangs = $langs; |
508
|
|
|
$newlang = ''; |
509
|
|
|
|
510
|
|
|
if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->thirdparty->default_lang)) { |
511
|
|
|
$newlang = $this->thirdparty->default_lang; // for proposal, order, invoice, ... |
512
|
|
|
} |
513
|
|
|
if (getDolGlobalInt('MAIN_MULTILANGS') && empty($newlang) && isset($this->default_lang)) { |
|
|
|
|
514
|
|
|
$newlang = $this->default_lang; // for thirdparty |
515
|
|
|
} |
516
|
|
|
if (!empty($newlang)) { |
517
|
|
|
$outputlangs = new Translate("", $conf); |
518
|
|
|
$outputlangs->setDefaultLang($newlang); |
519
|
|
|
} // Array of possible substitutions (See also file mailing-send.php that should manage same substitutions) |
520
|
|
|
$substitutionarray = getCommonSubstitutionArray($outputlangs, 0, null, $this); |
521
|
|
|
$substitutionarray['__INVOICE_PREVIOUS_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%m'); |
522
|
|
|
$substitutionarray['__INVOICE_MONTH__'] = dol_print_date($this->date, '%m'); |
523
|
|
|
$substitutionarray['__INVOICE_NEXT_MONTH__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%m'); |
524
|
|
|
$substitutionarray['__INVOICE_PREVIOUS_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'm'), '%B'); |
525
|
|
|
$substitutionarray['__INVOICE_MONTH_TEXT__'] = dol_print_date($this->date, '%B'); |
526
|
|
|
$substitutionarray['__INVOICE_NEXT_MONTH_TEXT__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'm'), '%B'); |
527
|
|
|
$substitutionarray['__INVOICE_PREVIOUS_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, -1, 'y'), '%Y'); |
528
|
|
|
$substitutionarray['__INVOICE_YEAR__'] = dol_print_date($this->date, '%Y'); |
529
|
|
|
$substitutionarray['__INVOICE_NEXT_YEAR__'] = dol_print_date(dol_time_plus_duree($this->date, 1, 'y'), '%Y'); // Only for template invoice |
530
|
|
|
$substitutionarray['__INVOICE_DATE_NEXT_INVOICE_BEFORE_GEN__'] = $originaldatewhen ? dol_print_date($originaldatewhen, 'dayhour') : ''; |
531
|
|
|
$substitutionarray['__INVOICE_DATE_NEXT_INVOICE_AFTER_GEN__'] = $nextdatewhen ? dol_print_date($nextdatewhen, 'dayhour') : ''; |
532
|
|
|
$substitutionarray['__INVOICE_PREVIOUS_DATE_NEXT_INVOICE_AFTER_GEN__'] = $previousdaynextdatewhen ? dol_print_date($previousdaynextdatewhen, 'dayhour') : ''; |
533
|
|
|
$substitutionarray['__INVOICE_COUNTER_CURRENT__'] = $_facrec->nb_gen_done; |
534
|
|
|
$substitutionarray['__INVOICE_COUNTER_MAX__'] = $_facrec->nb_gen_max; |
535
|
|
|
|
536
|
|
|
complete_substitutions_array($substitutionarray, $outputlangs); |
537
|
|
|
|
538
|
|
|
$this->note_public = make_substitutions($this->note_public, $substitutionarray); |
539
|
|
|
$this->note_private = make_substitutions($this->note_private, $substitutionarray); |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
// Define due date if not already defined |
543
|
|
|
if (!empty($forceduedate)) { |
544
|
|
|
$this->date_echeance = $forceduedate; |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
$sql = "INSERT INTO " . MAIN_DB_PREFIX . "facture_fourn ("; |
548
|
|
|
$sql .= "ref"; |
549
|
|
|
$sql .= ", ref_supplier"; |
550
|
|
|
$sql .= ", ref_ext"; |
551
|
|
|
$sql .= ", entity"; |
552
|
|
|
$sql .= ", type"; |
553
|
|
|
$sql .= ", subtype"; |
554
|
|
|
$sql .= ", libelle"; |
555
|
|
|
$sql .= ", fk_soc"; |
556
|
|
|
$sql .= ", datec"; |
557
|
|
|
$sql .= ", datef"; |
558
|
|
|
$sql .= ", vat_reverse_charge"; |
559
|
|
|
$sql .= ", fk_projet"; |
560
|
|
|
$sql .= ", fk_cond_reglement"; |
561
|
|
|
$sql .= ", fk_mode_reglement"; |
562
|
|
|
$sql .= ", fk_account"; |
563
|
|
|
$sql .= ", note_private"; |
564
|
|
|
$sql .= ", note_public"; |
565
|
|
|
$sql .= ", fk_user_author"; |
566
|
|
|
$sql .= ", date_lim_reglement"; |
567
|
|
|
$sql .= ", fk_incoterms, location_incoterms"; |
568
|
|
|
$sql .= ", fk_multicurrency"; |
569
|
|
|
$sql .= ", multicurrency_code"; |
570
|
|
|
$sql .= ", multicurrency_tx"; |
571
|
|
|
$sql .= ", fk_facture_source"; |
572
|
|
|
$sql .= ", fk_fac_rec_source"; |
573
|
|
|
$sql .= ")"; |
574
|
|
|
$sql .= " VALUES ("; |
575
|
|
|
$sql .= "'(PROV)'"; |
576
|
|
|
$sql .= ", '" . $this->db->escape($this->ref_supplier) . "'"; |
577
|
|
|
$sql .= ", '" . $this->db->escape($this->ref_ext) . "'"; |
578
|
|
|
$sql .= ", " . ((int) $conf->entity); |
579
|
|
|
$sql .= ", '" . $this->db->escape($this->type) . "'"; |
580
|
|
|
$sql .= ", " . ((int) $this->subtype); |
581
|
|
|
$sql .= ", '" . $this->db->escape(isset($this->label) ? $this->label : (isset($this->libelle) ? $this->libelle : '')) . "'"; |
582
|
|
|
$sql .= ", " . ((int) $this->socid); |
583
|
|
|
$sql .= ", '" . $this->db->idate($now) . "'"; |
584
|
|
|
$sql .= ", '" . $this->db->idate($this->date) . "'"; |
585
|
|
|
$sql .= ", " . ($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0); |
586
|
|
|
$sql .= ", " . ($this->fk_project > 0 ? ((int) $this->fk_project) : "null"); |
587
|
|
|
$sql .= ", " . ($this->cond_reglement_id > 0 ? ((int) $this->cond_reglement_id) : "null"); |
588
|
|
|
$sql .= ", " . ($this->mode_reglement_id > 0 ? ((int) $this->mode_reglement_id) : "null"); |
589
|
|
|
$sql .= ", " . ($this->fk_account > 0 ? ((int) $this->fk_account) : 'NULL'); |
590
|
|
|
$sql .= ", '" . $this->db->escape($this->note_private) . "'"; |
591
|
|
|
$sql .= ", '" . $this->db->escape($this->note_public) . "'"; |
592
|
|
|
$sql .= ", " . ((int) $user->id) . ","; |
593
|
|
|
$sql .= $this->date_echeance != '' ? "'" . $this->db->idate($this->date_echeance) . "'" : "null"; |
594
|
|
|
$sql .= ", " . (int) $this->fk_incoterms; |
595
|
|
|
$sql .= ", '" . $this->db->escape($this->location_incoterms) . "'"; |
596
|
|
|
$sql .= ", " . (int) $this->fk_multicurrency; |
597
|
|
|
$sql .= ", '" . $this->db->escape($this->multicurrency_code) . "'"; |
598
|
|
|
$sql .= ", " . (float) $this->multicurrency_tx; |
599
|
|
|
$sql .= ", " . ($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null"); |
600
|
|
|
$sql .= ", " . (isset($this->fk_fac_rec_source) ? $this->fk_fac_rec_source : "NULL"); |
601
|
|
|
$sql .= ")"; |
602
|
|
|
|
603
|
|
|
dol_syslog(get_only_class($this) . "::create", LOG_DEBUG); |
604
|
|
|
$resql = $this->db->query($sql); |
605
|
|
|
if ($resql) { |
606
|
|
|
$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . 'facture_fourn'); |
607
|
|
|
|
608
|
|
|
// Update ref with new one |
609
|
|
|
$this->ref = '(PROV' . $this->id . ')'; |
610
|
|
|
$sql = 'UPDATE ' . MAIN_DB_PREFIX . "facture_fourn SET ref='" . $this->db->escape($this->ref) . "' WHERE rowid=" . ((int) $this->id); |
611
|
|
|
|
612
|
|
|
dol_syslog(get_only_class($this) . "::create", LOG_DEBUG); |
613
|
|
|
$resql = $this->db->query($sql); |
614
|
|
|
if (!$resql) { |
615
|
|
|
$error++; |
616
|
|
|
} |
617
|
|
|
|
618
|
|
|
if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects |
619
|
|
|
$this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds |
620
|
|
|
} |
621
|
|
|
|
622
|
|
|
// Add object linked |
623
|
|
|
if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) { |
624
|
|
|
foreach ($this->linked_objects as $origin => $tmp_origin_id) { |
625
|
|
|
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, ...)) |
626
|
|
|
foreach ($tmp_origin_id as $origin_id) { |
627
|
|
|
$ret = $this->add_object_linked($origin, $origin_id); |
628
|
|
|
if (!$ret) { |
629
|
|
|
dol_print_error($this->db); |
630
|
|
|
$error++; |
631
|
|
|
} |
632
|
|
|
} |
633
|
|
|
} else { // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1)) |
634
|
|
|
$origin_id = $tmp_origin_id; |
635
|
|
|
$ret = $this->add_object_linked($origin, $origin_id); |
636
|
|
|
if (!$ret) { |
637
|
|
|
dol_print_error($this->db); |
638
|
|
|
$error++; |
639
|
|
|
} |
640
|
|
|
} |
641
|
|
|
} |
642
|
|
|
} |
643
|
|
|
|
644
|
|
|
if (!$error && empty($this->fac_rec) && count($this->lines) && is_object($this->lines[0])) { // If this->lines is array of InvoiceLines (preferred mode) |
645
|
|
|
dol_syslog("There is " . count($this->lines) . " lines that are invoice lines objects"); |
646
|
|
|
foreach ($this->lines as $i => $val) { |
647
|
|
|
$sql = 'INSERT INTO ' . MAIN_DB_PREFIX . 'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)'; |
648
|
|
|
$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') . ')'; |
|
|
|
|
649
|
|
|
|
650
|
|
|
$resql_insert = $this->db->query($sql); |
651
|
|
|
if ($resql_insert) { |
652
|
|
|
$idligne = $this->db->last_insert_id(MAIN_DB_PREFIX . 'facture_fourn_det'); |
653
|
|
|
|
654
|
|
|
$res = $this->updateline( |
655
|
|
|
$idligne, |
656
|
|
|
$this->lines[$i]->desc ? $this->lines[$i]->desc : $this->lines[$i]->description, |
657
|
|
|
$this->lines[$i]->subprice, |
658
|
|
|
$this->lines[$i]->tva_tx . ($this->lines[$i]->vat_src_code ? ' (' . $this->lines[$i]->vat_src_code . ')' : ''), |
659
|
|
|
$this->lines[$i]->localtax1_tx, |
660
|
|
|
$this->lines[$i]->localtax2_tx, |
661
|
|
|
$this->lines[$i]->qty, |
662
|
|
|
$this->lines[$i]->fk_product, |
663
|
|
|
'HT', |
664
|
|
|
(!empty($this->lines[$i]->info_bits) ? $this->lines[$i]->info_bits : ''), |
665
|
|
|
$this->lines[$i]->product_type, |
666
|
|
|
$this->lines[$i]->remise_percent, |
667
|
|
|
false, |
668
|
|
|
$this->lines[$i]->date_start, |
|
|
|
|
669
|
|
|
$this->lines[$i]->date_end, |
|
|
|
|
670
|
|
|
$this->lines[$i]->array_options, |
671
|
|
|
$this->lines[$i]->fk_unit, |
672
|
|
|
$this->lines[$i]->multicurrency_subprice, |
673
|
|
|
$this->lines[$i]->ref_supplier |
|
|
|
|
674
|
|
|
); |
675
|
|
|
} else { |
676
|
|
|
$this->error = $this->db->lasterror(); |
677
|
|
|
$this->db->rollback(); |
678
|
|
|
return -5; |
679
|
|
|
} |
680
|
|
|
} |
681
|
|
|
} elseif (!$error && empty($this->fac_rec)) { // If this->lines is an array of invoice line arrays |
682
|
|
|
dol_syslog("There is " . count($this->lines) . " lines that are array lines"); |
683
|
|
|
foreach ($this->lines as $i => $val) { |
684
|
|
|
$line = $this->lines[$i]; |
685
|
|
|
|
686
|
|
|
// Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array |
687
|
|
|
//if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object. |
688
|
|
|
if (!is_object($line)) { |
689
|
|
|
$line = (object) $line; |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
$sql = 'INSERT INTO ' . MAIN_DB_PREFIX . 'facture_fourn_det (fk_facture_fourn, special_code, fk_remise_except)'; |
693
|
|
|
$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') . ')'; |
694
|
|
|
|
695
|
|
|
$resql_insert = $this->db->query($sql); |
696
|
|
|
if ($resql_insert) { |
697
|
|
|
$idligne = $this->db->last_insert_id(MAIN_DB_PREFIX . 'facture_fourn_det'); |
698
|
|
|
|
699
|
|
|
$this->updateline( |
700
|
|
|
$idligne, |
701
|
|
|
$line->desc ? $line->desc : $line->description, |
702
|
|
|
$line->subprice, |
703
|
|
|
$line->tva_tx, |
704
|
|
|
$line->localtax1_tx, |
705
|
|
|
$line->localtax2_tx, |
706
|
|
|
$line->qty, |
707
|
|
|
$line->fk_product, |
708
|
|
|
'HT', |
709
|
|
|
(!empty($line->info_bits) ? $line->info_bits : ''), |
710
|
|
|
$line->product_type, |
711
|
|
|
$line->remise_percent, |
712
|
|
|
0, |
713
|
|
|
$line->date_start, |
714
|
|
|
$line->date_end, |
715
|
|
|
$line->array_options, |
716
|
|
|
$line->fk_unit, |
717
|
|
|
$line->multicurrency_subprice, |
718
|
|
|
$line->ref_supplier |
719
|
|
|
); |
720
|
|
|
} else { |
721
|
|
|
$this->error = $this->db->lasterror(); |
722
|
|
|
$this->db->rollback(); |
723
|
|
|
return -5; |
724
|
|
|
} |
725
|
|
|
} |
726
|
|
|
} |
727
|
|
|
|
728
|
|
|
/* |
729
|
|
|
* Insert lines of template invoices |
730
|
|
|
*/ |
731
|
|
|
if (! $error && $this->fac_rec > 0 && $_facrec instanceof FactureFournisseurRec) { |
732
|
|
|
foreach ($_facrec->lines as $i => $val) { |
733
|
|
|
if ($_facrec->lines[$i]->fk_product) { |
734
|
|
|
$prod = new Product($this->db); |
735
|
|
|
$res = $prod->fetch($_facrec->lines[$i]->fk_product); |
736
|
|
|
} |
737
|
|
|
|
738
|
|
|
// For line from template invoice, we use data from template invoice |
739
|
|
|
/* |
740
|
|
|
$tva_tx = get_default_tva($mysoc,$soc,$prod->id); |
741
|
|
|
$tva_npr = get_default_npr($mysoc,$soc,$prod->id); |
742
|
|
|
if (empty($tva_tx)) $tva_npr=0; |
743
|
|
|
$localtax1_tx=get_localtax($tva_tx,1,$soc,$mysoc,$tva_npr); |
744
|
|
|
$localtax2_tx=get_localtax($tva_tx,2,$soc,$mysoc,$tva_npr); |
745
|
|
|
*/ |
746
|
|
|
$tva_tx = $_facrec->lines[$i]->tva_tx . ($_facrec->lines[$i]->vat_src_code ? '(' . $_facrec->lines[$i]->vat_src_code . ')' : ''); |
747
|
|
|
$tva_npr = $_facrec->lines[$i]->info_bits; |
748
|
|
|
if (empty($tva_tx)) { |
749
|
|
|
$tva_npr = 0; |
750
|
|
|
} |
751
|
|
|
$localtax1_tx = $_facrec->lines[$i]->localtax1_tx; |
752
|
|
|
$localtax2_tx = $_facrec->lines[$i]->localtax2_tx; |
753
|
|
|
|
754
|
|
|
$fk_product_fournisseur_price = empty($_facrec->lines[$i]->fk_product_fournisseur_price) ? null : $_facrec->lines[$i]->fk_product_fournisseur_price; |
755
|
|
|
$buyprice = empty($_facrec->lines[$i]->buyprice) ? 0 : $_facrec->lines[$i]->buyprice; |
756
|
|
|
|
757
|
|
|
// If buyprice not defined from template invoice, we try to guess the best value |
758
|
|
|
if (! $buyprice && $_facrec->lines[$i]->fk_product > 0) { |
759
|
|
|
$producttmp = new ProductFournisseur($this->db); |
760
|
|
|
$producttmp->fetch($_facrec->lines[$i]->fk_product); |
761
|
|
|
|
762
|
|
|
// If margin module defined on costprice, we try the costprice |
763
|
|
|
// If not defined or if module margin defined and pmp and stock module enabled, we try pmp price |
764
|
|
|
// else we get the best supplier price |
765
|
|
|
if (getDolGlobalString('MARGIN_TYPE') == 'costprice' && !empty($producttmp->cost_price)) { |
766
|
|
|
$buyprice = $producttmp->cost_price; |
767
|
|
|
} elseif (isModEnabled('stock') && (getDolGlobalString('MARGIN_TYPE') == 'costprice' || getDolGlobalString('MARGIN_TYPE') == 'pmp') && !empty($producttmp->pmp)) { |
768
|
|
|
$buyprice = $producttmp->pmp; |
769
|
|
|
} else { |
770
|
|
|
if ($producttmp->find_min_price_product_fournisseur($_facrec->lines[$i]->fk_product) > 0) { |
771
|
|
|
if ($producttmp->product_fourn_price_id > 0) { |
772
|
|
|
$buyprice = price2num($producttmp->fourn_unitprice * (1 - $producttmp->fourn_remise_percent / 100) + $producttmp->fourn_remise, 'MU'); |
773
|
|
|
} |
774
|
|
|
} |
775
|
|
|
} |
776
|
|
|
} |
777
|
|
|
|
778
|
|
|
$result_insert = $this->addline( |
779
|
|
|
$_facrec->lines[$i]->desc ? $_facrec->lines[$i]->desc : $_facrec->lines[$i]->description, |
780
|
|
|
$_facrec->lines[$i]->pu_ht, |
781
|
|
|
$tva_tx, |
782
|
|
|
$localtax1_tx, |
783
|
|
|
$localtax2_tx, |
784
|
|
|
$_facrec->lines[$i]->qty, |
785
|
|
|
$_facrec->lines[$i]->fk_product, |
786
|
|
|
$_facrec->lines[$i]->remise_percent, |
787
|
|
|
($_facrec->lines[$i]->date_start == 1 && $this->date) ? $this->date : '', |
788
|
|
|
($_facrec->lines[$i]->date_end == 1 && $previousdaynextdatewhen) ? $previousdaynextdatewhen : '', |
789
|
|
|
0, |
790
|
|
|
$_facrec->lines[$i]->info_bits, |
791
|
|
|
'HT', |
792
|
|
|
0, |
793
|
|
|
$_facrec->lines[$i]->rang, |
794
|
|
|
false, |
795
|
|
|
$_facrec->lines[$i]->array_options, |
796
|
|
|
$_facrec->lines[$i]->fk_unit, |
797
|
|
|
0, |
798
|
|
|
0, |
799
|
|
|
$_facrec->lines[$i]->ref_supplier, |
800
|
|
|
$_facrec->lines[$i]->special_code, |
801
|
|
|
0, |
802
|
|
|
0 |
803
|
|
|
); |
804
|
|
|
if ($result_insert < 0) { |
805
|
|
|
$error++; |
806
|
|
|
$this->error = $this->db->error(); |
807
|
|
|
break; |
808
|
|
|
} |
809
|
|
|
} |
810
|
|
|
} |
811
|
|
|
|
812
|
|
|
|
813
|
|
|
// Update total price |
814
|
|
|
$result = $this->update_price(1); |
815
|
|
|
if ($result > 0) { |
816
|
|
|
// Actions on extra fields |
817
|
|
|
if (!$error) { |
818
|
|
|
$result = $this->insertExtraFields(); // This also set $this->error or $this->errors if errors are found |
819
|
|
|
if ($result < 0) { |
820
|
|
|
$error++; |
821
|
|
|
} |
822
|
|
|
} |
823
|
|
|
|
824
|
|
|
if (!$error) { |
825
|
|
|
// Call trigger |
826
|
|
|
$result = $this->call_trigger('BILL_SUPPLIER_CREATE', $user); |
827
|
|
|
if ($result < 0) { |
828
|
|
|
$error++; |
829
|
|
|
} |
830
|
|
|
// End call triggers |
831
|
|
|
} |
832
|
|
|
|
833
|
|
|
if (!$error) { |
834
|
|
|
$this->db->commit(); |
835
|
|
|
return $this->id; |
836
|
|
|
} else { |
837
|
|
|
$this->db->rollback(); |
838
|
|
|
return -4; |
839
|
|
|
} |
840
|
|
|
} else { |
841
|
|
|
$this->error = $langs->trans('FailedToUpdatePrice'); |
842
|
|
|
$this->db->rollback(); |
843
|
|
|
return -3; |
844
|
|
|
} |
845
|
|
|
} else { |
846
|
|
|
if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') { |
847
|
|
|
$this->error = $langs->trans('ErrorRefAlreadyExists'); |
848
|
|
|
$this->db->rollback(); |
849
|
|
|
return -1; |
850
|
|
|
} else { |
851
|
|
|
$this->error = $this->db->lasterror(); |
852
|
|
|
$this->db->rollback(); |
853
|
|
|
return -2; |
854
|
|
|
} |
855
|
|
|
} |
856
|
|
|
} |
857
|
|
|
|
858
|
|
|
/** |
859
|
|
|
* Load object in memory from database |
860
|
|
|
* |
861
|
|
|
* @param int $id Id supplier invoice |
862
|
|
|
* @param string $ref Ref supplier invoice |
863
|
|
|
* @param string $ref_ext External reference of invoice |
864
|
|
|
* @return int Return integer <0 if KO, >0 if OK, 0 if not found |
865
|
|
|
*/ |
866
|
|
|
public function fetch($id = 0, $ref = '', $ref_ext = '') |
867
|
|
|
{ |
868
|
|
|
if (empty($id) && empty($ref) && empty($ref_ext)) { |
869
|
|
|
return -1; |
870
|
|
|
} |
871
|
|
|
|
872
|
|
|
$sql = "SELECT"; |
873
|
|
|
$sql .= " t.rowid,"; |
874
|
|
|
$sql .= " t.ref,"; |
875
|
|
|
$sql .= " t.ref_supplier,"; |
876
|
|
|
$sql .= " t.ref_ext,"; |
877
|
|
|
$sql .= " t.entity,"; |
878
|
|
|
$sql .= " t.type,"; |
879
|
|
|
$sql .= " t.subtype,"; |
880
|
|
|
$sql .= " t.fk_soc,"; |
881
|
|
|
$sql .= " t.datec,"; |
882
|
|
|
$sql .= " t.datef,"; |
883
|
|
|
$sql .= " t.tms,"; |
884
|
|
|
$sql .= " t.libelle as label,"; |
885
|
|
|
$sql .= " t.paye,"; |
886
|
|
|
$sql .= " t.close_code,"; |
887
|
|
|
$sql .= " t.close_note,"; |
888
|
|
|
$sql .= " t.tva,"; |
889
|
|
|
$sql .= " t.localtax1,"; |
890
|
|
|
$sql .= " t.localtax2,"; |
891
|
|
|
$sql .= " t.total_ht,"; |
892
|
|
|
$sql .= " t.total_tva,"; |
893
|
|
|
$sql .= " t.total_ttc,"; |
894
|
|
|
$sql .= " t.fk_statut as status,"; |
895
|
|
|
$sql .= " t.fk_user_author,"; |
896
|
|
|
$sql .= " t.fk_user_valid,"; |
897
|
|
|
$sql .= " t.fk_facture_source,"; |
898
|
|
|
$sql .= " t.vat_reverse_charge,"; |
899
|
|
|
$sql .= " t.fk_fac_rec_source,"; |
900
|
|
|
$sql .= " t.fk_projet as fk_project,"; |
901
|
|
|
$sql .= " t.fk_cond_reglement,"; |
902
|
|
|
$sql .= " t.fk_account,"; |
903
|
|
|
$sql .= " t.fk_mode_reglement,"; |
904
|
|
|
$sql .= " t.date_lim_reglement,"; |
905
|
|
|
$sql .= " t.note_private,"; |
906
|
|
|
$sql .= " t.note_public,"; |
907
|
|
|
$sql .= " t.model_pdf,"; |
908
|
|
|
$sql .= " t.last_main_doc,"; |
909
|
|
|
$sql .= " t.import_key,"; |
910
|
|
|
$sql .= " t.extraparams,"; |
911
|
|
|
$sql .= " cr.code as cond_reglement_code, cr.libelle as cond_reglement_label, cr.libelle_facture as cond_reglement_doc,"; |
912
|
|
|
$sql .= " p.code as mode_reglement_code, p.libelle as mode_reglement_label,"; |
913
|
|
|
$sql .= ' s.nom as socnom, s.rowid as socid,'; |
914
|
|
|
$sql .= ' t.fk_incoterms, t.location_incoterms,'; |
915
|
|
|
$sql .= " i.libelle as label_incoterms,"; |
916
|
|
|
$sql .= ' t.fk_transport_mode,'; |
917
|
|
|
$sql .= ' t.fk_multicurrency, t.multicurrency_code, t.multicurrency_tx, t.multicurrency_total_ht, t.multicurrency_total_tva, t.multicurrency_total_ttc'; |
918
|
|
|
$sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_fourn as t'; |
919
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON (t.fk_soc = s.rowid)"; |
920
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_payment_term as cr ON t.fk_cond_reglement = cr.rowid"; |
921
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_paiement as p ON t.fk_mode_reglement = p.id"; |
922
|
|
|
$sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_incoterms as i ON t.fk_incoterms = i.rowid'; |
923
|
|
|
if ($id) { |
924
|
|
|
$sql .= " WHERE t.rowid = " . ((int) $id); |
925
|
|
|
} else { |
926
|
|
|
$sql .= ' WHERE t.entity IN (' . getEntity('supplier_invoice') . ')'; // Don't use entity if you use rowid |
927
|
|
|
if ($ref) { |
928
|
|
|
$sql .= " AND t.ref = '" . $this->db->escape($ref) . "'"; |
929
|
|
|
} |
930
|
|
|
if ($ref_ext) { |
931
|
|
|
$sql .= " AND t.ref_ext = '" . $this->db->escape($ref_ext) . "'"; |
932
|
|
|
} |
933
|
|
|
} |
934
|
|
|
|
935
|
|
|
dol_syslog(get_only_class($this) . "::fetch", LOG_DEBUG); |
936
|
|
|
$resql = $this->db->query($sql); |
937
|
|
|
if ($resql) { |
938
|
|
|
if ($this->db->num_rows($resql)) { |
939
|
|
|
$obj = $this->db->fetch_object($resql); |
940
|
|
|
|
941
|
|
|
$this->id = $obj->rowid; |
942
|
|
|
$this->ref = $obj->ref ? $obj->ref : $obj->rowid; // We take rowid if ref is empty for backward compatibility |
943
|
|
|
|
944
|
|
|
$this->ref_supplier = $obj->ref_supplier; |
945
|
|
|
$this->ref_ext = $obj->ref_ext; |
946
|
|
|
$this->entity = $obj->entity; |
947
|
|
|
$this->type = empty($obj->type) ? self::TYPE_STANDARD : $obj->type; |
948
|
|
|
$this->subtype = (int) $obj->subtype; |
949
|
|
|
$this->socid = $obj->fk_soc; |
950
|
|
|
$this->datec = $this->db->jdate($obj->datec); |
|
|
|
|
951
|
|
|
$this->date = $this->db->jdate($obj->datef); |
|
|
|
|
952
|
|
|
//$this->datep = $this->db->jdate($obj->datef); |
953
|
|
|
$this->tms = $this->db->jdate($obj->tms); |
|
|
|
|
954
|
|
|
$this->libelle = $obj->label; // deprecated |
|
|
|
|
955
|
|
|
$this->label = $obj->label; |
956
|
|
|
$this->paye = $obj->paye; |
|
|
|
|
957
|
|
|
$this->paid = $obj->paye; |
958
|
|
|
$this->close_code = $obj->close_code; |
959
|
|
|
$this->close_note = $obj->close_note; |
960
|
|
|
$this->total_localtax1 = $obj->localtax1; |
961
|
|
|
$this->total_localtax2 = $obj->localtax2; |
962
|
|
|
$this->total_ht = $obj->total_ht; |
963
|
|
|
$this->total_tva = $obj->total_tva; |
964
|
|
|
$this->total_ttc = $obj->total_ttc; |
965
|
|
|
$this->status = $obj->status; |
966
|
|
|
$this->statut = $obj->status; // For backward compatibility |
967
|
|
|
$this->fk_statut = $obj->status; // For backward compatibility |
968
|
|
|
$this->user_creation_id = $obj->fk_user_author; |
969
|
|
|
$this->author = $obj->fk_user_author; // deprecated |
|
|
|
|
970
|
|
|
$this->user_validation_id = $obj->fk_user_valid; |
971
|
|
|
$this->fk_facture_source = $obj->fk_facture_source; |
972
|
|
|
$this->vat_reverse_charge = empty($obj->vat_reverse_charge) ? 0 : 1; |
973
|
|
|
$this->fk_fac_rec_source = $obj->fk_fac_rec_source; |
974
|
|
|
$this->fk_project = $obj->fk_project; |
975
|
|
|
$this->cond_reglement_id = $obj->fk_cond_reglement; |
976
|
|
|
$this->cond_reglement_code = $obj->cond_reglement_code; |
977
|
|
|
$this->cond_reglement = $obj->cond_reglement_label; // deprecated |
|
|
|
|
978
|
|
|
$this->cond_reglement_label = $obj->cond_reglement_label; |
979
|
|
|
$this->cond_reglement_doc = $obj->cond_reglement_doc; |
980
|
|
|
$this->fk_account = $obj->fk_account; |
981
|
|
|
$this->mode_reglement_id = $obj->fk_mode_reglement; |
982
|
|
|
$this->mode_reglement_code = $obj->mode_reglement_code; |
983
|
|
|
$this->mode_reglement = $obj->mode_reglement_label; |
984
|
|
|
$this->date_echeance = $this->db->jdate($obj->date_lim_reglement); |
|
|
|
|
985
|
|
|
$this->note = $obj->note_private; // deprecated |
986
|
|
|
$this->note_private = $obj->note_private; |
987
|
|
|
$this->note_public = $obj->note_public; |
988
|
|
|
$this->model_pdf = $obj->model_pdf; |
989
|
|
|
$this->last_main_doc = $obj->last_main_doc; |
990
|
|
|
$this->import_key = $obj->import_key; |
991
|
|
|
|
992
|
|
|
//Incoterms |
993
|
|
|
$this->fk_incoterms = $obj->fk_incoterms; |
994
|
|
|
$this->location_incoterms = $obj->location_incoterms; |
995
|
|
|
$this->label_incoterms = $obj->label_incoterms; |
996
|
|
|
$this->transport_mode_id = $obj->fk_transport_mode; |
997
|
|
|
|
998
|
|
|
// Multicurrency |
999
|
|
|
$this->fk_multicurrency = $obj->fk_multicurrency; |
1000
|
|
|
$this->multicurrency_code = $obj->multicurrency_code; |
1001
|
|
|
$this->multicurrency_tx = $obj->multicurrency_tx; |
1002
|
|
|
$this->multicurrency_total_ht = $obj->multicurrency_total_ht; |
1003
|
|
|
$this->multicurrency_total_tva = $obj->multicurrency_total_tva; |
1004
|
|
|
$this->multicurrency_total_ttc = $obj->multicurrency_total_ttc; |
1005
|
|
|
|
1006
|
|
|
$this->extraparams = isset($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array(); |
1007
|
|
|
|
1008
|
|
|
$this->socid = $obj->socid; |
1009
|
|
|
|
1010
|
|
|
// Retrieve all extrafield |
1011
|
|
|
// fetch optionals attributes and labels |
1012
|
|
|
$this->fetch_optionals(); |
1013
|
|
|
|
1014
|
|
|
$result = $this->fetch_lines(); |
1015
|
|
|
if ($result < 0) { |
1016
|
|
|
$this->error = $this->db->lasterror(); |
1017
|
|
|
return -3; |
1018
|
|
|
} |
1019
|
|
|
} else { |
1020
|
|
|
$this->error = 'Bill with id ' . $id . ' not found'; |
1021
|
|
|
dol_syslog(get_only_class($this) . '::fetch ' . $this->error); |
1022
|
|
|
return 0; |
1023
|
|
|
} |
1024
|
|
|
|
1025
|
|
|
$this->db->free($resql); |
1026
|
|
|
return 1; |
1027
|
|
|
} else { |
1028
|
|
|
$this->error = "Error " . $this->db->lasterror(); |
1029
|
|
|
return -1; |
1030
|
|
|
} |
1031
|
|
|
} |
1032
|
|
|
|
1033
|
|
|
|
1034
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
1035
|
|
|
/** |
1036
|
|
|
* Load this->lines |
1037
|
|
|
* |
1038
|
|
|
* @return int 1 si ok, < 0 si erreur |
1039
|
|
|
*/ |
1040
|
|
|
public function fetch_lines() |
1041
|
|
|
{ |
1042
|
|
|
// phpcs:enable |
1043
|
|
|
$this->lines = array(); |
1044
|
|
|
|
1045
|
|
|
$sql = 'SELECT f.rowid, f.ref as ref_supplier, f.description as line_desc, f.date_start, f.date_end, f.pu_ht, f.pu_ttc, f.qty, f.remise_percent, f.vat_src_code, f.tva_tx'; |
1046
|
|
|
$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'; |
1047
|
|
|
$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'; |
1048
|
|
|
$sql .= ', p.rowid as product_id, p.ref as product_ref, p.label as label, p.barcode as product_barcode, p.description as product_desc'; |
1049
|
|
|
$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'; |
1050
|
|
|
$sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_fourn_det as f'; |
1051
|
|
|
$sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'product as p ON f.fk_product = p.rowid'; |
1052
|
|
|
$sql .= ' WHERE fk_facture_fourn=' . ((int) $this->id); |
1053
|
|
|
$sql .= ' ORDER BY f.rang, f.rowid'; |
1054
|
|
|
|
1055
|
|
|
dol_syslog(get_only_class($this) . "::fetch_lines", LOG_DEBUG); |
1056
|
|
|
|
1057
|
|
|
$resql_rows = $this->db->query($sql); |
1058
|
|
|
if ($resql_rows) { |
1059
|
|
|
$num_rows = $this->db->num_rows($resql_rows); |
1060
|
|
|
if ($num_rows) { |
1061
|
|
|
$i = 0; |
1062
|
|
|
while ($i < $num_rows) { |
1063
|
|
|
$obj = $this->db->fetch_object($resql_rows); |
1064
|
|
|
|
1065
|
|
|
$line = new SupplierInvoiceLine($this->db); |
1066
|
|
|
|
1067
|
|
|
$line->id = $obj->rowid; |
1068
|
|
|
$line->rowid = $obj->rowid; |
|
|
|
|
1069
|
|
|
$line->description = $obj->line_desc; |
|
|
|
|
1070
|
|
|
$line->desc = $obj->line_desc; |
1071
|
|
|
$line->date_start = $obj->date_start; |
1072
|
|
|
$line->date_end = $obj->date_end; |
1073
|
|
|
$line->product_ref = $obj->product_ref; |
1074
|
|
|
$line->ref = $obj->product_ref; |
1075
|
|
|
$line->ref_supplier = $obj->ref_supplier; |
1076
|
|
|
$line->libelle = $obj->label; |
|
|
|
|
1077
|
|
|
$line->label = $obj->label; |
|
|
|
|
1078
|
|
|
$line->product_barcode = $obj->product_barcode; |
1079
|
|
|
$line->product_desc = $obj->product_desc; |
1080
|
|
|
$line->subprice = $obj->pu_ht; |
1081
|
|
|
$line->pu_ht = $obj->pu_ht; |
|
|
|
|
1082
|
|
|
$line->pu_ttc = $obj->pu_ttc; |
1083
|
|
|
$line->vat_src_code = $obj->vat_src_code; |
1084
|
|
|
$line->tva_tx = $obj->tva_tx; |
1085
|
|
|
$line->localtax1_tx = $obj->localtax1_tx; |
1086
|
|
|
$line->localtax2_tx = $obj->localtax2_tx; |
1087
|
|
|
$line->localtax1_type = $obj->localtax1_type; |
1088
|
|
|
$line->localtax2_type = $obj->localtax2_type; |
1089
|
|
|
$line->qty = $obj->qty; |
1090
|
|
|
$line->remise_percent = $obj->remise_percent; |
1091
|
|
|
$line->fk_remise_except = $obj->fk_remise_except; |
1092
|
|
|
//$line->tva = $obj->total_tva; // deprecated |
1093
|
|
|
$line->total_ht = $obj->total_ht; |
1094
|
|
|
$line->total_ttc = $obj->total_ttc; |
1095
|
|
|
$line->total_tva = $obj->total_tva; |
1096
|
|
|
$line->total_localtax1 = $obj->total_localtax1; |
1097
|
|
|
$line->total_localtax2 = $obj->total_localtax2; |
1098
|
|
|
$line->fk_facture_fourn = $obj->fk_facture_fourn; |
1099
|
|
|
$line->fk_product = $obj->fk_product; |
1100
|
|
|
$line->product_type = $obj->product_type; |
1101
|
|
|
$line->product_label = $obj->label; |
1102
|
|
|
$line->info_bits = $obj->info_bits; |
1103
|
|
|
$line->fk_parent_line = $obj->fk_parent_line; |
1104
|
|
|
$line->special_code = $obj->special_code; |
1105
|
|
|
$line->rang = $obj->rang; |
1106
|
|
|
$line->fk_unit = $obj->fk_unit; |
1107
|
|
|
|
1108
|
|
|
// Accountancy |
1109
|
|
|
$line->fk_accounting_account = $obj->fk_code_ventilation; |
|
|
|
|
1110
|
|
|
|
1111
|
|
|
// Multicurrency |
1112
|
|
|
$line->fk_multicurrency = $obj->fk_multicurrency; |
1113
|
|
|
$line->multicurrency_code = $obj->multicurrency_code; |
1114
|
|
|
$line->multicurrency_subprice = $obj->multicurrency_subprice; |
1115
|
|
|
$line->multicurrency_total_ht = $obj->multicurrency_total_ht; |
1116
|
|
|
$line->multicurrency_total_tva = $obj->multicurrency_total_tva; |
1117
|
|
|
$line->multicurrency_total_ttc = $obj->multicurrency_total_ttc; |
1118
|
|
|
|
1119
|
|
|
// Extra fields |
1120
|
|
|
$line->fetch_optionals(); |
1121
|
|
|
|
1122
|
|
|
$this->lines[$i] = $line; |
1123
|
|
|
|
1124
|
|
|
$i++; |
1125
|
|
|
} |
1126
|
|
|
} |
1127
|
|
|
$this->db->free($resql_rows); |
1128
|
|
|
return 1; |
1129
|
|
|
} else { |
1130
|
|
|
$this->error = $this->db->error(); |
1131
|
|
|
dol_syslog(get_only_class($this) . "::fetch_lines - No lines:{$this->error} Error:{$this->error}", LOG_DEBUG); |
1132
|
|
|
return -3; |
1133
|
|
|
} |
1134
|
|
|
} |
1135
|
|
|
|
1136
|
|
|
|
1137
|
|
|
/** |
1138
|
|
|
* Update database |
1139
|
|
|
* |
1140
|
|
|
* @param User $user User that modify |
1141
|
|
|
* @param int $notrigger 0=launch triggers after, 1=disable triggers |
1142
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
1143
|
|
|
*/ |
1144
|
|
|
public function update($user = null, $notrigger = 0) |
1145
|
|
|
{ |
1146
|
|
|
global $langs; |
1147
|
|
|
$error = 0; |
1148
|
|
|
|
1149
|
|
|
// Clean parameters |
1150
|
|
|
if (empty($this->type)) { |
1151
|
|
|
$this->type = self::TYPE_STANDARD; |
1152
|
|
|
} |
1153
|
|
|
if (isset($this->ref)) { |
1154
|
|
|
$this->ref = trim($this->ref); |
1155
|
|
|
} |
1156
|
|
|
if (isset($this->ref_supplier)) { |
1157
|
|
|
$this->ref_supplier = trim($this->ref_supplier); |
1158
|
|
|
} |
1159
|
|
|
if (isset($this->ref_ext)) { |
1160
|
|
|
$this->ref_ext = trim($this->ref_ext); |
1161
|
|
|
} |
1162
|
|
|
if (isset($this->entity)) { |
1163
|
|
|
$this->entity = (int) $this->entity; |
1164
|
|
|
} |
1165
|
|
|
if (isset($this->type)) { |
1166
|
|
|
$this->type = (int) $this->type; |
1167
|
|
|
} |
1168
|
|
|
if (isset($this->subtype)) { |
1169
|
|
|
$this->subtype = (int) $this->subtype; |
1170
|
|
|
} |
1171
|
|
|
if (isset($this->socid)) { |
1172
|
|
|
$this->socid = (int) $this->socid; |
1173
|
|
|
} |
1174
|
|
|
if (isset($this->label)) { |
1175
|
|
|
$this->label = trim($this->label); |
1176
|
|
|
} |
1177
|
|
|
if (isset($this->paid)) { |
1178
|
|
|
$this->paid = (int) (bool) $this->paye; |
1179
|
|
|
$this->paye = $this->paid; |
1180
|
|
|
} elseif (isset($this->paye)) { |
1181
|
|
|
$this->paid = (int) (bool) $this->paye; |
1182
|
|
|
$this->paye = $this->paid; |
1183
|
|
|
} |
1184
|
|
|
if (isset($this->close_code)) { |
1185
|
|
|
$this->close_code = trim($this->close_code); |
1186
|
|
|
} |
1187
|
|
|
if (isset($this->close_note)) { |
1188
|
|
|
$this->close_note = trim($this->close_note); |
1189
|
|
|
} |
1190
|
|
|
if (empty($this->total_ht)) { |
1191
|
|
|
$this->total_ht = 0; |
1192
|
|
|
} |
1193
|
|
|
if (empty($this->total_tva)) { |
1194
|
|
|
$this->total_tva = 0; |
1195
|
|
|
} |
1196
|
|
|
if (isset($this->total_ttc)) { |
1197
|
|
|
$this->total_ttc = (float) $this->total_ttc; |
1198
|
|
|
} |
1199
|
|
|
if (isset($this->status)) { |
1200
|
|
|
$this->status = (int) $this->status; |
1201
|
|
|
$this->statut = $this->status; |
1202
|
|
|
} elseif (isset($this->statut)) { |
1203
|
|
|
$this->status = (int) $this->statut; |
1204
|
|
|
$this->statut = $this->status; |
1205
|
|
|
} |
1206
|
|
|
if (isset($this->author)) { // TODO: user_creation_id? |
1207
|
|
|
$this->author = (int) $this->author; |
1208
|
|
|
} |
1209
|
|
|
if (isset($this->fk_user_valid)) { |
1210
|
|
|
$this->fk_user_valid = trim($this->fk_user_valid); |
1211
|
|
|
} |
1212
|
|
|
if (isset($this->fk_facture_source)) { |
1213
|
|
|
$this->fk_facture_source = (int) $this->fk_facture_source; |
1214
|
|
|
} |
1215
|
|
|
if (isset($this->fk_project)) { |
1216
|
|
|
if (empty($this->fk_project)) { |
1217
|
|
|
$this->fk_project = 0; |
1218
|
|
|
} else { |
1219
|
|
|
$this->fk_project = (int) $this->fk_project; |
1220
|
|
|
} |
1221
|
|
|
} |
1222
|
|
|
if (isset($this->cond_reglement_id)) { |
1223
|
|
|
$this->cond_reglement_id = (int) $this->cond_reglement_id; |
1224
|
|
|
} |
1225
|
|
|
if (isset($this->note_private)) { |
1226
|
|
|
$this->note_private = trim($this->note_private); |
1227
|
|
|
$this->note = $this->note_private; |
1228
|
|
|
} |
1229
|
|
|
if (isset($this->note_public)) { |
1230
|
|
|
$this->note_public = trim($this->note_public); |
1231
|
|
|
} |
1232
|
|
|
if (isset($this->model_pdf)) { |
1233
|
|
|
$this->model_pdf = trim($this->model_pdf); |
1234
|
|
|
} |
1235
|
|
|
if (isset($this->import_key)) { |
1236
|
|
|
$this->import_key = trim($this->import_key); |
1237
|
|
|
} |
1238
|
|
|
|
1239
|
|
|
|
1240
|
|
|
// Check parameters |
1241
|
|
|
// Put here code to add control on parameters values |
1242
|
|
|
|
1243
|
|
|
// Update request |
1244
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "facture_fourn SET"; |
1245
|
|
|
$sql .= " ref=" . (isset($this->ref) ? "'" . $this->db->escape($this->ref) . "'" : "null") . ","; |
1246
|
|
|
$sql .= " ref_supplier=" . (isset($this->ref_supplier) ? "'" . $this->db->escape($this->ref_supplier) . "'" : "null") . ","; |
1247
|
|
|
$sql .= " ref_ext=" . (isset($this->ref_ext) ? "'" . $this->db->escape($this->ref_ext) . "'" : "null") . ","; |
1248
|
|
|
$sql .= " entity=" . (isset($this->entity) ? ((int) $this->entity) : "null") . ","; |
1249
|
|
|
$sql .= " type=" . (isset($this->type) ? ((int) $this->type) : "null") . ","; |
1250
|
|
|
$sql .= " subtype=" . ((int) $this->subtype) . ","; |
1251
|
|
|
$sql .= " fk_soc=" . (isset($this->socid) ? ((int) $this->socid) : "null") . ","; |
1252
|
|
|
$sql .= " datec=" . (dol_strlen($this->datec) != 0 ? "'" . $this->db->idate($this->datec) . "'" : 'null') . ","; |
1253
|
|
|
$sql .= " datef=" . (dol_strlen($this->date) != 0 ? "'" . $this->db->idate($this->date) . "'" : 'null') . ","; |
1254
|
|
|
if (dol_strlen($this->tms) != 0) { |
1255
|
|
|
$sql .= " tms=" . (dol_strlen($this->tms) != 0 ? "'" . $this->db->idate($this->tms) . "'" : 'null') . ","; |
1256
|
|
|
} |
1257
|
|
|
$sql .= " libelle=" . (isset($this->label) ? "'" . $this->db->escape($this->label) . "'" : "null") . ","; |
1258
|
|
|
$sql .= " paye=" . (isset($this->paid) ? ((int) $this->paid) : "0") . ","; |
1259
|
|
|
$sql .= " close_code=" . (isset($this->close_code) ? "'" . $this->db->escape($this->close_code) . "'" : "null") . ","; |
1260
|
|
|
$sql .= " close_note=" . (isset($this->close_note) ? "'" . $this->db->escape($this->close_note) . "'" : "null") . ","; |
1261
|
|
|
$sql .= " localtax1=" . (isset($this->total_localtax1) ? ((float) $this->total_localtax1) : "null") . ","; |
1262
|
|
|
$sql .= " localtax2=" . (isset($this->total_localtax2) ? ((float) $this->total_localtax2) : "null") . ","; |
1263
|
|
|
$sql .= " total_ht=" . (isset($this->total_ht) ? ((float) $this->total_ht) : "null") . ","; |
1264
|
|
|
$sql .= " total_tva=" . (isset($this->total_tva) ? ((float) $this->total_tva) : "null") . ","; |
1265
|
|
|
$sql .= " total_ttc=" . (isset($this->total_ttc) ? ((float) $this->total_ttc) : "null") . ","; |
1266
|
|
|
$sql .= " fk_statut=" . (isset($this->status) ? ((int) $this->status) : (isset($this->statut) ? ((int) $this->statut) : "null")) . ","; |
1267
|
|
|
$sql .= " fk_user_author=" . (isset($this->author) ? ((int) $this->author) : "null") . ","; |
1268
|
|
|
$sql .= " fk_user_valid=" . (isset($this->fk_user_valid) ? ((int) $this->fk_user_valid) : "null") . ","; |
1269
|
|
|
$sql .= " fk_facture_source=" . ($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null") . ","; |
1270
|
|
|
$sql .= " vat_reverse_charge = " . ($this->vat_reverse_charge != '' ? ((int) $this->db->escape($this->vat_reverse_charge)) : 0) . ","; |
1271
|
|
|
$sql .= " fk_projet=" . (!empty($this->fk_project) ? ((int) $this->fk_project) : "null") . ","; |
1272
|
|
|
$sql .= " fk_cond_reglement=" . (isset($this->cond_reglement_id) ? ((int) $this->cond_reglement_id) : "null") . ","; |
1273
|
|
|
$sql .= " date_lim_reglement=" . (dol_strlen($this->date_echeance) != 0 ? "'" . $this->db->idate($this->date_echeance) . "'" : 'null') . ","; |
1274
|
|
|
$sql .= " note_private=" . (isset($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "null") . ","; |
1275
|
|
|
$sql .= " note_public=" . (isset($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "null") . ","; |
1276
|
|
|
$sql .= " model_pdf=" . (isset($this->model_pdf) ? "'" . $this->db->escape($this->model_pdf) . "'" : "null") . ","; |
1277
|
|
|
$sql .= " import_key=" . (isset($this->import_key) ? "'" . $this->db->escape($this->import_key) . "'" : "null"); |
1278
|
|
|
$sql .= " WHERE rowid=" . ((int) $this->id); |
1279
|
|
|
|
1280
|
|
|
$this->db->begin(); |
1281
|
|
|
|
1282
|
|
|
dol_syslog(get_only_class($this) . "::update", LOG_DEBUG); |
1283
|
|
|
$resql = $this->db->query($sql); |
1284
|
|
|
|
1285
|
|
|
if (!$resql) { |
1286
|
|
|
$error++; |
1287
|
|
|
|
1288
|
|
|
if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') { |
1289
|
|
|
$this->errors[] = $langs->trans('ErrorRefAlreadyExists'); |
1290
|
|
|
} else { |
1291
|
|
|
$this->errors[] = "Error " . $this->db->lasterror(); |
1292
|
|
|
} |
1293
|
|
|
} |
1294
|
|
|
|
1295
|
|
|
if (!$error) { |
1296
|
|
|
$result = $this->insertExtraFields(); |
1297
|
|
|
if ($result < 0) { |
1298
|
|
|
$error++; |
1299
|
|
|
} |
1300
|
|
|
} |
1301
|
|
|
|
1302
|
|
|
if (!$error) { |
1303
|
|
|
if (!$notrigger) { |
1304
|
|
|
// Call trigger |
1305
|
|
|
$result = $this->call_trigger('BILL_SUPPLIER_MODIFY', $user); |
1306
|
|
|
if ($result < 0) { |
1307
|
|
|
$error++; |
1308
|
|
|
} |
1309
|
|
|
// End call triggers |
1310
|
|
|
} |
1311
|
|
|
} |
1312
|
|
|
|
1313
|
|
|
// Commit or rollback |
1314
|
|
|
if ($error) { |
1315
|
|
|
foreach ($this->errors as $errmsg) { |
1316
|
|
|
dol_syslog(get_only_class($this) . "::update " . $errmsg, LOG_ERR); |
1317
|
|
|
$this->error .= ($this->error ? ', ' . $errmsg : $errmsg); |
1318
|
|
|
} |
1319
|
|
|
$this->db->rollback(); |
1320
|
|
|
return -1 * $error; |
1321
|
|
|
} else { |
1322
|
|
|
$this->db->commit(); |
1323
|
|
|
return 1; |
1324
|
|
|
} |
1325
|
|
|
} |
1326
|
|
|
|
1327
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
1328
|
|
|
/** |
1329
|
|
|
* Add a discount line into an invoice (as an invoice line) using an existing absolute discount (Consume the discount) |
1330
|
|
|
* |
1331
|
|
|
* @param int $idremise Id of absolute discount |
1332
|
|
|
* @return int >0 if OK, <0 if KO |
1333
|
|
|
*/ |
1334
|
|
|
public function insert_discount($idremise) |
1335
|
|
|
{ |
1336
|
|
|
// phpcs:enable |
1337
|
|
|
global $conf, $langs; |
1338
|
|
|
|
1339
|
|
|
include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php'; |
1340
|
|
|
include_once DOL_DOCUMENT_ROOT . '/core/class/discount.class.php'; |
1341
|
|
|
|
1342
|
|
|
$this->db->begin(); |
1343
|
|
|
|
1344
|
|
|
$remise = new DiscountAbsolute($this->db); |
1345
|
|
|
$result = $remise->fetch($idremise); |
1346
|
|
|
|
1347
|
|
|
if ($result > 0) { |
1348
|
|
|
if ($remise->fk_invoice_supplier) { // Protection against multiple submission |
1349
|
|
|
$this->error = $langs->trans("ErrorDiscountAlreadyUsed"); |
1350
|
|
|
$this->db->rollback(); |
1351
|
|
|
return -5; |
1352
|
|
|
} |
1353
|
|
|
|
1354
|
|
|
$facligne = new SupplierInvoiceLine($this->db); |
1355
|
|
|
$facligne->fk_facture_fourn = $this->id; |
1356
|
|
|
$facligne->fk_remise_except = $remise->id; |
1357
|
|
|
$facligne->desc = $remise->description; // Description ligne |
1358
|
|
|
$facligne->vat_src_code = $remise->vat_src_code; |
1359
|
|
|
$facligne->tva_tx = $remise->tva_tx; |
1360
|
|
|
$facligne->subprice = -$remise->amount_ht; |
1361
|
|
|
$facligne->fk_product = 0; // Id produit predefini |
1362
|
|
|
$facligne->product_type = 0; |
1363
|
|
|
$facligne->qty = 1; |
1364
|
|
|
$facligne->remise_percent = 0; |
1365
|
|
|
$facligne->rang = -1; |
1366
|
|
|
$facligne->info_bits = 2; |
1367
|
|
|
|
1368
|
|
|
if (getDolGlobalString('MAIN_ADD_LINE_AT_POSITION')) { |
1369
|
|
|
$facligne->rang = 1; |
1370
|
|
|
$linecount = count($this->lines); |
1371
|
|
|
for ($ii = 1; $ii <= $linecount; $ii++) { |
1372
|
|
|
$this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1); |
1373
|
|
|
} |
1374
|
|
|
} |
1375
|
|
|
|
1376
|
|
|
// Get buy/cost price of invoice that is source of discount |
1377
|
|
|
if ($remise->fk_invoice_supplier_source > 0) { |
1378
|
|
|
$srcinvoice = new FactureFournisseur($this->db); |
1379
|
|
|
$srcinvoice->fetch($remise->fk_invoice_supplier_source); |
1380
|
|
|
$totalcostpriceofinvoice = 0; |
1381
|
|
|
include_once DOL_DOCUMENT_ROOT . '/core/class/html.formmargin.class.php'; // TODO Move this into commonobject |
1382
|
|
|
$formmargin = new FormMargin($this->db); |
1383
|
|
|
$arraytmp = $formmargin->getMarginInfosArray($srcinvoice, false); |
1384
|
|
|
$facligne->pa_ht = $arraytmp['pa_total']; |
1385
|
|
|
} |
1386
|
|
|
|
1387
|
|
|
$facligne->total_ht = -$remise->amount_ht; |
1388
|
|
|
$facligne->total_tva = -$remise->amount_tva; |
1389
|
|
|
$facligne->total_ttc = -$remise->amount_ttc; |
1390
|
|
|
|
1391
|
|
|
$facligne->multicurrency_subprice = -$remise->multicurrency_subprice; |
1392
|
|
|
$facligne->multicurrency_total_ht = -$remise->multicurrency_total_ht; |
1393
|
|
|
$facligne->multicurrency_total_tva = -$remise->multicurrency_total_tva; |
1394
|
|
|
$facligne->multicurrency_total_ttc = -$remise->multicurrency_total_ttc; |
1395
|
|
|
|
1396
|
|
|
$lineid = $facligne->insert(); |
1397
|
|
|
if ($lineid > 0) { |
1398
|
|
|
$result = $this->update_price(1); |
1399
|
|
|
if ($result > 0) { |
1400
|
|
|
// Create link between discount and invoice line |
1401
|
|
|
$result = $remise->link_to_invoice($lineid, 0); |
1402
|
|
|
if ($result < 0) { |
1403
|
|
|
$this->error = $remise->error; |
1404
|
|
|
$this->db->rollback(); |
1405
|
|
|
return -4; |
1406
|
|
|
} |
1407
|
|
|
|
1408
|
|
|
$this->db->commit(); |
1409
|
|
|
return 1; |
1410
|
|
|
} else { |
1411
|
|
|
$this->error = $facligne->error; |
1412
|
|
|
$this->db->rollback(); |
1413
|
|
|
return -1; |
1414
|
|
|
} |
1415
|
|
|
} else { |
1416
|
|
|
$this->error = $facligne->error; |
1417
|
|
|
$this->db->rollback(); |
1418
|
|
|
return -2; |
1419
|
|
|
} |
1420
|
|
|
} else { |
1421
|
|
|
$this->db->rollback(); |
1422
|
|
|
return -3; |
1423
|
|
|
} |
1424
|
|
|
} |
1425
|
|
|
|
1426
|
|
|
|
1427
|
|
|
/** |
1428
|
|
|
* Delete invoice from database |
1429
|
|
|
* |
1430
|
|
|
* @param User $user User object |
1431
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
1432
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
1433
|
|
|
*/ |
1434
|
|
|
public function delete(User $user, $notrigger = 0) |
1435
|
|
|
{ |
1436
|
|
|
global $conf; |
1437
|
|
|
|
1438
|
|
|
$rowid = $this->id; |
1439
|
|
|
|
1440
|
|
|
dol_syslog("FactureFournisseur::delete rowid=" . $rowid, LOG_DEBUG); |
1441
|
|
|
|
1442
|
|
|
// TODO Test if there is at least on payment. If yes, refuse to delete. |
1443
|
|
|
|
1444
|
|
|
$error = 0; |
1445
|
|
|
$this->db->begin(); |
1446
|
|
|
|
1447
|
|
|
if (!$error && !$notrigger) { |
1448
|
|
|
// Call trigger |
1449
|
|
|
$result = $this->call_trigger('BILL_SUPPLIER_DELETE', $user); |
1450
|
|
|
if ($result < 0) { |
1451
|
|
|
$this->db->rollback(); |
1452
|
|
|
return -1; |
1453
|
|
|
} |
1454
|
|
|
// Fin appel triggers |
1455
|
|
|
} |
1456
|
|
|
|
1457
|
|
|
if (!$error) { |
1458
|
|
|
// If invoice was converted into a discount not yet consumed, we remove discount |
1459
|
|
|
$sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'societe_remise_except'; |
1460
|
|
|
$sql .= ' WHERE fk_invoice_supplier_source = ' . ((int) $rowid); |
1461
|
|
|
$sql .= ' AND fk_invoice_supplier_line IS NULL'; |
1462
|
|
|
$resql = $this->db->query($sql); |
1463
|
|
|
|
1464
|
|
|
// If invoice has consumned discounts |
1465
|
|
|
$this->fetch_lines(); |
1466
|
|
|
$list_rowid_det = array(); |
1467
|
|
|
foreach ($this->lines as $key => $invoiceline) { |
1468
|
|
|
$list_rowid_det[] = $invoiceline->id; |
1469
|
|
|
} |
1470
|
|
|
|
1471
|
|
|
// Consumned discounts are freed |
1472
|
|
|
if (count($list_rowid_det)) { |
1473
|
|
|
$sql = 'UPDATE ' . MAIN_DB_PREFIX . 'societe_remise_except'; |
1474
|
|
|
$sql .= ' SET fk_invoice_supplier = NULL, fk_invoice_supplier_line = NULL'; |
1475
|
|
|
$sql .= ' WHERE fk_invoice_supplier_line IN (' . $this->db->sanitize(implode(',', $list_rowid_det)) . ')'; |
1476
|
|
|
|
1477
|
|
|
dol_syslog(get_only_class($this) . "::delete", LOG_DEBUG); |
1478
|
|
|
if (!$this->db->query($sql)) { |
1479
|
|
|
$error++; |
1480
|
|
|
} |
1481
|
|
|
} |
1482
|
|
|
} |
1483
|
|
|
|
1484
|
|
|
if (!$error) { |
1485
|
|
|
$main = MAIN_DB_PREFIX . 'facture_fourn_det'; |
1486
|
|
|
$ef = $main . "_extrafields"; |
1487
|
|
|
$sqlef = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM " . $main . " WHERE fk_facture_fourn = " . ((int) $rowid) . ")"; |
1488
|
|
|
$resqlef = $this->db->query($sqlef); |
1489
|
|
|
$sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'facture_fourn_det WHERE fk_facture_fourn = ' . ((int) $rowid); |
1490
|
|
|
dol_syslog(get_only_class($this) . "::delete", LOG_DEBUG); |
1491
|
|
|
$resql = $this->db->query($sql); |
1492
|
|
|
if ($resqlef && $resql) { |
1493
|
|
|
$sql = 'DELETE FROM ' . MAIN_DB_PREFIX . 'facture_fourn WHERE rowid = ' . ((int) $rowid); |
1494
|
|
|
dol_syslog(get_only_class($this) . "::delete", LOG_DEBUG); |
1495
|
|
|
$resql2 = $this->db->query($sql); |
1496
|
|
|
if (!$resql2) { |
1497
|
|
|
$error++; |
1498
|
|
|
} |
1499
|
|
|
} else { |
1500
|
|
|
$error++; |
1501
|
|
|
} |
1502
|
|
|
} |
1503
|
|
|
|
1504
|
|
|
if (!$error) { |
1505
|
|
|
// Delete linked object |
1506
|
|
|
$res = $this->deleteObjectLinked(); |
1507
|
|
|
if ($res < 0) { |
1508
|
|
|
$error++; |
1509
|
|
|
} |
1510
|
|
|
} |
1511
|
|
|
|
1512
|
|
|
if (!$error) { |
1513
|
|
|
// Delete record into ECM index (Note that delete is also done when deleting files with the dol_delete_dir_recursive |
1514
|
|
|
$this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive |
1515
|
|
|
$this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive |
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_only_class($this) . "::delete error -4 " . $this->error, LOG_ERR); |
1547
|
|
|
} |
1548
|
|
|
} |
1549
|
|
|
|
1550
|
|
|
if (!$error) { |
1551
|
|
|
dol_syslog(get_only_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 implemented yet. |
1570
|
|
|
* @param string $close_note Comment informs if the class has been paid while payment is incomplete. Not implemented yet. |
1571
|
|
|
* @return int Return integer <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_only_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 implemented yet. |
1585
|
|
|
* @param string $close_note Comment informs if the class has been paid while payment is incomplete. Not implemented yet. |
1586
|
|
|
* @return int<-1,1> Return integer <0 si ko, >0 si ok |
1587
|
|
|
*/ |
1588
|
|
|
public function setPaid($user, $close_code = '', $close_note = '') |
1589
|
|
|
{ |
1590
|
|
|
$error = 0; |
1591
|
|
|
|
1592
|
|
|
if ($this->paid != 1) { |
1593
|
|
|
$this->db->begin(); |
1594
|
|
|
|
1595
|
|
|
$now = dol_now(); |
1596
|
|
|
|
1597
|
|
|
dol_syslog("FactureFournisseur::setPaid", 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 Return integer <0 si ok, >0 si ok |
1650
|
|
|
*/ |
1651
|
|
|
public function set_unpaid($user) |
1652
|
|
|
{ |
1653
|
|
|
// phpcs:enable |
1654
|
|
|
dol_syslog(get_only_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 Return integer <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_only_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 Return integer <0 if KO, >0 if OK |
1711
|
|
|
*/ |
1712
|
|
|
public function setCanceled($user, $close_code = '', $close_note = '') |
1713
|
|
|
{ |
1714
|
|
|
dol_syslog(get_only_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 Return integer <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 $mysoc, $conf, $langs; |
1772
|
|
|
|
1773
|
|
|
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php'; |
1774
|
|
|
|
1775
|
|
|
$now = dol_now(); |
1776
|
|
|
|
1777
|
|
|
$error = 0; |
1778
|
|
|
dol_syslog(get_only_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->status > self::STATUS_DRAFT) { // This is to avoid to validate twice (avoid errors on logs and stock management) |
1786
|
|
|
dol_syslog(get_only_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
|
|
|
// Check for mandatory fields in thirdparty (defined into setup) |
1801
|
|
|
if (!empty($this->thirdparty) && is_object($this->thirdparty)) { |
1802
|
|
|
$array_to_check = array('IDPROF1', 'IDPROF2', 'IDPROF3', 'IDPROF4', 'IDPROF5', 'IDPROF6', 'EMAIL', 'ACCOUNTANCY_CODE_SUPPLIER'); |
1803
|
|
|
foreach ($array_to_check as $key) { |
1804
|
|
|
$keymin = strtolower($key); |
1805
|
|
|
if ($keymin == 'accountancy_code_supplier') { |
1806
|
|
|
$keymin = 'code_compta_fournisseur'; |
1807
|
|
|
} |
1808
|
|
|
if (!property_exists($this->thirdparty, $keymin)) { |
1809
|
|
|
continue; |
1810
|
|
|
} |
1811
|
|
|
$vallabel = $this->thirdparty->$keymin; |
1812
|
|
|
|
1813
|
|
|
$i = (int) preg_replace('/[^0-9]/', '', $key); |
1814
|
|
|
if ($i > 0) { |
1815
|
|
|
if ($this->thirdparty->isACompany()) { |
1816
|
|
|
// Check for mandatory prof id (but only if country is other than ours) |
1817
|
|
|
if ($mysoc->country_id > 0 && $this->thirdparty->country_id == $mysoc->country_id) { |
1818
|
|
|
$idprof_mandatory = 'SOCIETE_' . $key . '_INVOICE_MANDATORY'; |
1819
|
|
|
if (!$vallabel && getDolGlobalString($idprof_mandatory)) { |
1820
|
|
|
$langs->load("errors"); |
1821
|
|
|
$this->error = $langs->trans('ErrorProdIdIsMandatory', $langs->transcountry('ProfId' . $i, $this->thirdparty->country_code)) . ' (' . $langs->trans("ForbiddenBySetupRules") . ') [' . $langs->trans('Company') . ' : ' . $this->thirdparty->name . ']'; |
1822
|
|
|
dol_syslog(__METHOD__ . ' ' . $this->error, LOG_ERR); |
1823
|
|
|
return -1; |
1824
|
|
|
} |
1825
|
|
|
} |
1826
|
|
|
} |
1827
|
|
|
} else { |
1828
|
|
|
if ($key == 'EMAIL') { |
1829
|
|
|
// Check for mandatory |
1830
|
|
|
if (getDolGlobalString('SOCIETE_EMAIL_INVOICE_MANDATORY') && !isValidEmail($this->thirdparty->email)) { |
1831
|
|
|
$langs->load("errors"); |
1832
|
|
|
$this->error = $langs->trans("ErrorBadEMail", $this->thirdparty->email) . ' (' . $langs->trans("ForbiddenBySetupRules") . ') [' . $langs->trans('Company') . ' : ' . $this->thirdparty->name . ']'; |
1833
|
|
|
dol_syslog(__METHOD__ . ' ' . $this->error, LOG_ERR); |
1834
|
|
|
return -1; |
1835
|
|
|
} |
1836
|
|
|
} elseif ($key == 'ACCOUNTANCY_CODE_SUPPLIER') { |
1837
|
|
|
// Check for mandatory |
1838
|
|
|
if (getDolGlobalString('SOCIETE_ACCOUNTANCY_CODE_SUPPLIER_INVOICE_MANDATORY') && empty($this->thirdparty->code_compta_fournisseur)) { |
1839
|
|
|
$langs->load("errors"); |
1840
|
|
|
$this->error = $langs->trans("ErrorAccountancyCodeSupplierIsMandatory", $this->thirdparty->name) . ' (' . $langs->trans("ForbiddenBySetupRules") . ')'; |
1841
|
|
|
dol_syslog(__METHOD__ . ' ' . $this->error, LOG_ERR); |
1842
|
|
|
return -1; |
1843
|
|
|
} |
1844
|
|
|
} |
1845
|
|
|
} |
1846
|
|
|
} |
1847
|
|
|
} |
1848
|
|
|
|
1849
|
|
|
$this->db->begin(); |
1850
|
|
|
|
1851
|
|
|
// Define new ref |
1852
|
|
|
if ($force_number) { |
1853
|
|
|
$num = $force_number; |
1854
|
|
|
} elseif (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref)) { // empty should not happened, but when it occurs, the test save life |
1855
|
|
|
$num = $this->getNextNumRef($this->thirdparty); |
1856
|
|
|
} else { |
1857
|
|
|
$num = $this->ref; |
1858
|
|
|
} |
1859
|
|
|
$this->newref = dol_sanitizeFileName($num); |
1860
|
|
|
|
1861
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "facture_fourn"; |
1862
|
|
|
$sql .= " SET ref='" . $this->db->escape($num) . "', fk_statut = 1, fk_user_valid = " . ((int) $user->id) . ", date_valid = '" . $this->db->idate($now) . "'"; |
1863
|
|
|
$sql .= " WHERE rowid = " . ((int) $this->id); |
1864
|
|
|
|
1865
|
|
|
dol_syslog(get_only_class($this) . "::validate", LOG_DEBUG); |
1866
|
|
|
$resql = $this->db->query($sql); |
1867
|
|
|
if ($resql) { |
1868
|
|
|
// Si on incrémente le produit principal et ses composants à la validation de facture fournisseur |
1869
|
|
|
if (!$error && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) { |
1870
|
|
|
$langs->load("agenda"); |
1871
|
|
|
|
1872
|
|
|
$cpt = count($this->lines); |
1873
|
|
|
for ($i = 0; $i < $cpt; $i++) { |
1874
|
|
|
if ($this->lines[$i]->fk_product > 0) { |
1875
|
|
|
$mouvP = new MouvementStock($this->db); |
1876
|
|
|
$mouvP->origin = &$this; |
1877
|
|
|
$mouvP->setOrigin($this->element, $this->id); |
1878
|
|
|
// We increase stock for product |
1879
|
|
|
$up_ht_disc = $this->lines[$i]->subprice; |
1880
|
|
|
if (!empty($this->lines[$i]->remise_percent) && !getDolGlobalString('STOCK_EXCLUDE_DISCOUNT_FOR_PMP')) { |
1881
|
|
|
$up_ht_disc = price2num($up_ht_disc * (100 - $this->lines[$i]->remise_percent) / 100, 'MU'); |
1882
|
|
|
} |
1883
|
|
|
if ($this->type == FactureFournisseur::TYPE_CREDIT_NOTE) { |
1884
|
|
|
$result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num)); |
1885
|
|
|
} else { |
1886
|
|
|
$result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $up_ht_disc, $langs->trans("InvoiceValidatedInDolibarr", $num)); |
1887
|
|
|
} |
1888
|
|
|
if ($result < 0) { |
1889
|
|
|
$this->error = $mouvP->error; |
1890
|
|
|
if (count($mouvP->errors)) { |
1891
|
|
|
$this->errors = $mouvP->errors; |
1892
|
|
|
} |
1893
|
|
|
return -2; |
1894
|
|
|
} |
1895
|
|
|
} |
1896
|
|
|
} |
1897
|
|
|
} |
1898
|
|
|
|
1899
|
|
|
// Triggers call |
1900
|
|
|
if (!$error && empty($notrigger)) { |
1901
|
|
|
// Call trigger |
1902
|
|
|
$result = $this->call_trigger('BILL_SUPPLIER_VALIDATE', $user); |
1903
|
|
|
if ($result < 0) { |
1904
|
|
|
$error++; |
1905
|
|
|
} |
1906
|
|
|
// End call triggers |
1907
|
|
|
} |
1908
|
|
|
|
1909
|
|
|
if (!$error) { |
1910
|
|
|
$this->oldref = $this->ref; |
1911
|
|
|
|
1912
|
|
|
// Rename directory if dir was a temporary ref |
1913
|
|
|
if (preg_match('/^[\(]?PROV/i', $this->ref)) { |
1914
|
|
|
// Now we rename also files into index |
1915
|
|
|
$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) . "'"; |
1916
|
|
|
$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; |
1917
|
|
|
$resql = $this->db->query($sql); |
1918
|
|
|
if (!$resql) { |
1919
|
|
|
$error++; |
1920
|
|
|
$this->error = $this->db->lasterror(); |
1921
|
|
|
} |
1922
|
|
|
$sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'fournisseur/facture/" . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $this->db->escape($this->newref) . "'"; |
1923
|
|
|
$sql .= " WHERE filepath = 'fournisseur/facture/" . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $this->db->escape($this->ref) . "' and entity = " . $conf->entity; |
1924
|
|
|
$resql = $this->db->query($sql); |
1925
|
|
|
if (!$resql) { |
1926
|
|
|
$error++; |
1927
|
|
|
$this->error = $this->db->lasterror(); |
1928
|
|
|
} |
1929
|
|
|
|
1930
|
|
|
// We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments |
1931
|
|
|
$oldref = dol_sanitizeFileName($this->ref); |
1932
|
|
|
$dirsource = $conf->fournisseur->facture->dir_output . '/' . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $oldref; |
1933
|
|
|
$dirdest = $conf->fournisseur->facture->dir_output . '/' . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $this->newref; |
1934
|
|
|
if (!$error && file_exists($dirsource)) { |
1935
|
|
|
dol_syslog(get_only_class($this) . "::validate rename dir " . $dirsource . " into " . $dirdest); |
1936
|
|
|
|
1937
|
|
|
if (@rename($dirsource, $dirdest)) { |
1938
|
|
|
dol_syslog("Rename ok"); |
1939
|
|
|
// Rename docs starting with $oldref with $this->newref |
1940
|
|
|
$listoffiles = dol_dir_list($conf->fournisseur->facture->dir_output . '/' . get_exdir($this->id, 2, 0, 0, $this, 'invoice_supplier') . $this->newref, 'files', 1, '^' . preg_quote($oldref, '/')); |
1941
|
|
|
foreach ($listoffiles as $fileentry) { |
1942
|
|
|
$dirsource = $fileentry['name']; |
1943
|
|
|
$dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $this->newref, $dirsource); |
1944
|
|
|
$dirsource = $fileentry['path'] . '/' . $dirsource; |
1945
|
|
|
$dirdest = $fileentry['path'] . '/' . $dirdest; |
1946
|
|
|
@rename($dirsource, $dirdest); |
|
|
|
|
1947
|
|
|
} |
1948
|
|
|
} |
1949
|
|
|
} |
1950
|
|
|
} |
1951
|
|
|
} |
1952
|
|
|
|
1953
|
|
|
// Set new ref and define current statut |
1954
|
|
|
if (!$error) { |
1955
|
|
|
$this->ref = $this->newref; |
1956
|
|
|
$this->statut = self::STATUS_VALIDATED; |
1957
|
|
|
$this->status = self::STATUS_VALIDATED; |
1958
|
|
|
//$this->date_validation=$now; this is stored into log table |
1959
|
|
|
} |
1960
|
|
|
|
1961
|
|
|
if (!$error) { |
1962
|
|
|
$this->db->commit(); |
1963
|
|
|
return 1; |
1964
|
|
|
} else { |
1965
|
|
|
$this->db->rollback(); |
1966
|
|
|
return -1; |
1967
|
|
|
} |
1968
|
|
|
} else { |
1969
|
|
|
$this->error = $this->db->error(); |
1970
|
|
|
$this->db->rollback(); |
1971
|
|
|
return -1; |
1972
|
|
|
} |
1973
|
|
|
} |
1974
|
|
|
|
1975
|
|
|
/** |
1976
|
|
|
* Set draft status |
1977
|
|
|
* |
1978
|
|
|
* @param User $user Object user that modify |
1979
|
|
|
* @param int $idwarehouse Id warehouse to use for stock change. |
1980
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
1981
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
1982
|
|
|
*/ |
1983
|
|
|
public function setDraft($user, $idwarehouse = -1, $notrigger = 0) |
1984
|
|
|
{ |
1985
|
|
|
// phpcs:enable |
1986
|
|
|
global $conf, $langs; |
1987
|
|
|
|
1988
|
|
|
$error = 0; |
1989
|
|
|
|
1990
|
|
|
if ($this->status == self::STATUS_DRAFT) { |
1991
|
|
|
dol_syslog(__METHOD__ . " already draft status", LOG_WARNING); |
1992
|
|
|
return 0; |
1993
|
|
|
} |
1994
|
|
|
|
1995
|
|
|
dol_syslog(__METHOD__, LOG_DEBUG); |
1996
|
|
|
|
1997
|
|
|
$this->db->begin(); |
1998
|
|
|
|
1999
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . "facture_fourn"; |
2000
|
|
|
$sql .= " SET fk_statut = " . self::STATUS_DRAFT; |
2001
|
|
|
$sql .= " WHERE rowid = " . ((int) $this->id); |
2002
|
|
|
|
2003
|
|
|
$result = $this->db->query($sql); |
2004
|
|
|
if ($result) { |
2005
|
|
|
if (!$error) { |
2006
|
|
|
$this->oldcopy = clone $this; |
2007
|
|
|
} |
2008
|
|
|
|
2009
|
|
|
// Si on incremente le produit principal et ses composants a la validation de facture fournisseur, on decremente |
2010
|
|
|
if ($result >= 0 && isModEnabled('stock') && getDolGlobalString('STOCK_CALCULATE_ON_SUPPLIER_BILL')) { |
2011
|
|
|
$langs->load("agenda"); |
2012
|
|
|
|
2013
|
|
|
$cpt = count($this->lines); |
2014
|
|
|
for ($i = 0; $i < $cpt; $i++) { |
2015
|
|
|
if ($this->lines[$i]->fk_product > 0) { |
2016
|
|
|
$mouvP = new MouvementStock($this->db); |
2017
|
|
|
$mouvP->origin = &$this; |
2018
|
|
|
$mouvP->setOrigin($this->element, $this->id); |
2019
|
|
|
// We increase stock for product |
2020
|
|
|
if ($this->type == FactureFournisseur::TYPE_CREDIT_NOTE) { |
2021
|
|
|
$result = $mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref)); |
2022
|
|
|
} else { |
2023
|
|
|
$result = $mouvP->livraison($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, $this->lines[$i]->subprice, $langs->trans("InvoiceBackToDraftInDolibarr", $this->ref)); |
2024
|
|
|
} |
2025
|
|
|
} |
2026
|
|
|
} |
2027
|
|
|
} |
2028
|
|
|
// Triggers call |
2029
|
|
|
if (!$error && empty($notrigger)) { |
2030
|
|
|
// Call trigger |
2031
|
|
|
$result = $this->call_trigger('BILL_SUPPLIER_UNVALIDATE', $user); |
2032
|
|
|
if ($result < 0) { |
2033
|
|
|
$error++; |
2034
|
|
|
} |
2035
|
|
|
// End call triggers |
2036
|
|
|
} |
2037
|
|
|
if ($error == 0) { |
2038
|
|
|
$this->db->commit(); |
2039
|
|
|
return 1; |
2040
|
|
|
} else { |
2041
|
|
|
$this->db->rollback(); |
2042
|
|
|
return -1; |
2043
|
|
|
} |
2044
|
|
|
} else { |
2045
|
|
|
$this->error = $this->db->error(); |
2046
|
|
|
$this->db->rollback(); |
2047
|
|
|
return -1; |
2048
|
|
|
} |
2049
|
|
|
} |
2050
|
|
|
|
2051
|
|
|
|
2052
|
|
|
/** |
2053
|
|
|
* Adds an invoice line (associated with no predefined product/service) |
2054
|
|
|
* The parameters are already supposed to be correct and with final values when calling |
2055
|
|
|
* this method. Also, for the VAT rate, it must already have been defined by the caller by |
2056
|
|
|
* by the get_default_tva method(vendor_company, buying company, idprod) and the desc must |
2057
|
|
|
* already have the right value (the caller has to manage the multilanguage). |
2058
|
|
|
* |
2059
|
|
|
* @param string $desc Description of the line |
2060
|
|
|
* @param double $pu Unit price (HT or TTC according to price_base_type, > 0 even for credit note) |
2061
|
|
|
* @param double $txtva Force Vat rate to use, -1 for auto. |
2062
|
|
|
* @param double $txlocaltax1 LocalTax1 Rate |
2063
|
|
|
* @param double $txlocaltax2 LocalTax2 Rate |
2064
|
|
|
* @param double $qty Quantity |
2065
|
|
|
* @param int $fk_product Product/Service ID predefined |
2066
|
|
|
* @param double $remise_percent Percentage discount of the line |
2067
|
|
|
* @param int $date_start Service start date |
2068
|
|
|
* @param int $date_end Service expiry date |
2069
|
|
|
* @param int $fk_code_ventilation Accounting breakdown code |
2070
|
|
|
* @param int $info_bits Line type bits |
2071
|
|
|
* @param string $price_base_type HT or TTC |
2072
|
|
|
* @param int $type Type of line (0=product, 1=service) |
2073
|
|
|
* @param int $rang Position of line |
2074
|
|
|
* @param int $notrigger Disable triggers |
2075
|
|
|
* @param array $array_options extrafields array |
2076
|
|
|
* @param int|null $fk_unit Code of the unit to use. Null to use the default one |
2077
|
|
|
* @param int $origin_id id origin document |
2078
|
|
|
* @param double $pu_devise Amount in currency |
2079
|
|
|
* @param string $ref_supplier Supplier ref |
2080
|
|
|
* @param int $special_code Special code |
2081
|
|
|
* @param int $fk_parent_line Parent line id |
2082
|
|
|
* @param int $fk_remise_except Id discount used |
2083
|
|
|
* @return int >0 if OK, <0 if KO |
2084
|
|
|
*/ |
2085
|
|
|
public function addline($desc, $pu, $txtva, $txlocaltax1, $txlocaltax2, $qty, $fk_product = 0, $remise_percent = 0, $date_start = 0, $date_end = 0, $fk_code_ventilation = 0, $info_bits = 0, $price_base_type = 'HT', $type = 0, $rang = -1, $notrigger = 0, $array_options = [], $fk_unit = null, $origin_id = 0, $pu_devise = 0, $ref_supplier = '', $special_code = 0, $fk_parent_line = 0, $fk_remise_except = 0) |
2086
|
|
|
{ |
2087
|
|
|
global $langs, $mysoc; |
2088
|
|
|
|
2089
|
|
|
dol_syslog(get_only_class($this) . "::addline $desc,$pu,$qty,$txtva,$fk_product,$remise_percent,$date_start,$date_end,$fk_code_ventilation,$info_bits,$price_base_type,$type,$fk_unit,fk_remise_except=$fk_remise_except", LOG_DEBUG); |
2090
|
|
|
include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php'; |
2091
|
|
|
|
2092
|
|
|
if ($this->status == self::STATUS_DRAFT) { |
2093
|
|
|
// Clean parameters |
2094
|
|
|
if (empty($remise_percent)) { |
2095
|
|
|
$remise_percent = 0; |
2096
|
|
|
} |
2097
|
|
|
if (empty($qty)) { |
2098
|
|
|
$qty = 0; |
2099
|
|
|
} |
2100
|
|
|
if (empty($info_bits)) { |
2101
|
|
|
$info_bits = 0; |
2102
|
|
|
} |
2103
|
|
|
if (empty($rang)) { |
2104
|
|
|
$rang = 0; |
2105
|
|
|
} |
2106
|
|
|
if (empty($fk_code_ventilation)) { |
2107
|
|
|
$fk_code_ventilation = 0; |
2108
|
|
|
} |
2109
|
|
|
if (empty($txtva)) { |
2110
|
|
|
$txtva = 0; |
2111
|
|
|
} |
2112
|
|
|
if (empty($txlocaltax1)) { |
2113
|
|
|
$txlocaltax1 = 0; |
2114
|
|
|
} |
2115
|
|
|
if (empty($txlocaltax2)) { |
2116
|
|
|
$txlocaltax2 = 0; |
2117
|
|
|
} |
2118
|
|
|
|
2119
|
|
|
$remise_percent = price2num($remise_percent); |
2120
|
|
|
$qty = price2num($qty); |
2121
|
|
|
$pu = price2num($pu); |
2122
|
|
|
if (!preg_match('/\((.*)\)/', (string) $txtva)) { |
2123
|
|
|
$txtva = price2num($txtva); // $txtva can have format '5,1' or '5.1' or '5.1(XXX)', we must clean only if '5,1' |
2124
|
|
|
} |
2125
|
|
|
$txlocaltax1 = price2num($txlocaltax1); |
2126
|
|
|
$txlocaltax2 = price2num($txlocaltax2); |
2127
|
|
|
|
2128
|
|
|
if ($date_start && $date_end && $date_start > $date_end) { |
2129
|
|
|
$langs->load("errors"); |
2130
|
|
|
$this->error = $langs->trans('ErrorStartDateGreaterEnd'); |
2131
|
|
|
return -1; |
2132
|
|
|
} |
2133
|
|
|
|
2134
|
|
|
$this->db->begin(); |
2135
|
|
|
|
2136
|
|
|
if ($fk_product > 0) { |
2137
|
|
|
if (getDolGlobalString('SUPPLIER_INVOICE_WITH_PREDEFINED_PRICES_ONLY')) { |
2138
|
|
|
// Check quantity is enough |
2139
|
|
|
dol_syslog(get_only_class($this) . "::addline we check supplier prices fk_product=" . $fk_product . " qty=" . $qty . " ref_supplier=" . $ref_supplier); |
2140
|
|
|
$prod = new ProductFournisseur($this->db); |
2141
|
|
|
if ($prod->fetch($fk_product) > 0) { |
2142
|
|
|
$product_type = $prod->type; |
2143
|
|
|
$label = $prod->label; |
2144
|
|
|
$fk_prod_fourn_price = 0; |
2145
|
|
|
|
2146
|
|
|
// We use 'none' instead of $ref_supplier, because $ref_supplier may not exists anymore. So we will take the first supplier price ok. |
2147
|
|
|
// If we want a dedicated supplier price, we must provide $fk_prod_fourn_price. |
2148
|
|
|
$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 |
2149
|
|
|
if ($result > 0) { |
2150
|
|
|
if (empty($pu)) { |
2151
|
|
|
$pu = $prod->fourn_pu; // Unit price supplier price set by get_buyprice |
2152
|
|
|
} |
2153
|
|
|
$ref_supplier = $prod->ref_supplier; // Ref supplier price set by get_buyprice |
2154
|
|
|
// is remise percent not keyed but present for the product we add it |
2155
|
|
|
if ($remise_percent == 0 && $prod->remise_percent != 0) { |
2156
|
|
|
$remise_percent = $prod->remise_percent; |
2157
|
|
|
} |
2158
|
|
|
} |
2159
|
|
|
if ($result == 0) { // If result == 0, we failed to found the supplier reference price |
2160
|
|
|
$langs->load("errors"); |
2161
|
|
|
$this->error = "Ref " . $prod->ref . " " . $langs->trans("ErrorQtyTooLowForThisSupplier"); |
2162
|
|
|
$this->db->rollback(); |
2163
|
|
|
dol_syslog(get_only_class($this) . "::addline we did not found supplier price, so we can't guess unit price"); |
2164
|
|
|
//$pu = $prod->fourn_pu; // We do not overwrite unit price |
2165
|
|
|
//$ref = $prod->ref_fourn; // We do not overwrite ref supplier price |
2166
|
|
|
return -1; |
2167
|
|
|
} |
2168
|
|
|
if ($result == -1) { |
2169
|
|
|
$langs->load("errors"); |
2170
|
|
|
$this->error = "Ref " . $prod->ref . " " . $langs->trans("ErrorQtyTooLowForThisSupplier"); |
2171
|
|
|
$this->db->rollback(); |
2172
|
|
|
dol_syslog(get_only_class($this) . "::addline result=" . $result . " - " . $this->error, LOG_DEBUG); |
2173
|
|
|
return -1; |
2174
|
|
|
} |
2175
|
|
|
if ($result < -1) { |
2176
|
|
|
$this->error = $prod->error; |
2177
|
|
|
$this->db->rollback(); |
2178
|
|
|
dol_syslog(get_only_class($this) . "::addline result=" . $result . " - " . $this->error, LOG_ERR); |
2179
|
|
|
return -1; |
2180
|
|
|
} |
2181
|
|
|
} else { |
2182
|
|
|
$this->error = $prod->error; |
2183
|
|
|
$this->db->rollback(); |
2184
|
|
|
return -1; |
2185
|
|
|
} |
2186
|
|
|
} |
2187
|
|
|
} else { |
2188
|
|
|
$product_type = $type; |
2189
|
|
|
} |
2190
|
|
|
|
2191
|
|
|
if (isModEnabled("multicurrency") && $pu_devise > 0) { |
2192
|
|
|
$pu = 0; |
2193
|
|
|
} |
2194
|
|
|
|
2195
|
|
|
$localtaxes_type = getLocalTaxesFromRate($txtva, 0, $mysoc, $this->thirdparty); |
2196
|
|
|
|
2197
|
|
|
// Clean vat code |
2198
|
|
|
$reg = array(); |
2199
|
|
|
$vat_src_code = ''; |
2200
|
|
|
if (preg_match('/\((.*)\)/', $txtva, $reg)) { |
2201
|
|
|
$vat_src_code = $reg[1]; |
2202
|
|
|
$txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate. |
2203
|
|
|
} |
2204
|
|
|
|
2205
|
|
|
// Calcul du total TTC et de la TVA pour la ligne a partir de |
2206
|
|
|
// qty, pu, remise_percent et txtva |
2207
|
|
|
// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker |
2208
|
|
|
// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva. |
2209
|
|
|
|
2210
|
|
|
$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); |
2211
|
|
|
$total_ht = $tabprice[0]; |
2212
|
|
|
$total_tva = $tabprice[1]; |
2213
|
|
|
$total_ttc = $tabprice[2]; |
2214
|
|
|
$total_localtax1 = $tabprice[9]; |
2215
|
|
|
$total_localtax2 = $tabprice[10]; |
2216
|
|
|
$pu_ht = $tabprice[3]; |
2217
|
|
|
|
2218
|
|
|
// MultiCurrency |
2219
|
|
|
$multicurrency_total_ht = $tabprice[16]; |
2220
|
|
|
$multicurrency_total_tva = $tabprice[17]; |
2221
|
|
|
$multicurrency_total_ttc = $tabprice[18]; |
2222
|
|
|
$pu_ht_devise = $tabprice[19]; |
2223
|
|
|
|
2224
|
|
|
// Check parameters |
2225
|
|
|
if ($type < 0) { |
2226
|
|
|
return -1; |
2227
|
|
|
} |
2228
|
|
|
|
2229
|
|
|
if ($rang < 0) { |
2230
|
|
|
$rangmax = $this->line_max(); |
2231
|
|
|
$rang = $rangmax + 1; |
2232
|
|
|
} |
2233
|
|
|
|
2234
|
|
|
// Insert line |
2235
|
|
|
$supplierinvoiceline = new SupplierInvoiceLine($this->db); |
2236
|
|
|
|
2237
|
|
|
$supplierinvoiceline->context = $this->context; |
2238
|
|
|
|
2239
|
|
|
$supplierinvoiceline->fk_facture_fourn = $this->id; |
2240
|
|
|
//$supplierinvoiceline->label=$label; // deprecated |
2241
|
|
|
$supplierinvoiceline->desc = $desc; |
2242
|
|
|
$supplierinvoiceline->ref_supplier = $ref_supplier; |
2243
|
|
|
|
2244
|
|
|
$supplierinvoiceline->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : $qty); // For credit note, quantity is always positive and unit price negative |
|
|
|
|
2245
|
|
|
$supplierinvoiceline->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise |
2246
|
|
|
|
2247
|
|
|
$supplierinvoiceline->vat_src_code = $vat_src_code; |
2248
|
|
|
$supplierinvoiceline->tva_tx = $txtva; |
|
|
|
|
2249
|
|
|
$supplierinvoiceline->localtax1_tx = ($total_localtax1 ? $localtaxes_type[1] : 0); |
2250
|
|
|
$supplierinvoiceline->localtax2_tx = ($total_localtax2 ? $localtaxes_type[3] : 0); |
2251
|
|
|
$supplierinvoiceline->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0]; |
2252
|
|
|
$supplierinvoiceline->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2]; |
2253
|
|
|
|
2254
|
|
|
$supplierinvoiceline->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 |
2255
|
|
|
$supplierinvoiceline->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 |
2256
|
|
|
$supplierinvoiceline->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 |
2257
|
|
|
$supplierinvoiceline->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 |
2258
|
|
|
$supplierinvoiceline->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 |
2259
|
|
|
|
2260
|
|
|
$supplierinvoiceline->fk_product = $fk_product; |
2261
|
|
|
$supplierinvoiceline->product_type = $type; |
2262
|
|
|
$supplierinvoiceline->remise_percent = $remise_percent; |
2263
|
|
|
$supplierinvoiceline->date_start = $date_start; |
2264
|
|
|
$supplierinvoiceline->date_end = $date_end; |
2265
|
|
|
$supplierinvoiceline->fk_code_ventilation = $fk_code_ventilation; |
2266
|
|
|
$supplierinvoiceline->rang = $rang; |
2267
|
|
|
$supplierinvoiceline->info_bits = $info_bits; |
2268
|
|
|
$supplierinvoiceline->fk_remise_except = $fk_remise_except; |
2269
|
|
|
|
2270
|
|
|
|
2271
|
|
|
$supplierinvoiceline->special_code = (int) $special_code; |
2272
|
|
|
$supplierinvoiceline->fk_parent_line = $fk_parent_line; |
2273
|
|
|
$supplierinvoiceline->origin = $this->origin; |
|
|
|
|
2274
|
|
|
$supplierinvoiceline->origin_id = $origin_id; |
2275
|
|
|
$supplierinvoiceline->fk_unit = $fk_unit; |
2276
|
|
|
|
2277
|
|
|
// Multicurrency |
2278
|
|
|
$supplierinvoiceline->fk_multicurrency = $this->fk_multicurrency; |
2279
|
|
|
$supplierinvoiceline->multicurrency_code = $this->multicurrency_code; |
|
|
|
|
2280
|
|
|
$supplierinvoiceline->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 |
2281
|
|
|
|
2282
|
|
|
$supplierinvoiceline->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 |
2283
|
|
|
$supplierinvoiceline->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 |
2284
|
|
|
$supplierinvoiceline->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 |
2285
|
|
|
|
2286
|
|
|
if (is_array($array_options) && count($array_options) > 0) { |
2287
|
|
|
$supplierinvoiceline->array_options = $array_options; |
2288
|
|
|
} |
2289
|
|
|
|
2290
|
|
|
$result = $supplierinvoiceline->insert($notrigger); |
2291
|
|
|
if ($result > 0) { |
2292
|
|
|
// Reorder if child line |
2293
|
|
|
if (!empty($fk_parent_line)) { |
2294
|
|
|
$this->line_order(true, 'DESC'); |
2295
|
|
|
} elseif ($rang > 0 && $rang <= count($this->lines)) { // Update all rank of all other lines |
2296
|
|
|
$linecount = count($this->lines); |
2297
|
|
|
for ($ii = $rang; $ii <= $linecount; $ii++) { |
2298
|
|
|
$this->updateRangOfLine($this->lines[$ii - 1]->id, $ii + 1); |
2299
|
|
|
} |
2300
|
|
|
} |
2301
|
|
|
|
2302
|
|
|
// Mise a jour information denormalisees au niveau de la facture meme |
2303
|
|
|
$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. |
2304
|
|
|
if ($result > 0) { |
2305
|
|
|
$this->db->commit(); |
2306
|
|
|
return $supplierinvoiceline->id; |
2307
|
|
|
} else { |
2308
|
|
|
$this->error = $this->db->error(); |
2309
|
|
|
$this->db->rollback(); |
2310
|
|
|
return -1; |
2311
|
|
|
} |
2312
|
|
|
} else { |
2313
|
|
|
$this->error = $supplierinvoiceline->error; |
2314
|
|
|
$this->errors = $supplierinvoiceline->errors; |
2315
|
|
|
$this->db->rollback(); |
2316
|
|
|
return -2; |
2317
|
|
|
} |
2318
|
|
|
} else { |
2319
|
|
|
return 0; |
2320
|
|
|
} |
2321
|
|
|
} |
2322
|
|
|
|
2323
|
|
|
/** |
2324
|
|
|
* Update a line detail into database |
2325
|
|
|
* |
2326
|
|
|
* @param int $id Id of line invoice |
2327
|
|
|
* @param string $desc Description of line |
2328
|
|
|
* @param double $pu Prix unitaire (HT ou TTC selon price_base_type) |
2329
|
|
|
* @param double $vatrate VAT Rate (Can be '8.5', '8.5 (ABC)') |
2330
|
|
|
* @param double $txlocaltax1 LocalTax1 Rate |
2331
|
|
|
* @param double $txlocaltax2 LocalTax2 Rate |
2332
|
|
|
* @param double $qty Quantity |
2333
|
|
|
* @param int $idproduct Id produit |
2334
|
|
|
* @param string $price_base_type HT or TTC |
2335
|
|
|
* @param int $info_bits Miscellaneous information of line |
2336
|
|
|
* @param int $type Type of line (0=product, 1=service) |
2337
|
|
|
* @param double $remise_percent Percentage discount of the line |
2338
|
|
|
* @param int $notrigger Disable triggers |
2339
|
|
|
* @param int|string $date_start Date start of service |
2340
|
|
|
* @param int|string $date_end Date end of service |
2341
|
|
|
* @param array $array_options extrafields array |
2342
|
|
|
* @param int|null $fk_unit Code of the unit to use. Null to use the default one |
2343
|
|
|
* @param double $pu_devise Amount in currency |
2344
|
|
|
* @param string $ref_supplier Supplier ref |
2345
|
|
|
* @param int $rang Line rank |
2346
|
|
|
* @return int<-1,1> Return integer <0 if KO, >0 if OK |
2347
|
|
|
*/ |
2348
|
|
|
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 = 0, $date_start = '', $date_end = '', $array_options = [], $fk_unit = null, $pu_devise = 0, $ref_supplier = '', $rang = 0) |
2349
|
|
|
{ |
2350
|
|
|
global $mysoc, $langs; |
2351
|
|
|
|
2352
|
|
|
dol_syslog(get_only_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); |
2353
|
|
|
include_once DOL_DOCUMENT_ROOT . '/core/lib/price.lib.php'; |
2354
|
|
|
|
2355
|
|
|
$pu = price2num($pu); |
2356
|
|
|
$qty = price2num($qty); |
2357
|
|
|
$remise_percent = (float) price2num($remise_percent); |
2358
|
|
|
$pu_devise = price2num($pu_devise); |
2359
|
|
|
|
2360
|
|
|
// Check parameters |
2361
|
|
|
//if (! is_numeric($pu) || ! is_numeric($qty)) return -1; |
2362
|
|
|
if ($type < 0) { |
2363
|
|
|
return -1; |
2364
|
|
|
} |
2365
|
|
|
|
2366
|
|
|
if ($date_start && $date_end && $date_start > $date_end) { |
2367
|
|
|
$langs->load("errors"); |
2368
|
|
|
$this->error = $langs->trans('ErrorStartDateGreaterEnd'); |
2369
|
|
|
return -1; |
2370
|
|
|
} |
2371
|
|
|
|
2372
|
|
|
// Clean parameters |
2373
|
|
|
if (empty($vatrate)) { |
2374
|
|
|
$vatrate = 0; |
2375
|
|
|
} |
2376
|
|
|
if (empty($txlocaltax1)) { |
2377
|
|
|
$txlocaltax1 = 0; |
2378
|
|
|
} |
2379
|
|
|
if (empty($txlocaltax2)) { |
2380
|
|
|
$txlocaltax2 = 0; |
2381
|
|
|
} |
2382
|
|
|
|
2383
|
|
|
$txlocaltax1 = (float) price2num($txlocaltax1); |
2384
|
|
|
$txlocaltax2 = (float) price2num($txlocaltax2); |
2385
|
|
|
|
2386
|
|
|
// Calcul du total TTC et de la TVA pour la ligne a partir de |
2387
|
|
|
// qty, pu, remise_percent et txtva |
2388
|
|
|
// TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker |
2389
|
|
|
// la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva. |
2390
|
|
|
|
2391
|
|
|
$localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty); |
2392
|
|
|
|
2393
|
|
|
$reg = array(); |
2394
|
|
|
|
2395
|
|
|
// Clean vat code |
2396
|
|
|
$vat_src_code = ''; |
2397
|
|
|
if (preg_match('/\((.*)\)/', (string) $vatrate, $reg)) { |
2398
|
|
|
$vat_src_code = $reg[1]; |
2399
|
|
|
$vatrate = preg_replace('/\s*\(.*\)/', '', (string) $vatrate); // Remove code into vatrate. |
2400
|
|
|
} |
2401
|
|
|
|
2402
|
|
|
$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); |
2403
|
|
|
$total_ht = $tabprice[0]; |
2404
|
|
|
$total_tva = $tabprice[1]; |
2405
|
|
|
$total_ttc = $tabprice[2]; |
2406
|
|
|
$pu_ht = $tabprice[3]; |
2407
|
|
|
$pu_tva = $tabprice[4]; |
2408
|
|
|
$pu_ttc = $tabprice[5]; |
2409
|
|
|
$total_localtax1 = $tabprice[9]; |
2410
|
|
|
$total_localtax2 = $tabprice[10]; |
2411
|
|
|
|
2412
|
|
|
// MultiCurrency |
2413
|
|
|
$multicurrency_total_ht = $tabprice[16]; |
2414
|
|
|
$multicurrency_total_tva = $tabprice[17]; |
2415
|
|
|
$multicurrency_total_ttc = $tabprice[18]; |
2416
|
|
|
$pu_ht_devise = $tabprice[19]; |
2417
|
|
|
|
2418
|
|
|
if (empty($info_bits)) { |
2419
|
|
|
$info_bits = 0; |
2420
|
|
|
} |
2421
|
|
|
|
2422
|
|
|
//Fetch current line from the database and then clone the object and set it in $oldline property |
2423
|
|
|
$line = new SupplierInvoiceLine($this->db); |
2424
|
|
|
$line->fetch($id); |
2425
|
|
|
$line->fetch_optionals(); |
2426
|
|
|
|
2427
|
|
|
$staticline = clone $line; |
2428
|
|
|
|
2429
|
|
|
if ($idproduct) { |
2430
|
|
|
$product = new Product($this->db); |
2431
|
|
|
$result = $product->fetch($idproduct); |
2432
|
|
|
$product_type = $product->type; |
2433
|
|
|
} else { |
2434
|
|
|
$idproduct = $staticline->fk_product; |
2435
|
|
|
$product_type = $type; |
2436
|
|
|
} |
2437
|
|
|
|
2438
|
|
|
$line->oldline = $staticline; |
2439
|
|
|
$line->context = $this->context; |
2440
|
|
|
|
2441
|
|
|
$line->description = $desc; |
|
|
|
|
2442
|
|
|
$line->desc = $desc; |
2443
|
|
|
|
2444
|
|
|
$line->qty = ($this->type == self::TYPE_CREDIT_NOTE ? abs((float) $qty) : $qty); // For credit note, quantity is always positive and unit price negative |
|
|
|
|
2445
|
|
|
$line->subprice = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ht) : $pu_ht); // For credit note, unit price always negative, always positive otherwise |
2446
|
|
|
$line->pu_ht = $line->subprice; // deprecated |
|
|
|
|
2447
|
|
|
$line->pu_ttc = ($this->type == self::TYPE_CREDIT_NOTE ? -abs($pu_ttc) : $pu_ttc); // For credit note, unit price always negative, always positive otherwise |
2448
|
|
|
|
2449
|
|
|
$line->remise_percent = $remise_percent; |
2450
|
|
|
$line->ref_supplier = $ref_supplier; |
2451
|
|
|
|
2452
|
|
|
$line->date_start = $date_start; |
2453
|
|
|
$line->date_end = $date_end; |
2454
|
|
|
|
2455
|
|
|
$line->vat_src_code = $vat_src_code; |
2456
|
|
|
$line->tva_tx = $vatrate; |
|
|
|
|
2457
|
|
|
$line->localtax1_tx = $txlocaltax1; |
2458
|
|
|
$line->localtax2_tx = $txlocaltax2; |
2459
|
|
|
$line->localtax1_type = empty($localtaxes_type[0]) ? 0 : $localtaxes_type[0]; |
2460
|
|
|
$line->localtax2_type = empty($localtaxes_type[2]) ? 0 : $localtaxes_type[2]; |
2461
|
|
|
|
2462
|
|
|
$line->total_ht = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ht) : $total_ht); |
2463
|
|
|
$line->total_tva = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_tva) : $total_tva); |
2464
|
|
|
$line->total_localtax1 = $total_localtax1; |
2465
|
|
|
$line->total_localtax2 = $total_localtax2; |
2466
|
|
|
$line->total_ttc = (($this->type == self::TYPE_CREDIT_NOTE || $qty < 0) ? -abs($total_ttc) : $total_ttc); |
2467
|
|
|
|
2468
|
|
|
$line->fk_product = $idproduct; |
2469
|
|
|
$line->product_type = $product_type; |
2470
|
|
|
$line->info_bits = $info_bits; |
2471
|
|
|
$line->fk_unit = $fk_unit; |
2472
|
|
|
$line->rang = $rang; |
2473
|
|
|
|
2474
|
|
|
if (is_array($array_options) && count($array_options) > 0) { |
2475
|
|
|
// We replace values in this->line->array_options only for entries defined into $array_options |
2476
|
|
|
foreach ($array_options as $key => $value) { |
2477
|
|
|
$line->array_options[$key] = $array_options[$key]; |
2478
|
|
|
} |
2479
|
|
|
} |
2480
|
|
|
|
2481
|
|
|
// Multicurrency |
2482
|
|
|
$line->multicurrency_subprice = $pu_ht_devise; |
2483
|
|
|
$line->multicurrency_total_ht = $multicurrency_total_ht; |
2484
|
|
|
$line->multicurrency_total_tva = $multicurrency_total_tva; |
2485
|
|
|
$line->multicurrency_total_ttc = $multicurrency_total_ttc; |
2486
|
|
|
|
2487
|
|
|
$res = $line->update($notrigger); |
2488
|
|
|
|
2489
|
|
|
if ($res < 1) { |
2490
|
|
|
$this->errors[] = $line->error; |
2491
|
|
|
} else { |
2492
|
|
|
// Update total price into invoice record |
2493
|
|
|
$res = $this->update_price('1', 'auto', 0, $this->thirdparty); |
2494
|
|
|
} |
2495
|
|
|
|
2496
|
|
|
return $res; |
2497
|
|
|
} |
2498
|
|
|
|
2499
|
|
|
/** |
2500
|
|
|
* Delete a detail line from database |
2501
|
|
|
* |
2502
|
|
|
* @param int $rowid Id of line to delete |
2503
|
|
|
* @param int $notrigger 1=Does not execute triggers, 0= execute triggers |
2504
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
2505
|
|
|
*/ |
2506
|
|
|
public function deleteLine($rowid, $notrigger = 0) |
2507
|
|
|
{ |
2508
|
|
|
if (!$rowid) { |
2509
|
|
|
$rowid = $this->id; |
2510
|
|
|
} |
2511
|
|
|
|
2512
|
|
|
$this->db->begin(); |
2513
|
|
|
|
2514
|
|
|
// Free the discount linked to a line of invoice |
2515
|
|
|
$sql = 'UPDATE ' . MAIN_DB_PREFIX . 'societe_remise_except'; |
2516
|
|
|
$sql .= ' SET fk_invoice_supplier_line = NULL'; |
2517
|
|
|
$sql .= ' WHERE fk_invoice_supplier_line = ' . ((int) $rowid); |
2518
|
|
|
|
2519
|
|
|
dol_syslog(get_only_class($this) . "::deleteline", LOG_DEBUG); |
2520
|
|
|
$result = $this->db->query($sql); |
2521
|
|
|
if (!$result) { |
2522
|
|
|
$this->error = $this->db->error(); |
2523
|
|
|
$this->db->rollback(); |
2524
|
|
|
return -2; |
2525
|
|
|
} |
2526
|
|
|
|
2527
|
|
|
$line = new SupplierInvoiceLine($this->db); |
2528
|
|
|
|
2529
|
|
|
if ($line->fetch($rowid) < 1) { |
2530
|
|
|
return -1; |
2531
|
|
|
} |
2532
|
|
|
|
2533
|
|
|
$res = $line->delete($notrigger); |
2534
|
|
|
|
2535
|
|
|
if ($res < 1) { |
2536
|
|
|
$this->errors[] = $line->error; |
2537
|
|
|
$this->db->rollback(); |
2538
|
|
|
return -3; |
2539
|
|
|
} else { |
2540
|
|
|
$res = $this->update_price(1); |
2541
|
|
|
|
2542
|
|
|
if ($res > 0) { |
2543
|
|
|
$this->db->commit(); |
2544
|
|
|
return 1; |
2545
|
|
|
} else { |
2546
|
|
|
$this->db->rollback(); |
2547
|
|
|
$this->error = $this->db->lasterror(); |
2548
|
|
|
return -4; |
2549
|
|
|
} |
2550
|
|
|
} |
2551
|
|
|
} |
2552
|
|
|
|
2553
|
|
|
|
2554
|
|
|
/** |
2555
|
|
|
* Loads the info order information into the invoice object |
2556
|
|
|
* |
2557
|
|
|
* @param int $id Id of the invoice to load |
2558
|
|
|
* @return void |
2559
|
|
|
*/ |
2560
|
|
|
public function info($id) |
2561
|
|
|
{ |
2562
|
|
|
$sql = 'SELECT c.rowid, datec, tms as datem, '; |
2563
|
|
|
$sql .= ' fk_user_author, fk_user_modif, fk_user_valid'; |
2564
|
|
|
$sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_fourn as c'; |
2565
|
|
|
$sql .= ' WHERE c.rowid = ' . ((int) $id); |
2566
|
|
|
|
2567
|
|
|
$result = $this->db->query($sql); |
2568
|
|
|
if ($result) { |
2569
|
|
|
if ($this->db->num_rows($result)) { |
2570
|
|
|
$obj = $this->db->fetch_object($result); |
2571
|
|
|
|
2572
|
|
|
$this->id = $obj->rowid; |
2573
|
|
|
|
2574
|
|
|
$this->user_creation_id = $obj->fk_user_author; |
2575
|
|
|
$this->user_validation_id = $obj->fk_user_valid; |
2576
|
|
|
$this->user_modification_id = $obj->fk_user_modif; |
2577
|
|
|
$this->date_creation = $this->db->jdate($obj->datec); |
2578
|
|
|
$this->date_modification = $this->db->jdate($obj->datem); |
2579
|
|
|
//$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) |
2580
|
|
|
} |
2581
|
|
|
$this->db->free($result); |
2582
|
|
|
} else { |
2583
|
|
|
dol_print_error($this->db); |
2584
|
|
|
} |
2585
|
|
|
} |
2586
|
|
|
|
2587
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
2588
|
|
|
/** |
2589
|
|
|
* Return list of replaceable invoices |
2590
|
|
|
* Status valid or abandoned for other reason + not paid + no payment + not already replaced |
2591
|
|
|
* |
2592
|
|
|
* @param int $socid Thirdparty id |
2593
|
|
|
* @return array|int Table of invoices ('id'=>id, 'ref'=>ref, 'status'=>status, 'paymentornot'=>0/1) |
2594
|
|
|
* <0 if error |
2595
|
|
|
*/ |
2596
|
|
|
public function list_replacable_supplier_invoices($socid = 0) |
2597
|
|
|
{ |
2598
|
|
|
// phpcs:enable |
2599
|
|
|
global $conf; |
2600
|
|
|
|
2601
|
|
|
$return = array(); |
2602
|
|
|
|
2603
|
|
|
$sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut,"; |
2604
|
|
|
$sql .= " ff.rowid as rowidnext"; |
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 .= " LEFT JOIN " . MAIN_DB_PREFIX . "facture_fourn as ff ON f.rowid = ff.fk_facture_source"; |
2608
|
|
|
$sql .= " WHERE (f.fk_statut = " . self::STATUS_VALIDATED . " OR (f.fk_statut = " . self::STATUS_ABANDONED . " AND f.close_code = '" . self::CLOSECODE_ABANDONED . "'))"; |
2609
|
|
|
$sql .= " AND f.entity = " . $conf->entity; |
2610
|
|
|
$sql .= " AND f.paye = 0"; // Pas classee payee completement |
2611
|
|
|
$sql .= " AND pf.fk_paiementfourn IS NULL"; // Aucun paiement deja fait |
2612
|
|
|
$sql .= " AND ff.fk_statut IS NULL"; // Renvoi vrai si pas facture de replacement |
2613
|
|
|
if ($socid > 0) { |
2614
|
|
|
$sql .= " AND f.fk_soc = " . ((int) $socid); |
2615
|
|
|
} |
2616
|
|
|
$sql .= " ORDER BY f.ref"; |
2617
|
|
|
|
2618
|
|
|
dol_syslog(get_only_class($this) . "::list_replacable_supplier_invoices", LOG_DEBUG); |
2619
|
|
|
$resql = $this->db->query($sql); |
2620
|
|
|
if ($resql) { |
2621
|
|
|
while ($obj = $this->db->fetch_object($resql)) { |
2622
|
|
|
$return[$obj->rowid] = array( |
2623
|
|
|
'id' => $obj->rowid, |
2624
|
|
|
'ref' => $obj->ref, |
2625
|
|
|
'status' => $obj->fk_statut |
2626
|
|
|
); |
2627
|
|
|
} |
2628
|
|
|
//print_r($return); |
2629
|
|
|
return $return; |
2630
|
|
|
} else { |
2631
|
|
|
$this->error = $this->db->error(); |
2632
|
|
|
return -1; |
2633
|
|
|
} |
2634
|
|
|
} |
2635
|
|
|
|
2636
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
2637
|
|
|
/** |
2638
|
|
|
* Return list of qualifying invoices for correction by credit note |
2639
|
|
|
* Invoices that respect the following rules are returned: |
2640
|
|
|
* (validated + payment in progress) or classified (paid in full or paid in part) + not already replaced + not already having |
2641
|
|
|
* |
2642
|
|
|
* @param int $socid Thirdparty id |
2643
|
|
|
* @return array|int Table of invoices ($id => array('ref'=>,'paymentornot'=>,'status'=>,'paye'=>) |
2644
|
|
|
* <0 if error |
2645
|
|
|
*/ |
2646
|
|
|
public function list_qualified_avoir_supplier_invoices($socid = 0) |
2647
|
|
|
{ |
2648
|
|
|
// phpcs:enable |
2649
|
|
|
global $conf; |
2650
|
|
|
|
2651
|
|
|
$return = array(); |
2652
|
|
|
|
2653
|
|
|
$sql = "SELECT f.rowid as rowid, f.ref, f.fk_statut, f.type, f.subtype, f.paye, pf.fk_paiementfourn"; |
2654
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "facture_fourn as f"; |
2655
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "paiementfourn_facturefourn as pf ON f.rowid = pf.fk_facturefourn"; |
2656
|
|
|
$sql .= " WHERE f.entity = " . $conf->entity; |
2657
|
|
|
$sql .= " AND f.fk_statut in (" . self::STATUS_VALIDATED . "," . self::STATUS_CLOSED . ")"; |
2658
|
|
|
$sql .= " AND NOT EXISTS (SELECT rowid from " . MAIN_DB_PREFIX . "facture_fourn as ff WHERE f.rowid = ff.fk_facture_source"; |
2659
|
|
|
$sql .= " AND ff.type=" . self::TYPE_REPLACEMENT . ")"; |
2660
|
|
|
$sql .= " AND f.type != " . self::TYPE_CREDIT_NOTE; // Type non 2 si facture non avoir |
2661
|
|
|
if ($socid > 0) { |
2662
|
|
|
$sql .= " AND f.fk_soc = " . ((int) $socid); |
2663
|
|
|
} |
2664
|
|
|
$sql .= " ORDER BY f.ref"; |
2665
|
|
|
|
2666
|
|
|
dol_syslog(get_only_class($this) . "::list_qualified_avoir_supplier_invoices", LOG_DEBUG); |
2667
|
|
|
$resql = $this->db->query($sql); |
2668
|
|
|
if ($resql) { |
2669
|
|
|
while ($obj = $this->db->fetch_object($resql)) { |
2670
|
|
|
$qualified = 0; |
2671
|
|
|
if ($obj->fk_statut == self::STATUS_VALIDATED) { |
2672
|
|
|
$qualified = 1; |
2673
|
|
|
} |
2674
|
|
|
if ($obj->fk_statut == self::STATUS_CLOSED) { |
2675
|
|
|
$qualified = 1; |
2676
|
|
|
} |
2677
|
|
|
if ($qualified) { |
2678
|
|
|
$paymentornot = ($obj->fk_paiementfourn ? 1 : 0); |
2679
|
|
|
$return[$obj->rowid] = array('ref' => $obj->ref, 'status' => $obj->fk_statut, 'type' => $obj->type, 'paye' => $obj->paye, 'paymentornot' => $paymentornot); |
2680
|
|
|
} |
2681
|
|
|
} |
2682
|
|
|
|
2683
|
|
|
return $return; |
2684
|
|
|
} else { |
2685
|
|
|
$this->error = $this->db->error(); |
2686
|
|
|
return -1; |
2687
|
|
|
} |
2688
|
|
|
} |
2689
|
|
|
|
2690
|
|
|
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps |
2691
|
|
|
/** |
2692
|
|
|
* Load indicators for dashboard (this->nbtodo and this->nbtodolate) |
2693
|
|
|
* |
2694
|
|
|
* @param User $user Object user |
2695
|
|
|
* @return WorkboardResponse|int Return integer <0 if KO, WorkboardResponse if OK |
2696
|
|
|
*/ |
2697
|
|
|
public function load_board($user) |
2698
|
|
|
{ |
2699
|
|
|
// phpcs:enable |
2700
|
|
|
global $conf, $langs; |
2701
|
|
|
|
2702
|
|
|
$sql = 'SELECT ff.rowid, ff.date_lim_reglement as datefin, ff.fk_statut as status, ff.total_ht, ff.total_ttc'; |
2703
|
|
|
$sql .= ' FROM ' . MAIN_DB_PREFIX . 'facture_fourn as ff'; |
2704
|
|
|
if (!$user->hasRight("societe", "client", "voir") && !$user->socid) { |
2705
|
|
|
$sql .= " JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON ff.fk_soc = sc.fk_soc AND sc.fk_user = " . ((int) $user->id); |
2706
|
|
|
} |
2707
|
|
|
$sql .= ' WHERE ff.paye = 0'; |
2708
|
|
|
$sql .= " AND ff.fk_statut IN (" . self::STATUS_VALIDATED . ")"; |
2709
|
|
|
$sql .= " AND ff.entity = " . $conf->entity; |
2710
|
|
|
if ($user->socid) { |
2711
|
|
|
$sql .= ' AND ff.fk_soc = ' . ((int) $user->socid); |
2712
|
|
|
} |
2713
|
|
|
|
2714
|
|
|
$resql = $this->db->query($sql); |
2715
|
|
|
if ($resql) { |
2716
|
|
|
$langs->load("bills"); |
2717
|
|
|
$now = dol_now(); |
2718
|
|
|
|
2719
|
|
|
$response = new WorkboardResponse(); |
2720
|
|
|
$response->warning_delay = $conf->facture->fournisseur->warning_delay / 60 / 60 / 24; |
2721
|
|
|
$response->label = $langs->trans("SupplierBillsToPay"); |
2722
|
|
|
$response->labelShort = $langs->trans("StatusToPay"); |
2723
|
|
|
|
2724
|
|
|
$response->url = constant('BASE_URL') . '/fourn/facture/list.php?search_status=1&mainmenu=billing&leftmenu=suppliers_bills'; |
2725
|
|
|
$response->img = img_object($langs->trans("Bills"), "bill"); |
2726
|
|
|
|
2727
|
|
|
$facturestatic = new FactureFournisseur($this->db); |
2728
|
|
|
|
2729
|
|
|
while ($obj = $this->db->fetch_object($resql)) { |
2730
|
|
|
$facturestatic->date_echeance = $this->db->jdate($obj->datefin); |
|
|
|
|
2731
|
|
|
$facturestatic->statut = $obj->status; // For backward compatibility |
2732
|
|
|
$facturestatic->status = $obj->status; |
2733
|
|
|
|
2734
|
|
|
$response->nbtodo++; |
2735
|
|
|
$response->total += $obj->total_ht; |
2736
|
|
|
|
2737
|
|
|
if ($facturestatic->hasDelay()) { |
2738
|
|
|
$response->nbtodolate++; |
2739
|
|
|
$response->url_late = constant('BASE_URL') . '/fourn/facture/list.php?search_option=late&mainmenu=billing&leftmenu=suppliers_bills'; |
2740
|
|
|
} |
2741
|
|
|
} |
2742
|
|
|
|
2743
|
|
|
$this->db->free($resql); |
2744
|
|
|
return $response; |
2745
|
|
|
} else { |
2746
|
|
|
dol_print_error($this->db); |
2747
|
|
|
$this->error = $this->db->error(); |
2748
|
|
|
return -1; |
2749
|
|
|
} |
2750
|
|
|
} |
2751
|
|
|
|
2752
|
|
|
/** |
2753
|
|
|
* getTooltipContentArray |
2754
|
|
|
* |
2755
|
|
|
* @param array{moretitle?:string} $params ex option, infologin |
2756
|
|
|
* @since v18 |
2757
|
|
|
* @return array{picto:string,ref?:string,refsupplier?:string,label?:string,date?:string,date_echeance?:string,amountht?:string,total_ht?:string,totaltva?:string,amountlt1?:string,amountlt2?:string,amountrevenustamp?:string,totalttc?:string} |
2758
|
|
|
*/ |
2759
|
|
|
public function getTooltipContentArray($params) |
2760
|
|
|
{ |
2761
|
|
|
global $conf, $langs, $mysoc; |
2762
|
|
|
|
2763
|
|
|
$langs->load('bills'); |
2764
|
|
|
|
2765
|
|
|
$datas = []; |
2766
|
|
|
$moretitle = $params['moretitle'] ?? ''; |
2767
|
|
|
|
2768
|
|
|
$picto = $this->picto; |
2769
|
|
|
if ($this->type == self::TYPE_REPLACEMENT) { |
2770
|
|
|
$picto .= 'r'; // Replacement invoice |
2771
|
|
|
} |
2772
|
|
|
if ($this->type == self::TYPE_CREDIT_NOTE) { |
2773
|
|
|
$picto .= 'a'; // Credit note |
2774
|
|
|
} |
2775
|
|
|
if ($this->type == self::TYPE_DEPOSIT) { |
2776
|
|
|
$picto .= 'd'; // Deposit invoice |
2777
|
|
|
} |
2778
|
|
|
|
2779
|
|
|
$datas['picto'] = img_picto('', $picto) . ' <u class="paddingrightonly">' . $langs->trans("SupplierInvoice") . '</u>'; |
2780
|
|
|
if ($this->type == self::TYPE_REPLACEMENT) { |
2781
|
|
|
$datas['picto'] .= '<u class="paddingrightonly">' . $langs->transnoentitiesnoconv("InvoiceReplace") . '</u>'; |
2782
|
|
|
} elseif ($this->type == self::TYPE_CREDIT_NOTE) { |
2783
|
|
|
$datas['picto'] .= '<u class="paddingrightonly">' . $langs->transnoentitiesnoconv("CreditNote") . '</u>'; |
2784
|
|
|
} elseif ($this->type == self::TYPE_DEPOSIT) { |
2785
|
|
|
$datas['picto'] .= '<u class="paddingrightonly">' . $langs->transnoentitiesnoconv("Deposit") . '</u>'; |
2786
|
|
|
} |
2787
|
|
|
if (isset($this->status)) { |
2788
|
|
|
$alreadypaid = -1; |
2789
|
|
|
if (isset($this->totalpaid)) { |
2790
|
|
|
$alreadypaid = $this->totalpaid; |
2791
|
|
|
} |
2792
|
|
|
|
2793
|
|
|
$datas['picto'] .= ' ' . $this->getLibStatut(5, $alreadypaid); |
2794
|
|
|
} |
2795
|
|
|
if ($moretitle) { |
2796
|
|
|
$datas['picto'] .= ' - ' . $moretitle; |
2797
|
|
|
} |
2798
|
|
|
if (!empty($this->ref)) { |
2799
|
|
|
$datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref; |
2800
|
|
|
} |
2801
|
|
|
if (!empty($this->ref_supplier)) { |
2802
|
|
|
$datas['refsupplier'] = '<br><b>' . $langs->trans('RefSupplier') . ':</b> ' . $this->ref_supplier; |
2803
|
|
|
} |
2804
|
|
|
if (!empty($this->label)) { |
2805
|
|
|
$datas['label'] = '<br><b>' . $langs->trans('Label') . ':</b> ' . $this->label; |
2806
|
|
|
} |
2807
|
|
|
if (!empty($this->date)) { |
2808
|
|
|
$datas['date'] = '<br><b>' . $langs->trans('Date') . ':</b> ' . dol_print_date($this->date, 'day'); |
2809
|
|
|
} |
2810
|
|
|
if (!empty($this->date_echeance)) { |
2811
|
|
|
$datas['date_echeance'] = '<br><b>' . $langs->trans('DateDue') . ':</b> ' . dol_print_date($this->date_echeance, 'day'); |
2812
|
|
|
} |
2813
|
|
|
if (!empty($this->total_ht)) { |
2814
|
|
|
$datas['amountht'] = '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency); |
2815
|
|
|
} |
2816
|
|
|
if (!empty($this->total_tva)) { |
2817
|
|
|
$datas['totaltva'] = '<br><b>' . $langs->trans('AmountVAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency); |
2818
|
|
|
} |
2819
|
|
|
if (!empty($this->total_localtax1) && $this->total_localtax1 != 0) { |
2820
|
|
|
// We keep test != 0 because $this->total_localtax1 can be '0.00000000' |
2821
|
|
|
$datas['amountlt1'] = '<br><b>' . $langs->transcountry('AmountLT1', $mysoc->country_code) . ':</b> ' . price($this->total_localtax1, 0, $langs, 0, -1, -1, $conf->currency); |
2822
|
|
|
} |
2823
|
|
|
if (!empty($this->total_localtax2) && $this->total_localtax2 != 0) { |
2824
|
|
|
$datas['amountlt2'] = '<br><b>' . $langs->transcountry('AmountLT2', $mysoc->country_code) . ':</b> ' . price($this->total_localtax2, 0, $langs, 0, -1, -1, $conf->currency); |
2825
|
|
|
} |
2826
|
|
|
if (!empty($this->revenuestamp)) { |
2827
|
|
|
$datas['amountrevenustamp'] = '<br><b>' . $langs->trans('RevenueStamp') . ':</b> ' . price($this->revenuestamp, 0, $langs, 0, -1, -1, $conf->currency); |
2828
|
|
|
} |
2829
|
|
|
if (!empty($this->total_ttc)) { |
2830
|
|
|
$datas['totalttc'] = '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency); |
2831
|
|
|
} |
2832
|
|
|
return $datas; |
2833
|
|
|
} |
2834
|
|
|
|
2835
|
|
|
/** |
2836
|
|
|
* Return clicable name (with picto eventually) |
2837
|
|
|
* |
2838
|
|
|
* @param int $withpicto 0=No picto, 1=Include picto into link, 2=Only picto |
2839
|
|
|
* @param string $option Where point the link |
2840
|
|
|
* @param int $max Max length of shown ref |
2841
|
|
|
* @param int $short 1=Return just URL |
2842
|
|
|
* @param string $moretitle Add more text to title tooltip |
2843
|
|
|
* @param int $notooltip 1=Disable tooltip |
2844
|
|
|
* @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking |
2845
|
|
|
* @param int $addlinktonotes Add link to show notes |
2846
|
|
|
* @return string String with URL |
2847
|
|
|
*/ |
2848
|
|
|
public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1, $addlinktonotes = 0) |
2849
|
|
|
{ |
2850
|
|
|
global $langs, $conf, $user, $hookmanager; |
2851
|
|
|
|
2852
|
|
|
$result = ''; |
2853
|
|
|
|
2854
|
|
|
if ($option == 'withdraw') { |
2855
|
|
|
$url = constant('BASE_URL') . '/compta/facture/prelevement.php?facid=' . $this->id . '&type=bank-transfer'; |
2856
|
|
|
} elseif ($option == 'document') { |
2857
|
|
|
$url = constant('BASE_URL') . '/fourn/facture/document.php?facid=' . $this->id; |
2858
|
|
|
} else { |
2859
|
|
|
$url = constant('BASE_URL') . '/fourn/facture/card.php?facid=' . $this->id; |
2860
|
|
|
} |
2861
|
|
|
|
2862
|
|
|
if ($short) { |
2863
|
|
|
return $url; |
2864
|
|
|
} |
2865
|
|
|
|
2866
|
|
|
if ($option !== 'nolink') { |
2867
|
|
|
// Add param to save lastsearch_values or not |
2868
|
|
|
$add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0); |
2869
|
|
|
if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) { |
2870
|
|
|
$add_save_lastsearch_values = 1; |
2871
|
|
|
} |
2872
|
|
|
if ($add_save_lastsearch_values) { |
2873
|
|
|
$url .= '&save_lastsearch_values=1'; |
2874
|
|
|
} |
2875
|
|
|
} |
2876
|
|
|
|
2877
|
|
|
$picto = $this->picto; |
2878
|
|
|
if ($this->type == self::TYPE_REPLACEMENT) { |
2879
|
|
|
$picto .= 'r'; // Replacement invoice |
2880
|
|
|
} |
2881
|
|
|
if ($this->type == self::TYPE_CREDIT_NOTE) { |
2882
|
|
|
$picto .= 'a'; // Credit note |
2883
|
|
|
} |
2884
|
|
|
if ($this->type == self::TYPE_DEPOSIT) { |
2885
|
|
|
$picto .= 'd'; // Deposit invoice |
2886
|
|
|
} |
2887
|
|
|
|
2888
|
|
|
$params = [ |
2889
|
|
|
'id' => $this->id, |
2890
|
|
|
'objecttype' => $this->element, |
2891
|
|
|
'option' => $option, |
2892
|
|
|
'moretitle' => $moretitle, |
2893
|
|
|
]; |
2894
|
|
|
$classfortooltip = 'classfortooltip'; |
2895
|
|
|
$dataparams = ''; |
2896
|
|
|
if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) { |
2897
|
|
|
$classfortooltip = 'classforajaxtooltip'; |
2898
|
|
|
$dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"'; |
2899
|
|
|
$label = ''; |
2900
|
|
|
} else { |
2901
|
|
|
$label = implode($this->getTooltipContentArray($params)); |
2902
|
|
|
} |
2903
|
|
|
|
2904
|
|
|
$ref = $this->ref; |
2905
|
|
|
if (empty($ref)) { |
2906
|
|
|
$ref = $this->id; |
2907
|
|
|
} |
2908
|
|
|
|
2909
|
|
|
$linkclose = ''; |
2910
|
|
|
if (empty($notooltip)) { |
2911
|
|
|
if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) { |
2912
|
|
|
$label = $langs->trans("ShowSupplierInvoice"); |
2913
|
|
|
$linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"'; |
2914
|
|
|
} |
2915
|
|
|
$linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"'); |
2916
|
|
|
$linkclose .= $dataparams . ' class="' . $classfortooltip . '"'; |
2917
|
|
|
} |
2918
|
|
|
|
2919
|
|
|
$linkstart = '<a href="' . $url . '"'; |
2920
|
|
|
$linkstart .= $linkclose . '>'; |
2921
|
|
|
$linkend = '</a>'; |
2922
|
|
|
|
2923
|
|
|
$result .= $linkstart; |
2924
|
|
|
if ($withpicto) { |
2925
|
|
|
$result .= img_object(($notooltip ? '' : $label), ($picto ? $picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . '"'), 0, 0, $notooltip ? 0 : 1); |
2926
|
|
|
} |
2927
|
|
|
if ($withpicto != 2) { |
2928
|
|
|
$result .= ($max ? dol_trunc($ref, $max) : $ref); |
2929
|
|
|
} |
2930
|
|
|
$result .= $linkend; |
2931
|
|
|
|
2932
|
|
|
if ($addlinktonotes) { |
2933
|
|
|
$txttoshow = ($user->socid > 0 ? $this->note_public : $this->note_private); |
2934
|
|
|
if ($txttoshow) { |
2935
|
|
|
$notetoshow = $langs->trans("ViewPrivateNote") . ':<br>' . dol_string_nohtmltag($txttoshow, 1); |
2936
|
|
|
$result .= ' <span class="note inline-block">'; |
2937
|
|
|
$result .= '<a href="' . constant('BASE_URL') . '/fourn/facture/note.php?id=' . $this->id . '" class="classfortooltip" title="' . dol_escape_htmltag($notetoshow) . '">'; |
2938
|
|
|
$result .= img_picto('', 'note'); |
2939
|
|
|
$result .= '</a>'; |
2940
|
|
|
$result .= '</span>'; |
2941
|
|
|
} |
2942
|
|
|
} |
2943
|
|
|
global $action; |
2944
|
|
|
$hookmanager->initHooks(array($this->element . 'dao')); |
2945
|
|
|
$parameters = array('id' => $this->id, 'getnomurl' => &$result); |
2946
|
|
|
$reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks |
2947
|
|
|
if ($reshook > 0) { |
2948
|
|
|
$result = $hookmanager->resPrint; |
2949
|
|
|
} else { |
2950
|
|
|
$result .= $hookmanager->resPrint; |
2951
|
|
|
} |
2952
|
|
|
return $result; |
2953
|
|
|
} |
2954
|
|
|
|
2955
|
|
|
/** |
2956
|
|
|
* Return next reference of supplier invoice not already used (or last reference) |
2957
|
|
|
* according to numbering module defined into constant INVOICE_SUPPLIER_ADDON_NUMBER |
2958
|
|
|
* |
2959
|
|
|
* @param Societe $soc Thirdparty object |
2960
|
|
|
* @param string $mode 'next' for next value or 'last' for last value |
2961
|
|
|
* @return string|-1 Returns free reference or last reference, or '' or -1 if error |
|
|
|
|
2962
|
|
|
*/ |
2963
|
|
|
public function getNextNumRef($soc, $mode = 'next') |
2964
|
|
|
{ |
2965
|
|
|
global $db, $langs, $conf; |
2966
|
|
|
$langs->load("orders"); |
2967
|
|
|
|
2968
|
|
|
// Clean parameters (if not defined or using deprecated value) |
2969
|
|
|
if (!getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER')) { |
2970
|
|
|
$conf->global->INVOICE_SUPPLIER_ADDON_NUMBER = 'mod_facture_fournisseur_cactus'; |
2971
|
|
|
} |
2972
|
|
|
|
2973
|
|
|
$mybool = false; |
2974
|
|
|
|
2975
|
|
|
$file = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER') . ".php"; |
2976
|
|
|
$classname = getDolGlobalString('INVOICE_SUPPLIER_ADDON_NUMBER'); |
2977
|
|
|
|
2978
|
|
|
// Include file with class |
2979
|
|
|
$dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); |
2980
|
|
|
|
2981
|
|
|
foreach ($dirmodels as $reldir) { |
2982
|
|
|
$dir = dol_buildpath($reldir . "core/modules/supplier_invoice/"); |
2983
|
|
|
|
2984
|
|
|
// Load file with numbering class (if found) |
2985
|
|
|
$mybool = ((bool) @include_once $dir . $file) || $mybool; |
2986
|
|
|
} |
2987
|
|
|
|
2988
|
|
|
if (!$mybool) { |
2989
|
|
|
dol_print_error(null, "Failed to include file " . $file); |
2990
|
|
|
return ''; |
2991
|
|
|
} |
2992
|
|
|
|
2993
|
|
|
$obj = new $classname(); |
2994
|
|
|
'@phan-var-force ModeleNumRefSuppliersInvoices $obj'; |
2995
|
|
|
$numref = ""; |
2996
|
|
|
$numref = $obj->getNextValue($soc, $this, $mode); |
2997
|
|
|
|
2998
|
|
|
if ($numref != "") { |
2999
|
|
|
return $numref; |
3000
|
|
|
} else { |
3001
|
|
|
$this->error = $obj->error; |
3002
|
|
|
return -1; |
3003
|
|
|
} |
3004
|
|
|
} |
3005
|
|
|
|
3006
|
|
|
|
3007
|
|
|
/** |
3008
|
|
|
* Initialise an instance with random values. |
3009
|
|
|
* Used to build previews or test instances. |
3010
|
|
|
* id must be 0 if object instance is a specimen. |
3011
|
|
|
* |
3012
|
|
|
* @param string $option ''=Create a specimen invoice with lines, 'nolines'=No lines |
3013
|
|
|
* @return int |
3014
|
|
|
*/ |
3015
|
|
|
public function initAsSpecimen($option = '') |
3016
|
|
|
{ |
3017
|
|
|
global $langs, $conf; |
3018
|
|
|
|
3019
|
|
|
$now = dol_now(); |
3020
|
|
|
|
3021
|
|
|
// Load array of products prodids |
3022
|
|
|
$num_prods = 0; |
3023
|
|
|
$prodids = array(); |
3024
|
|
|
|
3025
|
|
|
$sql = "SELECT rowid"; |
3026
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "product"; |
3027
|
|
|
$sql .= " WHERE entity IN (" . getEntity('product') . ")"; |
3028
|
|
|
$sql .= $this->db->plimit(100); |
3029
|
|
|
|
3030
|
|
|
$resql = $this->db->query($sql); |
3031
|
|
|
if ($resql) { |
3032
|
|
|
$num_prods = $this->db->num_rows($resql); |
3033
|
|
|
$i = 0; |
3034
|
|
|
while ($i < $num_prods) { |
3035
|
|
|
$i++; |
3036
|
|
|
$row = $this->db->fetch_row($resql); |
3037
|
|
|
$prodids[$i] = $row[0]; |
3038
|
|
|
} |
3039
|
|
|
} |
3040
|
|
|
|
3041
|
|
|
// Initialise parameters |
3042
|
|
|
$this->id = 0; |
3043
|
|
|
$this->ref = 'SPECIMEN'; |
3044
|
|
|
$this->ref_supplier = 'SUPPLIER_REF_SPECIMEN'; |
3045
|
|
|
$this->specimen = 1; |
3046
|
|
|
$this->socid = 1; |
3047
|
|
|
$this->date = $now; |
3048
|
|
|
$this->date_lim_reglement = $this->date + 3600 * 24 * 30; |
3049
|
|
|
$this->cond_reglement_code = 'RECEP'; |
3050
|
|
|
$this->mode_reglement_code = 'CHQ'; |
3051
|
|
|
|
3052
|
|
|
$this->note_public = 'This is a comment (public)'; |
3053
|
|
|
$this->note_private = 'This is a comment (private)'; |
3054
|
|
|
|
3055
|
|
|
$this->multicurrency_tx = 1; |
3056
|
|
|
$this->multicurrency_code = $conf->currency; |
3057
|
|
|
|
3058
|
|
|
$xnbp = 0; |
3059
|
|
|
if (empty($option) || $option != 'nolines') { |
3060
|
|
|
// Lines |
3061
|
|
|
$nbp = 5; |
3062
|
|
|
while ($xnbp < $nbp) { |
3063
|
|
|
$line = new SupplierInvoiceLine($this->db); |
3064
|
|
|
$line->desc = $langs->trans("Description") . " " . $xnbp; |
3065
|
|
|
$line->qty = 1; |
3066
|
|
|
$line->subprice = 100; |
3067
|
|
|
$line->pu_ht = $line->subprice; // the canelle template use pu_ht and not subprice |
|
|
|
|
3068
|
|
|
$line->price = 100; |
|
|
|
|
3069
|
|
|
$line->tva_tx = 19.6; |
3070
|
|
|
$line->localtax1_tx = 0; |
3071
|
|
|
$line->localtax2_tx = 0; |
3072
|
|
|
if ($xnbp == 2) { |
3073
|
|
|
$line->total_ht = 50; |
3074
|
|
|
$line->total_ttc = 59.8; |
3075
|
|
|
$line->total_tva = 9.8; |
3076
|
|
|
$line->remise_percent = 50; |
3077
|
|
|
} else { |
3078
|
|
|
$line->total_ht = 100; |
3079
|
|
|
$line->total_ttc = 119.6; |
3080
|
|
|
$line->total_tva = 19.6; |
3081
|
|
|
$line->remise_percent = 0; |
3082
|
|
|
} |
3083
|
|
|
|
3084
|
|
|
if ($num_prods > 0) { |
3085
|
|
|
$prodid = mt_rand(1, $num_prods); |
3086
|
|
|
$line->fk_product = $prodids[$prodid]; |
3087
|
|
|
} |
3088
|
|
|
$line->product_type = 0; |
3089
|
|
|
|
3090
|
|
|
$this->lines[$xnbp] = $line; |
3091
|
|
|
|
3092
|
|
|
$this->total_ht += $line->total_ht; |
3093
|
|
|
$this->total_tva += $line->total_tva; |
3094
|
|
|
$this->total_ttc += $line->total_ttc; |
3095
|
|
|
|
3096
|
|
|
$xnbp++; |
3097
|
|
|
} |
3098
|
|
|
} |
3099
|
|
|
|
3100
|
|
|
$this->total_ht = $xnbp * 100; |
3101
|
|
|
$this->total_tva = $xnbp * 19.6; |
3102
|
|
|
$this->total_ttc = $xnbp * 119.6; |
3103
|
|
|
|
3104
|
|
|
return 1; |
3105
|
|
|
} |
3106
|
|
|
|
3107
|
|
|
/** |
3108
|
|
|
* Load indicators for dashboard (this->nbtodo and this->nbtodolate) |
3109
|
|
|
* |
3110
|
|
|
* @return int Return integer <0 if KO, >0 if OK |
3111
|
|
|
*/ |
3112
|
|
|
public function loadStateBoard() |
3113
|
|
|
{ |
3114
|
|
|
global $conf, $user; |
3115
|
|
|
|
3116
|
|
|
$this->nb = array(); |
3117
|
|
|
|
3118
|
|
|
$clause = "WHERE"; |
3119
|
|
|
|
3120
|
|
|
$sql = "SELECT count(f.rowid) as nb"; |
3121
|
|
|
$sql .= " FROM " . MAIN_DB_PREFIX . "facture_fourn as f"; |
3122
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe as s ON f.fk_soc = s.rowid"; |
3123
|
|
|
if (!$user->hasRight("societe", "client", "voir") && !$user->socid) { |
3124
|
|
|
$sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "societe_commerciaux as sc ON s.rowid = sc.fk_soc"; |
3125
|
|
|
$sql .= " WHERE sc.fk_user = " . ((int) $user->id); |
3126
|
|
|
$clause = "AND"; |
3127
|
|
|
} |
3128
|
|
|
$sql .= " " . $clause . " f.entity = " . $conf->entity; |
3129
|
|
|
|
3130
|
|
|
$resql = $this->db->query($sql); |
3131
|
|
|
if ($resql) { |
3132
|
|
|
while ($obj = $this->db->fetch_object($resql)) { |
3133
|
|
|
$this->nb["supplier_invoices"] = $obj->nb; |
3134
|
|
|
} |
3135
|
|
|
$this->db->free($resql); |
3136
|
|
|
return 1; |
3137
|
|
|
} else { |
3138
|
|
|
dol_print_error($this->db); |
3139
|
|
|
$this->error = $this->db->error(); |
3140
|
|
|
return -1; |
3141
|
|
|
} |
3142
|
|
|
} |
3143
|
|
|
|
3144
|
|
|
/** |
3145
|
|
|
* Load an object from its id and create a new one in database |
3146
|
|
|
* |
3147
|
|
|
* @param User $user User that clone |
3148
|
|
|
* @param int $fromid Id of object to clone |
3149
|
|
|
* @param int $invertdetail Reverse sign of amounts for lines |
3150
|
|
|
* @return int New id of clone |
3151
|
|
|
*/ |
3152
|
|
|
public function createFromClone(User $user, $fromid, $invertdetail = 0) |
3153
|
|
|
{ |
3154
|
|
|
global $conf, $langs; |
3155
|
|
|
|
3156
|
|
|
$error = 0; |
3157
|
|
|
|
3158
|
|
|
$object = new FactureFournisseur($this->db); |
3159
|
|
|
|
3160
|
|
|
$this->db->begin(); |
3161
|
|
|
|
3162
|
|
|
// Load source object |
3163
|
|
|
$object->fetch($fromid); |
3164
|
|
|
$object->id = 0; |
3165
|
|
|
$object->statut = self::STATUS_DRAFT; // For backward compatibility |
3166
|
|
|
$object->status = self::STATUS_DRAFT; |
3167
|
|
|
|
3168
|
|
|
$object->fetch_thirdparty(); // We need it to recalculate VAT localtaxes according to main sale taxes and vendor |
3169
|
|
|
|
3170
|
|
|
// Clear fields |
3171
|
|
|
$object->ref_supplier = (empty($this->ref_supplier) ? $langs->trans("CopyOf") . ' ' . $object->ref_supplier : $this->ref_supplier); |
3172
|
|
|
$object->author = $user->id; // FIXME? user_validation_id is replacement for author |
|
|
|
|
3173
|
|
|
$object->user_validation_id = 0; // FIXME? user_validation_id is replacement for author |
3174
|
|
|
$object->fk_facture_source = 0; |
3175
|
|
|
$object->date_creation = ''; |
3176
|
|
|
$object->date_validation = ''; |
3177
|
|
|
$object->date = (empty($this->date) ? dol_now() : $this->date); |
3178
|
|
|
$object->ref_client = ''; |
3179
|
|
|
$object->close_code = ''; |
3180
|
|
|
$object->close_note = ''; |
3181
|
|
|
if (getDolGlobalInt('MAIN_DONT_KEEP_NOTE_ON_CLONING') == 1) { |
3182
|
|
|
$object->note_private = ''; |
3183
|
|
|
$object->note_public = ''; |
3184
|
|
|
} |
3185
|
|
|
|
3186
|
|
|
$object->date_echeance = $object->calculate_date_lim_reglement(); |
|
|
|
|
3187
|
|
|
|
3188
|
|
|
// Loop on each line of new invoice |
3189
|
|
|
foreach ($object->lines as $i => $line) { |
3190
|
|
|
if (isset($object->lines[$i]->info_bits) && ($object->lines[$i]->info_bits & 0x02) == 0x02) { // We do not clone line of discounts |
3191
|
|
|
unset($object->lines[$i]); |
3192
|
|
|
} |
3193
|
|
|
} |
3194
|
|
|
|
3195
|
|
|
// Create clone |
3196
|
|
|
$object->context['createfromclone'] = 'createfromclone'; |
3197
|
|
|
$result = $object->create($user); |
3198
|
|
|
|
3199
|
|
|
// Other options |
3200
|
|
|
if ($result < 0) { |
3201
|
|
|
$this->error = $object->error; |
3202
|
|
|
$this->errors = $object->errors; |
3203
|
|
|
$error++; |
3204
|
|
|
} |
3205
|
|
|
|
3206
|
|
|
if (!$error) { |
3207
|
|
|
} |
3208
|
|
|
|
3209
|
|
|
unset($object->context['createfromclone']); |
3210
|
|
|
|
3211
|
|
|
// End |
3212
|
|
|
if (!$error) { |
3213
|
|
|
$this->db->commit(); |
3214
|
|
|
return $object->id; |
3215
|
|
|
} else { |
3216
|
|
|
$this->db->rollback(); |
3217
|
|
|
return -1; |
3218
|
|
|
} |
3219
|
|
|
} |
3220
|
|
|
|
3221
|
|
|
/** |
3222
|
|
|
* Create a document onto disk according to template model. |
3223
|
|
|
* |
3224
|
|
|
* @param string $modele Force template to use ('' to not force) |
3225
|
|
|
* @param Translate $outputlangs Object lang a utiliser pour traduction |
3226
|
|
|
* @param int $hidedetails Hide details of lines |
3227
|
|
|
* @param int $hidedesc Hide description |
3228
|
|
|
* @param int $hideref Hide ref |
3229
|
|
|
* @param ?array<string,mixed> $moreparams Array to provide more information |
3230
|
|
|
* @return int<-1,1> Return integer <0 if KO, 0 if nothing done, >0 if OK |
3231
|
|
|
*/ |
3232
|
|
|
public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null) |
3233
|
|
|
{ |
3234
|
|
|
global $langs; |
3235
|
|
|
|
3236
|
|
|
$langs->load("suppliers"); |
3237
|
|
|
$outputlangs->load("products"); |
3238
|
|
|
|
3239
|
|
|
// Set the model on the model name to use |
3240
|
|
|
if (empty($modele)) { |
3241
|
|
|
if (getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF')) { |
3242
|
|
|
$modele = getDolGlobalString('INVOICE_SUPPLIER_ADDON_PDF'); |
3243
|
|
|
} else { |
3244
|
|
|
$modele = ''; // No default value. For supplier invoice, we allow to disable all PDF generation |
3245
|
|
|
} |
3246
|
|
|
} |
3247
|
|
|
|
3248
|
|
|
if (empty($modele)) { |
3249
|
|
|
return 0; |
3250
|
|
|
} else { |
3251
|
|
|
$modelpath = "core/modules/supplier_invoice/doc/"; |
3252
|
|
|
|
3253
|
|
|
return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams); |
3254
|
|
|
} |
3255
|
|
|
} |
3256
|
|
|
|
3257
|
|
|
/** |
3258
|
|
|
* Returns the rights used for this class |
3259
|
|
|
* @return int |
3260
|
|
|
*/ |
3261
|
|
|
public function getRights() |
3262
|
|
|
{ |
3263
|
|
|
global $user; |
3264
|
|
|
|
3265
|
|
|
return $user->hasRight("fournisseur", "facture"); |
3266
|
|
|
} |
3267
|
|
|
|
3268
|
|
|
/** |
3269
|
|
|
* Function used to replace a thirdparty id with another one. |
3270
|
|
|
* |
3271
|
|
|
* @param DoliDB $dbs Database handler, because function is static we name it $dbs not $db to avoid breaking coding test |
3272
|
|
|
* @param int $origin_id Old thirdparty id |
3273
|
|
|
* @param int $dest_id New thirdparty id |
3274
|
|
|
* @return bool |
3275
|
|
|
*/ |
3276
|
|
|
public static function replaceThirdparty(DoliDB $dbs, $origin_id, $dest_id) |
3277
|
|
|
{ |
3278
|
|
|
$tables = array( |
3279
|
|
|
'facture_fourn' |
3280
|
|
|
); |
3281
|
|
|
|
3282
|
|
|
return CommonObject::commonReplaceThirdparty($dbs, $origin_id, $dest_id, $tables); |
3283
|
|
|
} |
3284
|
|
|
|
3285
|
|
|
/** |
3286
|
|
|
* Function used to replace a product id with another one. |
3287
|
|
|
* |
3288
|
|
|
* @param DoliDB $db Database handler |
3289
|
|
|
* @param int $origin_id Old product id |
3290
|
|
|
* @param int $dest_id New product id |
3291
|
|
|
* @return bool |
3292
|
|
|
*/ |
3293
|
|
|
public static function replaceProduct(DoliDB $db, $origin_id, $dest_id) |
3294
|
|
|
{ |
3295
|
|
|
$tables = array( |
3296
|
|
|
'facture_fourn_det' |
3297
|
|
|
); |
3298
|
|
|
|
3299
|
|
|
return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables); |
3300
|
|
|
} |
3301
|
|
|
|
3302
|
|
|
/** |
3303
|
|
|
* Is the payment of the supplier invoice having a delay? |
3304
|
|
|
* |
3305
|
|
|
* @return bool |
3306
|
|
|
*/ |
3307
|
|
|
public function hasDelay() |
3308
|
|
|
{ |
3309
|
|
|
global $conf; |
3310
|
|
|
|
3311
|
|
|
$now = dol_now(); |
3312
|
|
|
|
3313
|
|
|
if (!$this->date_echeance) { |
3314
|
|
|
return false; |
3315
|
|
|
} |
3316
|
|
|
|
3317
|
|
|
$status = isset($this->status) ? $this->status : $this->statut; |
3318
|
|
|
|
3319
|
|
|
return ($status == self::STATUS_VALIDATED) && ($this->date_echeance < ($now - $conf->facture->fournisseur->warning_delay)); |
3320
|
|
|
} |
3321
|
|
|
|
3322
|
|
|
/** |
3323
|
|
|
* Is credit note used |
3324
|
|
|
* |
3325
|
|
|
* @return bool |
3326
|
|
|
*/ |
3327
|
|
|
public function isCreditNoteUsed() |
3328
|
|
|
{ |
3329
|
|
|
$isUsed = false; |
3330
|
|
|
|
3331
|
|
|
$sql = "SELECT fk_invoice_supplier FROM " . MAIN_DB_PREFIX . "societe_remise_except WHERE fk_invoice_supplier_source = " . ((int) $this->id); |
3332
|
|
|
$resql = $this->db->query($sql); |
3333
|
|
|
if (!empty($resql)) { |
3334
|
|
|
$obj = $this->db->fetch_object($resql); |
3335
|
|
|
if (!empty($obj->fk_invoice_supplier)) { |
3336
|
|
|
$isUsed = true; |
3337
|
|
|
} |
3338
|
|
|
} |
3339
|
|
|
|
3340
|
|
|
return $isUsed; |
3341
|
|
|
} |
3342
|
|
|
/** |
3343
|
|
|
* Return clicable link of object (with eventually picto) |
3344
|
|
|
* |
3345
|
|
|
* @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link) |
3346
|
|
|
* @param ?array{selected?:int<0,1>} $arraydata Array of data |
|
|
|
|
3347
|
|
|
* @return string HTML Code for Kanban thumb. |
3348
|
|
|
*/ |
3349
|
|
|
public function getKanbanView($option = '', $arraydata = null) |
3350
|
|
|
{ |
3351
|
|
|
global $langs; |
3352
|
|
|
|
3353
|
|
|
$selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']); |
3354
|
|
|
|
3355
|
|
|
$picto = $this->picto; |
3356
|
|
|
if ($this->type == self::TYPE_REPLACEMENT) { |
3357
|
|
|
$picto .= 'r'; // Replacement invoice |
3358
|
|
|
} |
3359
|
|
|
if ($this->type == self::TYPE_CREDIT_NOTE) { |
3360
|
|
|
$picto .= 'a'; // Credit note |
3361
|
|
|
} |
3362
|
|
|
if ($this->type == self::TYPE_DEPOSIT) { |
3363
|
|
|
$picto .= 'd'; // Deposit invoice |
3364
|
|
|
} |
3365
|
|
|
|
3366
|
|
|
$return = '<div class="box-flex-item box-flex-grow-zero">'; |
3367
|
|
|
$return .= '<div class="info-box info-box-sm">'; |
3368
|
|
|
$return .= '<span class="info-box-icon bg-infobox-action">'; |
3369
|
|
|
$return .= img_picto('', $picto); |
3370
|
|
|
$return .= '</span>'; |
3371
|
|
|
$return .= '<div class="info-box-content">'; |
3372
|
|
|
$return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref) . '</span>'; |
3373
|
|
|
if ($selected >= 0) { |
3374
|
|
|
$return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>'; |
3375
|
|
|
} |
3376
|
|
|
if (!empty($arraydata['thirdparty'])) { |
3377
|
|
|
$return .= '<br><span class="info-box-label">' . $arraydata['thirdparty'] . '</span>'; |
3378
|
|
|
} |
3379
|
|
|
if (property_exists($this, 'date')) { |
3380
|
|
|
$return .= '<br><span class="info-box-label">' . dol_print_date($this->date, 'day') . '</span>'; |
3381
|
|
|
} |
3382
|
|
|
if (property_exists($this, 'total_ht')) { |
3383
|
|
|
$return .= ' <span class="info-box-label amount" title="' . dol_escape_htmltag($langs->trans("AmountHT")) . '">' . price($this->total_ht); |
3384
|
|
|
$return .= ' ' . $langs->trans("HT"); |
3385
|
|
|
$return .= '</span>'; |
3386
|
|
|
} |
3387
|
|
|
if (method_exists($this, 'getLibStatut')) { |
3388
|
|
|
$alreadypaid = (empty($arraydata['alreadypaid']) ? 0 : $arraydata['alreadypaid']); |
3389
|
|
|
$return .= '<br><div class="info-box-status">' . $this->getLibStatut(3, $alreadypaid) . '</div>'; |
3390
|
|
|
} |
3391
|
|
|
$return .= '</div>'; |
3392
|
|
|
$return .= '</div>'; |
3393
|
|
|
$return .= '</div>'; |
3394
|
|
|
return $return; |
3395
|
|
|
} |
3396
|
|
|
|
3397
|
|
|
/** |
3398
|
|
|
* Change the option VAT reverse charge |
3399
|
|
|
* |
3400
|
|
|
* @param int $vatreversecharge 0 = Off, 1 = On |
3401
|
|
|
* @return int 1 if OK, 0 if KO |
3402
|
|
|
*/ |
3403
|
|
|
public function setVATReverseCharge($vatreversecharge) |
3404
|
|
|
{ |
3405
|
|
|
if (!$this->table_element) { |
3406
|
|
|
dol_syslog(get_only_class($this) . "::setVATReverseCharge was called on object with property table_element not defined", LOG_ERR); |
3407
|
|
|
return -1; |
3408
|
|
|
} |
3409
|
|
|
|
3410
|
|
|
dol_syslog(get_only_class($this) . '::setVATReverseCharge(' . $vatreversecharge . ')'); |
3411
|
|
|
|
3412
|
|
|
$sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element; |
3413
|
|
|
$sql .= " SET vat_reverse_charge = " . ((int) $vatreversecharge); |
3414
|
|
|
$sql .= " WHERE rowid=" . ((int) $this->id); |
3415
|
|
|
|
3416
|
|
|
if ($this->db->query($sql)) { |
3417
|
|
|
$this->vat_reverse_charge = ($vatreversecharge == 0) ? 0 : 1; |
3418
|
|
|
return 1; |
3419
|
|
|
} else { |
3420
|
|
|
dol_syslog(get_only_class($this) . '::setVATReverseCharge Error ', LOG_DEBUG); |
3421
|
|
|
$this->error = $this->db->error(); |
3422
|
|
|
return 0; |
3423
|
|
|
} |
3424
|
|
|
} |
3425
|
|
|
|
3426
|
|
|
/** |
3427
|
|
|
* Send reminders by emails for supplier invoices validated that are due. |
3428
|
|
|
* CAN BE A CRON TASK |
3429
|
|
|
* |
3430
|
|
|
* @param int $nbdays Delay before due date (or after if delay is negative) |
3431
|
|
|
* @param string $paymentmode '' or 'all' by default (no filter), or 'LIQ', 'CHQ', CB', ... |
3432
|
|
|
* @param int|string $template Name (or id) of email template (Must be a template of type 'invoice_supplier_send') |
3433
|
|
|
* @param string $datetouse 'duedate' (default) or 'invoicedate' |
3434
|
|
|
* @param string $forcerecipient Force email of recipient (for example to send the email to an accountant supervisor instead of the customer) |
3435
|
|
|
* @return int 0 if OK, <>0 if KO (this function is used also by cron so only 0 is OK) |
3436
|
|
|
*/ |
3437
|
|
|
public function sendEmailsRemindersOnSupplierInvoiceDueDate($nbdays = 0, $paymentmode = 'all', $template = '', $datetouse = 'duedate', $forcerecipient = '') |
3438
|
|
|
{ |
3439
|
|
|
global $conf, $langs, $user; |
3440
|
|
|
|
3441
|
|
|
$this->output = ''; |
3442
|
|
|
$this->error = ''; |
3443
|
|
|
$nbMailSend = 0; |
3444
|
|
|
|
3445
|
|
|
$error = 0; |
3446
|
|
|
$errorsMsg = array(); |
3447
|
|
|
|
3448
|
|
|
$langs->load('bills'); |
3449
|
|
|
|
3450
|
|
|
if (!isModEnabled(empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) ? 'fournisseur' : 'supplier_invoice')) { // Should not happen. If module disabled, cron job should not be visible. |
3451
|
|
|
$this->output .= $langs->trans('ModuleNotEnabled', $langs->transnoentitiesnoconv('Suppliers')); |
3452
|
|
|
return 0; |
3453
|
|
|
} |
3454
|
|
|
if (!in_array($datetouse, array('duedate', 'invoicedate'))) { |
3455
|
|
|
$this->output .= 'Bad value for parameter datetouse. Must be "duedate" or "invoicedate"'; |
3456
|
|
|
return 0; |
3457
|
|
|
} |
3458
|
|
|
|
3459
|
|
|
require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/date.lib.php'; |
3460
|
|
|
$formmail = new FormMail($this->db); |
3461
|
|
|
|
3462
|
|
|
$now = dol_now(); |
3463
|
|
|
$tmpidate = dol_get_first_hour(dol_time_plus_duree($now, $nbdays, 'd'), 'gmt'); |
3464
|
|
|
|
3465
|
|
|
$tmpinvoice = new FactureFournisseur($this->db); |
3466
|
|
|
|
3467
|
|
|
dol_syslog(__METHOD__ . " start", LOG_INFO); |
3468
|
|
|
|
3469
|
|
|
// Select all action comm reminder |
3470
|
|
|
$sql = "SELECT rowid as id FROM " . MAIN_DB_PREFIX . "facture_fourn as f"; |
3471
|
|
|
if (!empty($paymentmode) && $paymentmode != 'all') { |
3472
|
|
|
$sql .= ", " . MAIN_DB_PREFIX . "c_paiement as cp"; |
3473
|
|
|
} |
3474
|
|
|
$sql .= " WHERE f.paye = 0"; // Only unpaid |
3475
|
|
|
$sql .= " AND f.fk_statut = " . self::STATUS_VALIDATED; // Only validated status |
3476
|
|
|
if ($datetouse == 'invoicedate') { |
3477
|
|
|
$sql .= " AND f.datef = '" . $this->db->idate($tmpidate, 'gmt') . "'"; |
3478
|
|
|
} else { |
3479
|
|
|
$sql .= " AND f.date_lim_reglement = '" . $this->db->idate($tmpidate, 'gmt') . "'"; |
3480
|
|
|
} |
3481
|
|
|
$sql .= " AND f.entity IN (" . getEntity('supplier_invoice', 0) . ")"; // One batch process only one company (no sharing) |
3482
|
|
|
if (!empty($paymentmode) && $paymentmode != 'all') { |
3483
|
|
|
$sql .= " AND f.fk_mode_reglement = cp.id AND cp.code = '" . $this->db->escape($paymentmode) . "'"; |
3484
|
|
|
} |
3485
|
|
|
// TODO Add a filter to check there is no payment started yet |
3486
|
|
|
if ($datetouse == 'invoicedate') { |
3487
|
|
|
$sql .= $this->db->order("datef", "ASC"); |
3488
|
|
|
} else { |
3489
|
|
|
$sql .= $this->db->order("date_lim_reglement", "ASC"); |
3490
|
|
|
} |
3491
|
|
|
|
3492
|
|
|
$resql = $this->db->query($sql); |
3493
|
|
|
|
3494
|
|
|
$stmpidate = dol_print_date($tmpidate, 'day', 'gmt'); |
3495
|
|
|
if ($datetouse == 'invoicedate') { |
3496
|
|
|
$this->output .= $langs->transnoentitiesnoconv("SearchValidatedSupplierInvoicesWithDate", $stmpidate); |
3497
|
|
|
} else { |
3498
|
|
|
$this->output .= $langs->transnoentitiesnoconv("SearchUnpaidSupplierInvoicesWithDueDate", $stmpidate); |
3499
|
|
|
} |
3500
|
|
|
if (!empty($paymentmode) && $paymentmode != 'all') { |
3501
|
|
|
$this->output .= ' (' . $langs->transnoentitiesnoconv("PaymentMode") . ' ' . $paymentmode . ')'; |
3502
|
|
|
} |
3503
|
|
|
$this->output .= '<br>'; |
3504
|
|
|
|
3505
|
|
|
if ($resql) { |
3506
|
|
|
while ($obj = $this->db->fetch_object($resql)) { |
3507
|
|
|
if (!$error) { |
3508
|
|
|
// Load event |
3509
|
|
|
$res = $tmpinvoice->fetch($obj->id); |
3510
|
|
|
if ($res > 0) { |
3511
|
|
|
$tmpinvoice->fetch_thirdparty(); |
3512
|
|
|
|
3513
|
|
|
$outputlangs = new Translate('', $conf); |
3514
|
|
|
if ($tmpinvoice->thirdparty->default_lang) { |
3515
|
|
|
$outputlangs->setDefaultLang($tmpinvoice->thirdparty->default_lang); |
3516
|
|
|
$outputlangs->loadLangs(array("main", "suppliers")); |
3517
|
|
|
} else { |
3518
|
|
|
$outputlangs = $langs; |
3519
|
|
|
} |
3520
|
|
|
|
3521
|
|
|
// Select email template according to language of recipient |
3522
|
|
|
$templateId = 0; |
3523
|
|
|
$templateLabel = ''; |
3524
|
|
|
if (empty($template) || $template == 'EmailTemplateCode') { |
3525
|
|
|
$templateLabel = '(SendingReminderEmailOnUnpaidSupplierInvoice)'; |
3526
|
|
|
} else { |
3527
|
|
|
if (is_numeric($template)) { |
3528
|
|
|
$templateId = $template; |
3529
|
|
|
} else { |
3530
|
|
|
$templateLabel = $template; |
3531
|
|
|
} |
3532
|
|
|
} |
3533
|
|
|
|
3534
|
|
|
$arraymessage = $formmail->getEMailTemplate($this->db, 'invoice_supplier_send', $user, $outputlangs, $templateId, 1, $templateLabel); |
3535
|
|
|
if (is_numeric($arraymessage) && $arraymessage <= 0) { |
3536
|
|
|
$langs->load("errors"); |
3537
|
|
|
$this->output .= $langs->trans('ErrorFailedToFindEmailTemplate', $template); |
3538
|
|
|
return 0; |
3539
|
|
|
} |
3540
|
|
|
|
3541
|
|
|
// PREPARE EMAIL |
3542
|
|
|
$errormesg = ''; |
3543
|
|
|
|
3544
|
|
|
// Make substitution in email content |
3545
|
|
|
$substitutionarray = getCommonSubstitutionArray($outputlangs, 0, '', $tmpinvoice); |
3546
|
|
|
|
3547
|
|
|
complete_substitutions_array($substitutionarray, $outputlangs, $tmpinvoice); |
3548
|
|
|
|
3549
|
|
|
// Topic |
3550
|
|
|
$sendTopic = make_substitutions(empty($arraymessage->topic) ? $outputlangs->transnoentitiesnoconv('InformationMessage') : $arraymessage->topic, $substitutionarray, $outputlangs, 1); |
3551
|
|
|
|
3552
|
|
|
// Content |
3553
|
|
|
$content = $outputlangs->transnoentitiesnoconv($arraymessage->content); |
3554
|
|
|
|
3555
|
|
|
$sendContent = make_substitutions($content, $substitutionarray, $outputlangs, 1); |
3556
|
|
|
|
3557
|
|
|
// Recipient |
3558
|
|
|
$to = array(); |
3559
|
|
|
if ($forcerecipient) { // If a recipient was forced |
3560
|
|
|
$to = array($forcerecipient); |
3561
|
|
|
} else { |
3562
|
|
|
$res = $tmpinvoice->fetch_thirdparty(); |
3563
|
|
|
$recipient = $tmpinvoice->thirdparty; |
3564
|
|
|
if ($res > 0) { |
3565
|
|
|
$tmparraycontact = $tmpinvoice->liste_contact(-1, 'internal', 0, 'SALESREPFOLL'); |
3566
|
|
|
if (is_array($tmparraycontact) && count($tmparraycontact) > 0) { |
3567
|
|
|
foreach ($tmparraycontact as $data_email) { |
3568
|
|
|
if (!empty($data_email['email'])) { |
3569
|
|
|
$to[] = $data_email['email']; |
3570
|
|
|
} |
3571
|
|
|
} |
3572
|
|
|
} |
3573
|
|
|
if (empty($to) && !empty($recipient->email)) { |
3574
|
|
|
$to[] = $recipient->email; |
3575
|
|
|
} |
3576
|
|
|
if (empty($to)) { |
3577
|
|
|
$errormesg = "Failed to send remind to thirdparty id=" . $tmpinvoice->socid . ". No email defined for supplier invoice or customer."; |
3578
|
|
|
$error++; |
3579
|
|
|
} |
3580
|
|
|
} else { |
3581
|
|
|
$errormesg = "Failed to load recipient with thirdparty id=" . $tmpinvoice->socid; |
3582
|
|
|
$error++; |
3583
|
|
|
} |
3584
|
|
|
} |
3585
|
|
|
|
3586
|
|
|
// Sender |
3587
|
|
|
$from = getDolGlobalString('MAIN_MAIL_EMAIL_FROM'); |
3588
|
|
|
if (!empty($arraymessage->email_from)) { // If a sender is defined into template, we use it in priority |
3589
|
|
|
$from = $arraymessage->email_from; |
3590
|
|
|
} |
3591
|
|
|
if (empty($from)) { |
3592
|
|
|
$errormesg = "Failed to get sender into global setup MAIN_MAIL_EMAIL_FROM"; |
3593
|
|
|
$error++; |
3594
|
|
|
} |
3595
|
|
|
|
3596
|
|
|
if (!$error && !empty($to)) { |
3597
|
|
|
$this->db->begin(); |
3598
|
|
|
|
3599
|
|
|
$to = implode(',', $to); |
3600
|
|
|
if (!empty($arraymessage->email_to)) { // If a recipient is defined into template, we add it |
3601
|
|
|
$to = $to . ',' . $arraymessage->email_to; |
3602
|
|
|
} |
3603
|
|
|
|
3604
|
|
|
// Errors Recipient |
3605
|
|
|
$errors_to = $conf->global->MAIN_MAIL_ERRORS_TO; |
3606
|
|
|
|
3607
|
|
|
$trackid = 'inv' . $tmpinvoice->id; |
3608
|
|
|
$sendcontext = 'standard'; |
3609
|
|
|
|
3610
|
|
|
$email_tocc = ''; |
3611
|
|
|
if (!empty($arraymessage->email_tocc)) { // If a CC is defined into template, we use it |
3612
|
|
|
$email_tocc = $arraymessage->email_tocc; |
3613
|
|
|
} |
3614
|
|
|
|
3615
|
|
|
$email_tobcc = ''; |
3616
|
|
|
if (!empty($arraymessage->email_tobcc)) { // If a BCC is defined into template, we use it |
3617
|
|
|
$email_tobcc = $arraymessage->email_tobcc; |
3618
|
|
|
} |
3619
|
|
|
|
3620
|
|
|
// Mail Creation |
3621
|
|
|
$cMailFile = new CMailFile($sendTopic, $to, $from, $sendContent, array(), array(), array(), $email_tocc, $email_tobcc, 0, 1, $errors_to, '', $trackid, '', $sendcontext, ''); |
3622
|
|
|
|
3623
|
|
|
// Sending Mail |
3624
|
|
|
if ($cMailFile->sendfile()) { |
3625
|
|
|
$nbMailSend++; |
3626
|
|
|
|
3627
|
|
|
// Add a line into event table |
3628
|
|
|
|
3629
|
|
|
// Insert record of emails sent |
3630
|
|
|
$actioncomm = new ActionComm($this->db); |
3631
|
|
|
|
3632
|
|
|
$actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically |
3633
|
|
|
$actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company |
3634
|
|
|
$actioncomm->contact_id = 0; |
3635
|
|
|
|
3636
|
|
|
$actioncomm->code = 'AC_EMAIL'; |
3637
|
|
|
$actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateOK (nbdays=' . $nbdays . ' paymentmode=' . $paymentmode . ' template=' . $template . ' datetouse=' . $datetouse . ' forcerecipient=' . $forcerecipient . ')'; |
3638
|
|
|
$actioncomm->note_private = $sendContent; |
3639
|
|
|
$actioncomm->fk_project = $tmpinvoice->fk_project; |
3640
|
|
|
$actioncomm->datep = dol_now(); |
3641
|
|
|
$actioncomm->datef = $actioncomm->datep; |
3642
|
|
|
$actioncomm->percentage = -1; // Not applicable |
3643
|
|
|
$actioncomm->authorid = $user->id; // User saving action |
3644
|
|
|
$actioncomm->userownerid = $user->id; // Owner of action |
3645
|
|
|
// Fields when action is an email (content should be added into note) |
3646
|
|
|
$actioncomm->email_msgid = $cMailFile->msgid; |
3647
|
|
|
$actioncomm->email_subject = $sendTopic; |
3648
|
|
|
$actioncomm->email_from = $from; |
3649
|
|
|
$actioncomm->email_sender = ''; |
3650
|
|
|
$actioncomm->email_to = $to; |
3651
|
|
|
//$actioncomm->email_tocc = $sendtocc; |
3652
|
|
|
//$actioncomm->email_tobcc = $sendtobcc; |
3653
|
|
|
//$actioncomm->email_subject = $subject; |
3654
|
|
|
$actioncomm->errors_to = $errors_to; |
3655
|
|
|
|
3656
|
|
|
$actioncomm->elementtype = 'invoice_supplier'; |
3657
|
|
|
$actioncomm->fk_element = $tmpinvoice->id; |
3658
|
|
|
|
3659
|
|
|
//$actioncomm->extraparams = $extraparams; |
3660
|
|
|
|
3661
|
|
|
$actioncomm->create($user); |
3662
|
|
|
} else { |
3663
|
|
|
$errormesg = $cMailFile->error . ' : ' . $to; |
3664
|
|
|
$error++; |
3665
|
|
|
|
3666
|
|
|
// Add a line into event table |
3667
|
|
|
|
3668
|
|
|
// Insert record of emails sent |
3669
|
|
|
$actioncomm = new ActionComm($this->db); |
3670
|
|
|
|
3671
|
|
|
$actioncomm->type_code = 'AC_OTH_AUTO'; // Event insert into agenda automatically |
3672
|
|
|
$actioncomm->socid = $tmpinvoice->thirdparty->id; // To link to a company |
3673
|
|
|
$actioncomm->contact_id = 0; |
3674
|
|
|
|
3675
|
|
|
$actioncomm->code = 'AC_EMAIL'; |
3676
|
|
|
$actioncomm->label = 'sendEmailsRemindersOnInvoiceDueDateKO'; |
3677
|
|
|
$actioncomm->note_private = $errormesg; |
3678
|
|
|
$actioncomm->fk_project = $tmpinvoice->fk_project; |
3679
|
|
|
$actioncomm->datep = dol_now(); |
3680
|
|
|
$actioncomm->datef = $actioncomm->datep; |
3681
|
|
|
$actioncomm->percentage = -1; // Not applicable |
3682
|
|
|
$actioncomm->authorid = $user->id; // User saving action |
3683
|
|
|
$actioncomm->userownerid = $user->id; // Owner of action |
3684
|
|
|
// Fields when action is an email (content should be added into note) |
3685
|
|
|
$actioncomm->email_msgid = $cMailFile->msgid; |
3686
|
|
|
$actioncomm->email_from = $from; |
3687
|
|
|
$actioncomm->email_sender = ''; |
3688
|
|
|
$actioncomm->email_to = $to; |
3689
|
|
|
//$actioncomm->email_tocc = $sendtocc; |
3690
|
|
|
//$actioncomm->email_tobcc = $sendtobcc; |
3691
|
|
|
//$actioncomm->email_subject = $subject; |
3692
|
|
|
$actioncomm->errors_to = $errors_to; |
3693
|
|
|
|
3694
|
|
|
//$actioncomm->extraparams = $extraparams; |
3695
|
|
|
|
3696
|
|
|
$actioncomm->create($user); |
3697
|
|
|
} |
3698
|
|
|
|
3699
|
|
|
$this->db->commit(); // We always commit |
3700
|
|
|
} |
3701
|
|
|
|
3702
|
|
|
if ($errormesg) { |
3703
|
|
|
$errorsMsg[] = $errormesg; |
3704
|
|
|
} |
3705
|
|
|
} else { |
3706
|
|
|
$errorsMsg[] = 'Failed to fetch record invoice with ID = ' . $obj->id; |
3707
|
|
|
$error++; |
3708
|
|
|
} |
3709
|
|
|
} |
3710
|
|
|
} |
3711
|
|
|
} else { |
3712
|
|
|
$error++; |
3713
|
|
|
} |
3714
|
|
|
|
3715
|
|
|
if (!$error) { |
3716
|
|
|
$this->output .= 'Nb of emails sent : ' . $nbMailSend; |
3717
|
|
|
|
3718
|
|
|
dol_syslog(__METHOD__ . " end - " . $this->output, LOG_INFO); |
3719
|
|
|
|
3720
|
|
|
return 0; |
3721
|
|
|
} else { |
3722
|
|
|
$this->error = 'Nb of emails sent : ' . $nbMailSend . ', ' . (empty($errorsMsg) ? $error : implode(', ', $errorsMsg)); |
3723
|
|
|
|
3724
|
|
|
dol_syslog(__METHOD__ . " end - " . $this->error, LOG_INFO); |
3725
|
|
|
|
3726
|
|
|
return $error; |
3727
|
|
|
} |
3728
|
|
|
} |
3729
|
|
|
} |
3730
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.