Passed
Push — EXTRACT_CLASSES ( c25e41...9f3ede )
by Rafael
55:18
created

ExpenseReport::addline()   F

Complexity

Conditions 15
Paths 769

Size

Total Lines 102
Code Lines 73

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 73
nc 769
nop 10
dl 0
loc 102
rs 2.2456
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* Copyright (C) 2011       Dimitri Mouillard           <[email protected]>
4
 * Copyright (C) 2015 		Laurent Destailleur 	    <[email protected]>
5
 * Copyright (C) 2015 		Alexandre Spangaro  	    <[email protected]>
6
 * Copyright (C) 2018       Nicolas ZABOURI             <[email protected]>
7
 * Copyright (c) 2018-2024  Frédéric France             <[email protected]>
8
 * Copyright (C) 2016-2020 	Ferran Marcet       	    <[email protected]>
9
 * Copyright (C) 2024		MDW							<[email protected]>
10
 * Copyright (C) 2024       Frédéric France             <[email protected]>
11
 * Copyright (C) 2024       Rafael San José             <[email protected]>
12
 *
13
 * This program is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 3 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
25
 */
26
27
namespace Dolibarr\Code\ExpenseReport\Classes;
28
29
use Dolibarr\Core\Base\CommonObject;
30
31
/**
32
 *       \file       htdocs/expensereport/class/expensereport.class.php
33
 *       \ingroup    expensereport
34
 *       \brief      File to manage Expense Reports
35
 */
36
37
require_once constant('DOL_DOCUMENT_ROOT') . '/expensereport/class/expensereport_ik.class.php';
38
require_once constant('DOL_DOCUMENT_ROOT') . '/expensereport/class/expensereport_rule.class.php';
39
40
41
/**
42
 * Class to manage Trips and Expenses
43
 */
44
class ExpenseReport extends CommonObject
45
{
46
    /**
47
     * @var string ID to identify managed object
48
     */
49
    public $element = 'expensereport';
50
51
    /**
52
     * @var string Name of table without prefix where object is stored
53
     */
54
    public $table_element = 'expensereport';
55
56
    /**
57
     * @var string table element line name
58
     */
59
    public $table_element_line = 'expensereport_det';
60
61
    /**
62
     * @var string Fieldname with ID of parent key if this field has a parent
63
     */
64
    public $fk_element = 'fk_expensereport';
65
66
    /**
67
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
68
     */
69
    public $picto = 'trip';
70
71
    /**
72
     * @var ExpenseReportLine[] array of expensereport lines
73
     */
74
    public $lines = array();
75
76
    /**
77
     * @var ExpenseReportLine expensereport lines
78
     */
79
    public $line;
80
81
    /**
82
     * @var int|string
83
     */
84
    public $date_debut;
85
86
    /**
87
     * @var int|string
88
     */
89
    public $date_fin;
90
91
    /**
92
     * @var int|string
93
     */
94
    public $date_approbation;
95
96
    /**
97
     * @var int ID
98
     */
99
    public $fk_user;
100
101
    /**
102
     * @var int ID
103
     */
104
    public $user_approve_id;
105
106
    /**
107
     * 0=draft, 2=validated (attente approb), 4=canceled, 5=approved, 6=paid, 99=denied
108
     *
109
     * @var int     Status
110
     */
111
    public $status;
112
113
    /**
114
     * 0=draft, 2=validated (attente approb), 4=canceled, 5=approved, 6=paid, 99=denied
115
     *
116
     * @var int     Status
117
     * @deprecated
118
     */
119
    public $fk_statut;
120
121
    /**
122
     * @var int ID
123
     */
124
    public $fk_c_paiement;
125
126
    /**
127
     * @var int ID
128
     */
129
    public $modepaymentid;
130
131
    public $paid;
132
133
    // Paiement
134
    /**
135
     * @var string Firstname Lastname
136
     */
137
    public $user_paid_infos;
138
139
    /**
140
     * @var string Firstname Lastname
141
     */
142
    public $user_author_infos;
143
144
    /**
145
     * @var string Firstname Lastname
146
     */
147
    public $user_validator_infos;
148
149
    public $rule_warning_message;
150
151
    // ACTIONS
152
153
    // Create
154
    /**
155
     * @var int|string
156
     */
157
    public $date_create;
158
159
    /**
160
     * @var int ID of user creator
161
     */
162
    public $fk_user_creat;
163
164
    /**
165
     * @var int ID of user who reclaim expense report
166
     */
167
    public $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
168
169
    // Update
170
    /**
171
     * @var int|string
172
     */
173
    public $date_modif;
174
175
    /**
176
     * @var int ID
177
     */
178
    public $fk_user_modif;
179
180
    // Refus
181
    /**
182
     * @var int|string
183
     */
184
    public $date_refuse;
185
186
    /**
187
     * @var string
188
     */
189
    public $detail_refuse;
190
191
    /**
192
     * @var int ID
193
     */
194
    public $fk_user_refuse;
195
196
    // Annulation
197
    /**
198
     * @var int|string
199
     */
200
    public $date_cancel;
201
202
    /**
203
     * @var string
204
     */
205
    public $detail_cancel;
206
207
    /**
208
     * @var int ID of user who cancel expense report
209
     */
210
    public $fk_user_cancel;
211
212
    /**
213
     * @var int User that is defined to approve
214
     */
215
    public $fk_user_validator;
216
217
    /**
218
     * Validation date
219
     * @var int
220
     * @deprecated
221
     * @see $date_valid
222
     */
223
    public $datevalid;
224
225
    /**
226
     * Validation date
227
     * @var int
228
     */
229
    public $date_valid;
230
231
    /**
232
     * @var int ID of User making validation
233
     */
234
    public $fk_user_valid;
235
236
    /**
237
     * @var string Firstname Lastname
238
     */
239
    public $user_valid_infos;
240
241
    // Approve
242
    /**
243
     * @var int|string
244
     */
245
    public $date_approve;
246
247
    /**
248
     * @var int ID User that has approved
249
     */
250
    public $fk_user_approve;
251
252
    public $localtax1;  // for backward compatibility (real field should be total_localtax1 defined into CommonObject)
253
    public $localtax2;  // for backward compatibility (real field should be total_localtax2 defined into CommonObject)
254
255
    /**
256
     * Draft status
257
     */
258
    const STATUS_DRAFT = 0;
259
260
    /**
261
     * Validated (need to be paid)
262
     */
263
    const STATUS_VALIDATED = 2;
264
265
    /**
266
     * Classified canceled
267
     */
268
    const STATUS_CANCELED = 4;
269
270
    /**
271
     * Classified approved
272
     */
273
    const STATUS_APPROVED = 5;
274
275
    /**
276
     * Classified paid.
277
     */
278
    const STATUS_CLOSED = 6;
279
280
    /**
281
     * Classified refused
282
     */
283
    const STATUS_REFUSED = 99;
284
285
    public $fields = array(
286
        'rowid' => array('type' => 'integer', 'label' => 'ID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
287
        'ref' => array('type' => 'varchar(50)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15),
288
        'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 20),
289
        'ref_number_int' => array('type' => 'integer', 'label' => 'Ref number int', 'enabled' => 1, 'visible' => -1, 'position' => 25),
290
        'ref_ext' => array('type' => 'integer', 'label' => 'Ref ext', 'enabled' => 1, 'visible' => -1, 'position' => 30),
291
        'total_ht' => array('type' => 'double(24,8)', 'label' => 'Total ht', 'enabled' => 1, 'visible' => -1, 'position' => 35),
292
        'total_tva' => array('type' => 'double(24,8)', 'label' => 'Total tva', 'enabled' => 1, 'visible' => -1, 'position' => 40),
293
        'localtax1' => array('type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 45),
294
        'localtax2' => array('type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 50),
295
        'total_ttc' => array('type' => 'double(24,8)', 'label' => 'Total ttc', 'enabled' => 1, 'visible' => -1, 'position' => 55),
296
        'date_debut' => array('type' => 'date', 'label' => 'Date debut', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 60),
297
        'date_fin' => array('type' => 'date', 'label' => 'Date fin', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 65),
298
        'date_valid' => array('type' => 'datetime', 'label' => 'Date valid', 'enabled' => 1, 'visible' => -1, 'position' => 75),
299
        'date_approve' => array('type' => 'datetime', 'label' => 'Date approve', 'enabled' => 1, 'visible' => -1, 'position' => 80),
300
        'date_refuse' => array('type' => 'datetime', 'label' => 'Date refuse', 'enabled' => 1, 'visible' => -1, 'position' => 85),
301
        'date_cancel' => array('type' => 'datetime', 'label' => 'Date cancel', 'enabled' => 1, 'visible' => -1, 'position' => 90),
302
        'fk_user_author' => array('type' => 'integer', 'label' => 'Fk user author', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 100),
303
        'fk_user_modif' => array('type' => 'integer', 'label' => 'Fk user modif', 'enabled' => 1, 'visible' => -1, 'position' => 105),
304
        'fk_user_valid' => array('type' => 'integer', 'label' => 'Fk user valid', 'enabled' => 1, 'visible' => -1, 'position' => 110),
305
        'fk_user_validator' => array('type' => 'integer', 'label' => 'Fk user validator', 'enabled' => 1, 'visible' => -1, 'position' => 115),
306
        'fk_user_approve' => array('type' => 'integer', 'label' => 'Fk user approve', 'enabled' => 1, 'visible' => -1, 'position' => 120),
307
        'fk_user_refuse' => array('type' => 'integer', 'label' => 'Fk user refuse', 'enabled' => 1, 'visible' => -1, 'position' => 125),
308
        'fk_user_cancel' => array('type' => 'integer', 'label' => 'Fk user cancel', 'enabled' => 1, 'visible' => -1, 'position' => 130),
309
        'fk_c_paiement' => array('type' => 'integer', 'label' => 'Fk c paiement', 'enabled' => 1, 'visible' => -1, 'position' => 140),
310
        'paid' => array('type' => 'integer', 'label' => 'Paid', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 145),
311
        'note_public' => array('type' => 'html', 'label' => 'Note public', 'enabled' => 1, 'visible' => 0, 'position' => 150),
312
        'note_private' => array('type' => 'html', 'label' => 'Note private', 'enabled' => 1, 'visible' => 0, 'position' => 155),
313
        'detail_refuse' => array('type' => 'varchar(255)', 'label' => 'Detail refuse', 'enabled' => 1, 'visible' => -1, 'position' => 160),
314
        'detail_cancel' => array('type' => 'varchar(255)', 'label' => 'Detail cancel', 'enabled' => 1, 'visible' => -1, 'position' => 165),
315
        'integration_compta' => array('type' => 'integer', 'label' => 'Integration compta', 'enabled' => 1, 'visible' => -1, 'position' => 170),
316
        'fk_bank_account' => array('type' => 'integer', 'label' => 'Fk bank account', 'enabled' => 1, 'visible' => -1, 'position' => 175),
317
        'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 1, 'visible' => -1, 'position' => 185),
318
        'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'Multicurrency code', 'enabled' => 1, 'visible' => -1, 'position' => 190),
319
        'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'Multicurrency tx', 'enabled' => 1, 'visible' => -1, 'position' => 195),
320
        'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total ht', 'enabled' => 1, 'visible' => -1, 'position' => 200),
321
        'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total tva', 'enabled' => 1, 'visible' => -1, 'position' => 205),
322
        'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total ttc', 'enabled' => 1, 'visible' => -1, 'position' => 210),
323
        'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 220),
324
        'date_create' => array('type' => 'datetime', 'label' => 'Date create', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 300),
325
        'tms' => array('type' => 'timestamp', 'label' => 'Tms', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 305),
326
        'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -1, 'position' => 1000),
327
        'model_pdf' => array('type' => 'varchar(255)', 'label' => 'Model pdf', 'enabled' => 1, 'visible' => 0, 'position' => 1010),
328
        'fk_statut' => array('type' => 'integer', 'label' => 'Fk statut', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
329
    );
330
331
    /**
332
     *  Constructor
333
     *
334
     *  @param  DoliDB  $db     Handler access base de donnees
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\ExpenseReport\Classes\DoliDB was not found. Did you mean DoliDB? If so, make sure to prefix the type with \.
Loading history...
335
     */
336
    public function __construct($db)
337
    {
338
        $this->db = $db;
339
        $this->total_ht = 0;
340
        $this->total_ttc = 0;
341
        $this->total_tva = 0;
342
        $this->total_localtax1 = 0;
343
        $this->total_localtax2 = 0;
344
        $this->localtax1 = 0;   // For backward compatibility
345
        $this->localtax2 = 0;   // For backward compatibility
346
        $this->modepaymentid = 0;
347
348
        // List of language codes for status
349
        $this->labelStatusShort = array(0 => 'Draft', 2 => 'Validated', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
350
        $this->labelStatus = array(0 => 'Draft', 2 => 'ValidatedWaitingApproval', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
351
    }
352
353
    /**
354
     * Create object in database
355
     *
356
     * @param   User    $user   User that create
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\ExpenseReport\Classes\User was not found. Did you mean User? If so, make sure to prefix the type with \.
Loading history...
357
     * @param   int     $notrigger   Disable triggers
358
     * @return  int             Return integer <0 if KO, >0 if OK
359
     */
360
    public function create($user, $notrigger = 0)
361
    {
362
        global $conf, $langs;
363
364
        $now = dol_now();
365
366
        $error = 0;
367
368
        // Check parameters
369
        if (empty($this->date_debut) || empty($this->date_fin)) {
370
            $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Date'));
371
            return -1;
372
        }
373
374
        $fuserid = $this->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
375
        if (empty($fuserid)) {
376
            $fuserid = $user->id;
377
        }
378
379
        $this->db->begin();
380
381
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . $this->table_element . " (";
382
        $sql .= "ref";
383
        $sql .= ",total_ht";
384
        $sql .= ",total_ttc";
385
        $sql .= ",total_tva";
386
        $sql .= ",date_debut";
387
        $sql .= ",date_fin";
388
        $sql .= ",date_create";
389
        $sql .= ",fk_user_creat";
390
        $sql .= ",fk_user_author";
391
        $sql .= ",fk_user_validator";
392
        $sql .= ",fk_user_approve";
393
        $sql .= ",fk_user_modif";
394
        $sql .= ",fk_statut";
395
        $sql .= ",fk_c_paiement";
396
        $sql .= ",paid";
397
        $sql .= ",note_public";
398
        $sql .= ",note_private";
399
        $sql .= ",entity";
400
        $sql .= ") VALUES(";
401
        $sql .= "'(PROV)'";
402
        $sql .= ", " . price2num($this->total_ht, 'MT');
403
        $sql .= ", " . price2num($this->total_ttc, 'MT');
404
        $sql .= ", " . price2num($this->total_tva, 'MT');
405
        $sql .= ", '" . $this->db->idate($this->date_debut) . "'";
406
        $sql .= ", '" . $this->db->idate($this->date_fin) . "'";
407
        $sql .= ", '" . $this->db->idate($now) . "'";
408
        $sql .= ", " . ((int) $user->id);
409
        $sql .= ", " . ((int) $fuserid);
410
        $sql .= ", " . ($this->fk_user_validator > 0 ? ((int) $this->fk_user_validator) : "null");
411
        $sql .= ", " . ($this->fk_user_approve > 0 ? ((int) $this->fk_user_approve) : "null");
412
        $sql .= ", " . ($this->fk_user_modif > 0 ? ((int) $this->fk_user_modif) : "null");
413
        $sql .= ", " . ($this->fk_statut > 1 ? ((int) $this->fk_statut) : 0);
414
        $sql .= ", " . ($this->modepaymentid ? ((int) $this->modepaymentid) : "null");
415
        $sql .= ", 0";
416
        $sql .= ", " . ($this->note_public ? "'" . $this->db->escape($this->note_public) . "'" : "null");
417
        $sql .= ", " . ($this->note_private ? "'" . $this->db->escape($this->note_private) . "'" : "null");
418
        $sql .= ", " . ((int) $conf->entity);
419
        $sql .= ")";
420
421
        $result = $this->db->query($sql);
422
        if ($result) {
423
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
424
            $this->ref = '(PROV' . $this->id . ')';
425
426
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . " SET ref='" . $this->db->escape($this->ref) . "' WHERE rowid=" . ((int) $this->id);
427
            $resql = $this->db->query($sql);
428
            if (!$resql) {
429
                $this->error = $this->db->lasterror();
430
                $error++;
431
            }
432
433
            if (!$error) {
434
                if (is_array($this->lines) && count($this->lines) > 0) {
435
                    foreach ($this->lines as $line) {
436
                        // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
437
                        //if (! is_object($line)) $line=json_decode(json_encode($line), false);  // convert recursively array into object.
438
                        if (!is_object($line)) {
439
                            $line = (object) $line;
440
                            $newndfline = new ExpenseReportLine($this->db);
441
                            $newndfline->fk_expensereport = $line->fk_expensereport;
442
                            $newndfline->fk_c_type_fees = $line->fk_c_type_fees;
443
                            $newndfline->fk_project = $line->fk_project;
444
                            $newndfline->vatrate = $line->vatrate;
445
                            $newndfline->vat_src_code = $line->vat_src_code;
446
                            $newndfline->localtax1_tx = $line->localtax1_tx;
447
                            $newndfline->localtax2_tx = $line->localtax2_tx;
448
                            $newndfline->localtax1_type = $line->localtax1_type;
449
                            $newndfline->localtax2_type = $line->localtax2_type;
450
                            $newndfline->comments = $line->comments;
451
                            $newndfline->qty = $line->qty;
452
                            $newndfline->value_unit = $line->value_unit;
453
                            $newndfline->total_ht = $line->total_ht;
454
                            $newndfline->total_ttc = $line->total_ttc;
455
                            $newndfline->total_tva = $line->total_tva;
456
                            $newndfline->total_localtax1 = $line->total_localtax1;
457
                            $newndfline->total_localtax2 = $line->total_localtax2;
458
                            $newndfline->date = $line->date;
459
                            $newndfline->rule_warning_message = $line->rule_warning_message;
460
                            $newndfline->fk_c_exp_tax_cat = $line->fk_c_exp_tax_cat;
461
                            $newndfline->fk_ecm_files = $line->fk_ecm_files;
462
                        } else {
463
                            $newndfline = $line;
464
                        }
465
                        //$newndfline=new ExpenseReportLine($this->db);
466
                        $newndfline->fk_expensereport = $this->id;
467
                        $result = $newndfline->insert();
468
                        if ($result < 0) {
469
                            $this->error = $newndfline->error;
470
                            $this->errors = $newndfline->errors;
471
                            $error++;
472
                            break;
473
                        }
474
                    }
475
                }
476
            }
477
478
            if (!$error) {
479
                $result = $this->insertExtraFields();
480
                if ($result < 0) {
481
                    $error++;
482
                }
483
            }
484
485
            if (!$error) {
486
                $result = $this->update_price(1);
487
                if ($result > 0) {
488
                    if (!$notrigger) {
489
                        // Call trigger
490
                        $result = $this->call_trigger('EXPENSE_REPORT_CREATE', $user);
491
492
                        if ($result < 0) {
493
                            $error++;
494
                        }
495
                        // End call triggers
496
                    }
497
498
                    if (empty($error)) {
499
                        $this->db->commit();
500
                        return $this->id;
501
                    } else {
502
                        $this->db->rollback();
503
                        return -4;
504
                    }
505
                } else {
506
                    $this->db->rollback();
507
                    return -3;
508
                }
509
            } else {
510
                dol_syslog(get_class($this) . "::create error " . $this->error, LOG_ERR);
511
                $this->db->rollback();
512
                return -2;
513
            }
514
        } else {
515
            $this->error = $this->db->lasterror() . " sql=" . $sql;
516
            $this->db->rollback();
517
            return -1;
518
        }
519
    }
520
521
    /**
522
     *  Load an object from its id and create a new one in database
523
     *
524
     *  @param      User    $user               User making the clone
525
     *  @param      int     $fk_user_author     Id of new user
526
     *  @return     int                         New id of clone
527
     */
528
    public function createFromClone(User $user, $fk_user_author)
529
    {
530
        global $hookmanager;
531
532
        $error = 0;
533
534
        if (empty($fk_user_author)) {
535
            $fk_user_author = $user->id;
536
        }
537
538
        $this->db->begin();
539
540
        // get extrafields so they will be clone
541
        //foreach($this->lines as $line)
542
        //$line->fetch_optionals();
543
544
        // Load source object
545
        $objFrom = clone $this;
546
547
        $this->id = 0;
548
        $this->ref = '';
549
        $this->status = 0;
550
        $this->fk_statut = 0; // deprecated
551
552
        // Clear fields
553
        $this->fk_user_creat = $user->id;
554
        $this->fk_user_author = $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
555
        $this->fk_user_valid = 0;
556
        $this->date_create = '';
557
        $this->date_creation = '';
558
        $this->date_validation = '';
559
560
        // Remove link on lines to a joined file
561
        if (is_array($this->lines) && count($this->lines) > 0) {
562
            foreach ($this->lines as $key => $line) {
563
                $this->lines[$key]->fk_ecm_files = 0;
564
            }
565
        }
566
567
        // Create clone
568
        $this->context['createfromclone'] = 'createfromclone';
569
        $result = $this->create($user);
570
        if ($result < 0) {
571
            $error++;
572
        }
573
574
        if (!$error) {
575
            // Hook of thirdparty module
576
            if (is_object($hookmanager)) {
577
                $parameters = array('objFrom' => $objFrom);
578
                $action = '';
579
                $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
580
                if ($reshook < 0) {
581
                    $this->setErrorsFromObject($hookmanager);
582
                    $error++;
583
                }
584
            }
585
        }
586
587
        unset($this->context['createfromclone']);
588
589
        // End
590
        if (!$error) {
591
            $this->db->commit();
592
            return $this->id;
593
        } else {
594
            $this->db->rollback();
595
            return -1;
596
        }
597
    }
598
599
600
    /**
601
     * update
602
     *
603
     * @param   User    $user                   User making change
604
     * @param   int     $notrigger              Disable triggers
605
     * @param   User    $userofexpensereport    New user we want to have the expense report on.
606
     * @return  int                             Return integer <0 if KO, >0 if OK
607
     */
608
    public function update($user, $notrigger = 0, $userofexpensereport = null)
609
    {
610
        global $langs;
611
612
        $error = 0;
613
        $this->db->begin();
614
615
        $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET";
616
        $sql .= " total_ht = " . $this->total_ht;
617
        $sql .= " , total_ttc = " . $this->total_ttc;
618
        $sql .= " , total_tva = " . $this->total_tva;
619
        $sql .= " , date_debut = '" . $this->db->idate($this->date_debut) . "'";
620
        $sql .= " , date_fin = '" . $this->db->idate($this->date_fin) . "'";
621
        if ($userofexpensereport && is_object($userofexpensereport)) {
622
            $sql .= " , fk_user_author = " . ($userofexpensereport->id > 0 ? $userofexpensereport->id : "null"); // Note fk_user_author is not the 'author' but the guy the expense report is for.
623
        }
624
        $sql .= " , fk_user_validator = " . ($this->fk_user_validator > 0 ? $this->fk_user_validator : "null");
625
        $sql .= " , fk_user_valid = " . ($this->fk_user_valid > 0 ? $this->fk_user_valid : "null");
626
        $sql .= " , fk_user_approve = " . ($this->fk_user_approve > 0 ? $this->fk_user_approve : "null");
627
        $sql .= " , fk_user_modif = " . $user->id;
628
        $sql .= " , fk_statut = " . ($this->fk_statut >= 0 ? $this->fk_statut : '0');
629
        $sql .= " , fk_c_paiement = " . ($this->fk_c_paiement > 0 ? $this->fk_c_paiement : "null");
630
        $sql .= " , note_public = " . (!empty($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "''");
631
        $sql .= " , note_private = " . (!empty($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "''");
632
        $sql .= " , detail_refuse = " . (!empty($this->detail_refuse) ? "'" . $this->db->escape($this->detail_refuse) . "'" : "''");
633
        $sql .= " WHERE rowid = " . ((int) $this->id);
634
635
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
636
        $result = $this->db->query($sql);
637
        if ($result) {
638
            if (!$notrigger) {
639
                // Call trigger
640
                $result = $this->call_trigger('EXPENSE_REPORT_MODIFY', $user);
641
642
                if ($result < 0) {
643
                    $error++;
644
                }
645
                // End call triggers
646
            }
647
648
            if (empty($error)) {
649
                $this->db->commit();
650
                return 1;
651
            } else {
652
                $this->db->rollback();
653
                $this->error = $this->db->error();
654
                return -2;
655
            }
656
        } else {
657
            $this->db->rollback();
658
            $this->error = $this->db->error();
659
            return -1;
660
        }
661
    }
662
663
    /**
664
     *  Load an object from database
665
     *
666
     *  @param  int     $id     Id                      {@min 1}
667
     *  @param  string  $ref    Ref                     {@name ref}
668
     *  @return int             Return integer <0 if KO, >0 if OK
669
     */
670
    public function fetch($id, $ref = '')
671
    {
672
        $sql = "SELECT d.rowid, d.entity, d.ref, d.note_public, d.note_private,"; // DEFAULT
673
        $sql .= " d.detail_refuse, d.detail_cancel, d.fk_user_refuse, d.fk_user_cancel,"; // ACTIONS
674
        $sql .= " d.date_refuse, d.date_cancel,"; // ACTIONS
675
        $sql .= " d.total_ht, d.total_ttc, d.total_tva,";
676
        $sql .= " d.localtax1 as total_localtax1, d.localtax2 as total_localtax2,";
677
        $sql .= " d.date_debut, d.date_fin, d.date_create, d.tms as date_modif, d.date_valid, d.date_approve,"; // DATES (datetime)
678
        $sql .= " d.fk_user_creat, d.fk_user_author, d.fk_user_modif, d.fk_user_validator,";
679
        $sql .= " d.fk_user_valid, d.fk_user_approve,";
680
        $sql .= " d.fk_statut as status, d.fk_c_paiement, d.paid";
681
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . " as d";
682
        if ($ref) {
683
            $sql .= " WHERE d.ref = '" . $this->db->escape($ref) . "'";
684
        } else {
685
            $sql .= " WHERE d.rowid = " . ((int) $id);
686
        }
687
        //$sql.= $restrict;
688
689
        dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
690
        $resql = $this->db->query($sql);
691
        if ($resql) {
692
            $obj = $this->db->fetch_object($resql);
693
            if ($obj) {
694
                $this->id           = $obj->rowid;
695
                $this->ref          = $obj->ref;
696
697
                $this->entity       = $obj->entity;
698
699
                $this->total_ht     = $obj->total_ht;
700
                $this->total_tva    = $obj->total_tva;
701
                $this->total_ttc    = $obj->total_ttc;
702
                $this->localtax1    = $obj->total_localtax1;        // For backward compatibility
703
                $this->localtax2    = $obj->total_localtax2;        // For backward compatibility
704
                $this->total_localtax1 = $obj->total_localtax1;
705
                $this->total_localtax2 = $obj->total_localtax2;
706
707
                $this->note_public  = $obj->note_public;
708
                $this->note_private = $obj->note_private;
709
                $this->detail_refuse = $obj->detail_refuse;
710
                $this->detail_cancel = $obj->detail_cancel;
711
712
                $this->date_debut       = $this->db->jdate($obj->date_debut);
713
                $this->date_fin         = $this->db->jdate($obj->date_fin);
714
                $this->date_valid       = $this->db->jdate($obj->date_valid);
715
                $this->date_approve     = $this->db->jdate($obj->date_approve);
716
                $this->date_create      = $this->db->jdate($obj->date_create);
717
                $this->date_modif       = $this->db->jdate($obj->date_modif);
718
                $this->date_refuse      = $this->db->jdate($obj->date_refuse);
719
                $this->date_cancel      = $this->db->jdate($obj->date_cancel);
720
721
                $this->fk_user_creat            = $obj->fk_user_creat;
722
                $this->fk_user_author           = $obj->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
723
                $this->fk_user_modif            = $obj->fk_user_modif;
724
                $this->fk_user_validator        = $obj->fk_user_validator;
725
                $this->fk_user_valid            = $obj->fk_user_valid;
726
                $this->fk_user_refuse           = $obj->fk_user_refuse;
727
                $this->fk_user_cancel           = $obj->fk_user_cancel;
728
                $this->fk_user_approve          = $obj->fk_user_approve;
729
730
                $user_author = new User($this->db);
731
                if ($this->fk_user_author > 0) {
732
                    $user_author->fetch($this->fk_user_author);
733
                }
734
735
                $this->user_author_infos = dolGetFirstLastname($user_author->firstname, $user_author->lastname);
736
737
                $user_approver = new User($this->db);
738
                if ($this->fk_user_approve > 0) {
739
                    $user_approver->fetch($this->fk_user_approve);
740
                } elseif ($this->fk_user_validator > 0) {
741
                    $user_approver->fetch($this->fk_user_validator); // For backward compatibility
742
                }
743
                $this->user_validator_infos = dolGetFirstLastname($user_approver->firstname, $user_approver->lastname);
744
745
                $this->fk_statut                = $obj->status; // deprecated
746
                $this->status                   = $obj->status;
747
                $this->fk_c_paiement            = $obj->fk_c_paiement;
748
                $this->paid                     = $obj->paid;
749
750
                if ($this->status == self::STATUS_APPROVED || $this->status == self::STATUS_CLOSED) {
751
                    $user_valid = new User($this->db);
752
                    if ($this->fk_user_valid > 0) {
753
                        $user_valid->fetch($this->fk_user_valid);
754
                    }
755
                    $this->user_valid_infos = dolGetFirstLastname($user_valid->firstname, $user_valid->lastname);
756
                }
757
758
                $this->fetch_optionals();
759
760
                $result = $this->fetch_lines();
761
762
                return $result;
763
            } else {
764
                return 0;
765
            }
766
        } else {
767
            $this->error = $this->db->lasterror();
768
            return -1;
769
        }
770
    }
771
772
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
773
    /**
774
     *  Classify the expense report as paid
775
     *
776
     *  @deprecated
777
     *  @see setPaid()
778
     *  @param    int     $id                 Id of expense report
779
     *  @param    user    $fuser              User making change
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\ExpenseReport\Classes\user was not found. Did you mean user? If so, make sure to prefix the type with \.
Loading history...
780
     *  @param    int     $notrigger          Disable triggers
781
     *  @return   int                         Return integer <0 if KO, >0 if OK
782
     */
783
    public function set_paid($id, $fuser, $notrigger = 0)
784
    {
785
		// phpcs:enable
786
        dol_syslog(get_class($this) . "::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
787
        return $this->setPaid($id, $fuser, $notrigger);
788
    }
789
790
    /**
791
     *    Classify the expense report as paid
792
     *
793
     *    @param    int     $id                 Id of expense report
794
     *    @param    user    $fuser              User making change
795
     *    @param    int     $notrigger          Disable triggers
796
     *    @return   int                         Return integer <0 if KO, >0 if OK
797
     */
798
    public function setPaid($id, $fuser, $notrigger = 0)
799
    {
800
        $error = 0;
801
        $this->db->begin();
802
803
        $sql = "UPDATE " . MAIN_DB_PREFIX . "expensereport";
804
        $sql .= " SET fk_statut = " . self::STATUS_CLOSED . ", paid=1";
805
        $sql .= " WHERE rowid = " . ((int) $id) . " AND fk_statut = " . self::STATUS_APPROVED;
806
807
        dol_syslog(get_class($this) . "::setPaid", LOG_DEBUG);
808
        $resql = $this->db->query($sql);
809
        if ($resql) {
810
            if ($this->db->affected_rows($resql)) {
811
                if (!$notrigger) {
812
                    // Call trigger
813
                    $result = $this->call_trigger('EXPENSE_REPORT_PAID', $fuser);
814
815
                    if ($result < 0) {
816
                        $error++;
817
                    }
818
                    // End call triggers
819
                }
820
821
                if (empty($error)) {
822
                    $this->db->commit();
823
                    return 1;
824
                } else {
825
                    $this->db->rollback();
826
                    $this->error = $this->db->error();
827
                    return -2;
828
                }
829
            } else {
830
                $this->db->commit();
831
                return 0;
832
            }
833
        } else {
834
            $this->db->rollback();
835
            dol_print_error($this->db);
836
            return -1;
837
        }
838
    }
839
840
    /**
841
     *  Returns the label status
842
     *
843
     *  @param      int     $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
844
     *  @return     string              Label
845
     */
846
    public function getLibStatut($mode = 0)
847
    {
848
        return $this->LibStatut($this->status, $mode);
849
    }
850
851
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
852
    /**
853
     *  Returns the label of a status
854
     *
855
     *  @param      int     $status     ID status
856
     *  @param      int     $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto
857
     *  @return     string              Label
858
     */
859
    public function LibStatut($status, $mode = 0)
860
    {
861
		// phpcs:enable
862
        global $langs;
863
864
        $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
865
        $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
866
867
        $statuslogo = array(0 => 'status0', 2 => 'status1', 4 => 'status6', 5 => 'status4', 6 => 'status6', 99 => 'status5');
868
869
        $statusType = $statuslogo[$status];
870
871
        return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
872
    }
873
874
875
    /**
876
     *  Load information on object
877
     *
878
     *  @param  int     $id      Id of object
879
     *  @return void
880
     */
881
    public function info($id)
882
    {
883
        global $conf;
884
885
        $sql = "SELECT f.rowid,";
886
        $sql .= " f.date_create as datec,";
887
        $sql .= " f.tms as date_modification,";
888
        $sql .= " f.date_valid as datev,";
889
        $sql .= " f.date_approve as datea,";
890
        $sql .= " f.fk_user_creat as fk_user_creation,";
891
        $sql .= " f.fk_user_author as fk_user_author,";
892
        $sql .= " f.fk_user_modif as fk_user_modification,";
893
        $sql .= " f.fk_user_valid,";
894
        $sql .= " f.fk_user_approve";
895
        $sql .= " FROM " . MAIN_DB_PREFIX . "expensereport as f";
896
        $sql .= " WHERE f.rowid = " . ((int) $id);
897
        $sql .= " AND f.entity = " . $conf->entity;
898
899
900
901
        $resql = $this->db->query($sql);
902
        if ($resql) {
903
            if ($this->db->num_rows($resql)) {
904
                $obj = $this->db->fetch_object($resql);
905
906
                $this->id = $obj->rowid;
907
908
                $this->date_creation = $this->db->jdate($obj->datec);
909
                $this->date_modification = $this->db->jdate($obj->date_modification);
910
                $this->date_validation = $this->db->jdate($obj->datev);
911
                $this->date_approbation = $this->db->jdate($obj->datea);
912
913
                $this->user_creation_id = $obj->fk_user_author;
914
                $this->user_creation_id = $obj->fk_user_creation;
915
                $this->user_validation_id = $obj->fk_user_valid;
916
                $this->user_modification_id = $obj->fk_user_modification;
917
                $this->user_approve_id = $obj->fk_user_approve;
918
            }
919
            $this->db->free($resql);
920
        } else {
921
            dol_print_error($this->db);
922
        }
923
    }
924
925
926
927
    /**
928
     *  Initialise an instance with random values.
929
     *  Used to build previews or test instances.
930
     *  id must be 0 if object instance is a specimen.
931
     *
932
     *  @return int
933
     */
934
    public function initAsSpecimen()
935
    {
936
        global $user, $langs;
937
938
        $now = dol_now();
939
940
        // Initialise parameters
941
        $this->id = 0;
942
        $this->ref = 'SPECIMEN';
943
        $this->specimen = 1;
944
        $this->entity = 1;
945
        $this->date_create = $now;
946
        $this->date_debut = $now;
947
        $this->date_fin = $now;
948
        $this->date_valid = $now;
949
        $this->date_approve = $now;
950
951
        $type_fees_id = 2; // TF_TRIP
952
953
        $this->status = 5;
954
955
        $this->fk_user_author = $user->id;
956
        $this->fk_user_validator = $user->id;
957
        $this->fk_user_valid = $user->id;
958
        $this->fk_user_approve = $user->id;
959
960
        $this->note_private = 'Private note';
961
        $this->note_public = 'SPECIMEN';
962
        $nbp = 5;
963
        $xnbp = 0;
964
        while ($xnbp < $nbp) {
965
            $line = new ExpenseReportLine($this->db);
966
            $line->comments = $langs->trans("Comment") . " " . $xnbp;
967
            $line->date = ($now - 3600 * (1 + $xnbp));
968
            $line->total_ht = 100;
969
            $line->total_tva = 20;
970
            $line->total_ttc = 120;
971
            $line->qty = 1;
972
            $line->vatrate = 20;
973
            $line->value_unit = 120;
974
            $line->fk_expensereport = 0;
975
            $line->type_fees_code = 'TRA';
976
            $line->fk_c_type_fees = $type_fees_id;
977
978
            $line->projet_ref = 'ABC';
979
980
            $this->lines[$xnbp] = $line;
981
            $xnbp++;
982
983
            $this->total_ht += $line->total_ht;
984
            $this->total_tva += $line->total_tva;
985
            $this->total_ttc += $line->total_ttc;
986
        }
987
988
        return 1;
989
    }
990
991
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
992
    /**
993
     * fetch_line_by_project
994
     *
995
     * @param   int     $projectid      Project id
996
     * @param   User    $user           User
997
     * @return  int                     Return integer <0 if KO, >0 if OK
998
     */
999
    public function fetch_line_by_project($projectid, $user)
1000
    {
1001
		// phpcs:enable
1002
        global $langs;
1003
1004
        $langs->load('trips');
1005
1006
        if ($user->hasRight('expensereport', 'lire')) {
1007
            $sql = "SELECT de.fk_expensereport, de.date, de.comments, de.total_ht, de.total_ttc";
1008
            $sql .= " FROM " . MAIN_DB_PREFIX . "expensereport_det as de";
1009
            $sql .= " WHERE de.fk_projet = " . ((int) $projectid);
1010
1011
            dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
1012
            $result = $this->db->query($sql);
1013
            if ($result) {
1014
                $num = $this->db->num_rows($result);
1015
                $i = 0;
1016
                $total_HT = 0;
1017
                $total_TTC = 0;
1018
1019
                while ($i < $num) {
1020
                    $objp = $this->db->fetch_object($result);
1021
1022
                    $sql2 = "SELECT d.rowid, d.fk_user_author, d.ref, d.fk_statut as status";
1023
                    $sql2 .= " FROM " . MAIN_DB_PREFIX . "expensereport as d";
1024
                    $sql2 .= " WHERE d.rowid = " . ((int) $objp->fk_expensereport);
1025
1026
                    $result2 = $this->db->query($sql2);
1027
                    $obj = $this->db->fetch_object($result2);
1028
1029
                    $objp->fk_user_author = $obj->fk_user_author;
1030
                    $objp->ref = $obj->ref;
1031
                    $objp->fk_c_expensereport_status = $obj->status;
1032
                    $objp->rowid = $obj->rowid;
1033
1034
                    $total_HT += $objp->total_ht;
1035
                    $total_TTC += $objp->total_ttc;
1036
                    $author = new User($this->db);
1037
                    $author->fetch($objp->fk_user_author);
1038
1039
                    print '<tr>';
1040
                    print '<td><a href="' . constant('BASE_URL') . '/expensereport/card.php?id=' . $objp->rowid . '">' . $objp->ref_num . '</a></td>';
1041
                    print '<td class="center">' . dol_print_date($objp->date, 'day') . '</td>';
1042
                    print '<td>' . $author->getNomUrl(1) . '</td>';
1043
                    print '<td>' . $objp->comments . '</td>';
1044
                    print '<td class="right">' . price($objp->total_ht) . '</td>';
1045
                    print '<td class="right">' . price($objp->total_ttc) . '</td>';
1046
                    print '<td class="right">';
1047
1048
                    switch ($objp->fk_c_expensereport_status) {
1049
                        case 4:
1050
                            print img_picto($langs->trans('StatusOrderCanceled'), 'statut5');
1051
                            break;
1052
                        case 1:
1053
                            print $langs->trans('Draft') . ' ' . img_picto($langs->trans('Draft'), 'statut0');
1054
                            break;
1055
                        case 2:
1056
                            print $langs->trans('TripForValid') . ' ' . img_picto($langs->trans('TripForValid'), 'statut3');
1057
                            break;
1058
                        case 5:
1059
                            print $langs->trans('TripForPaid') . ' ' . img_picto($langs->trans('TripForPaid'), 'statut3');
1060
                            break;
1061
                        case 6:
1062
                            print $langs->trans('TripPaid') . ' ' . img_picto($langs->trans('TripPaid'), 'statut4');
1063
                            break;
1064
                    }
1065
                    /*
1066
                     if ($status==4) return img_picto($langs->trans('StatusOrderCanceled'),'statut5');
1067
                    if ($status==1) return img_picto($langs->trans('StatusOrderDraft'),'statut0');
1068
                    if ($status==2) return img_picto($langs->trans('StatusOrderValidated'),'statut1');
1069
                    if ($status==2) return img_picto($langs->trans('StatusOrderOnProcess'),'statut3');
1070
                    if ($status==5) return img_picto($langs->trans('StatusOrderToBill'),'statut4');
1071
                    if ($status==6) return img_picto($langs->trans('StatusOrderOnProcess'),'statut6');
1072
                    */
1073
                    print '</td>';
1074
                    print '</tr>';
1075
1076
                    $i++;
1077
                }
1078
1079
                print '<tr class="liste_total"><td colspan="4">' . $langs->trans("Number") . ': ' . $i . '</td>';
1080
                print '<td class="right" width="100">' . $langs->trans("TotalHT") . ' : ' . price($total_HT) . '</td>';
1081
                print '<td class="right" width="100">' . $langs->trans("TotalTTC") . ' : ' . price($total_TTC) . '</td>';
1082
                print '<td>&nbsp;</td>';
1083
                print '</tr>';
1084
            } else {
1085
                $this->error = $this->db->lasterror();
1086
                return -1;
1087
            }
1088
        }
1089
1090
        return 0;
1091
    }
1092
1093
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1094
    /**
1095
     * fetch_lines
1096
     *
1097
     * @return  int     Return integer <0 if OK, >0 if KO
1098
     */
1099
    public function fetch_lines()
1100
    {
1101
		// phpcs:enable
1102
        $this->lines = array();
1103
1104
        $sql = ' SELECT de.rowid, de.comments, de.qty, de.value_unit, de.date, de.rang,';
1105
        $sql .= " de." . $this->fk_element . ", de.fk_c_type_fees, de.fk_c_exp_tax_cat, de.fk_projet as fk_project,";
1106
        $sql .= ' de.tva_tx, de.vat_src_code,';
1107
        $sql .= ' de.localtax1_tx, de.localtax2_tx, de.localtax1_type, de.localtax2_type,';
1108
        $sql .= ' de.fk_ecm_files,';
1109
        $sql .= ' de.total_ht, de.total_tva, de.total_ttc,';
1110
        $sql .= ' de.total_localtax1, de.total_localtax2, de.rule_warning_message,';
1111
        $sql .= ' ctf.code as code_type_fees, ctf.label as label_type_fees, ctf.accountancy_code as accountancy_code_type_fees,';
1112
        $sql .= ' p.ref as ref_projet, p.title as title_projet';
1113
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element_line . ' as de';
1114
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_type_fees as ctf ON de.fk_c_type_fees = ctf.id';
1115
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'projet as p ON de.fk_projet = p.rowid';
1116
        $sql .= " WHERE de." . $this->fk_element . " = " . ((int) $this->id);
1117
        if (getDolGlobalString('EXPENSEREPORT_LINES_SORTED_BY_ROWID')) {
1118
            $sql .= ' ORDER BY de.rang ASC, de.rowid ASC';
1119
        } else {
1120
            $sql .= ' ORDER BY de.rang ASC, de.date ASC';
1121
        }
1122
1123
        $resql = $this->db->query($sql);
1124
        if ($resql) {
1125
            $num = $this->db->num_rows($resql);
1126
            $i = 0;
1127
            while ($i < $num) {
1128
                $objp = $this->db->fetch_object($resql);
1129
1130
                $deplig = new ExpenseReportLine($this->db);
1131
1132
                $deplig->rowid          = $objp->rowid;
1133
                $deplig->id             = $objp->rowid;
1134
                $deplig->comments       = $objp->comments;
1135
                $deplig->qty            = $objp->qty;
1136
                $deplig->value_unit     = $objp->value_unit;
1137
                $deplig->date           = $objp->date;
1138
                $deplig->dates          = $this->db->jdate($objp->date);
1139
1140
                $deplig->fk_expensereport = $objp->fk_expensereport;
1141
                $deplig->fk_c_type_fees   = $objp->fk_c_type_fees;
1142
                $deplig->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
1143
                $deplig->fk_projet        = $objp->fk_project; // deprecated
1144
                $deplig->fk_project       = $objp->fk_project;
1145
                $deplig->fk_ecm_files     = $objp->fk_ecm_files;
1146
1147
                $deplig->total_ht         = $objp->total_ht;
1148
                $deplig->total_tva        = $objp->total_tva;
1149
                $deplig->total_ttc        = $objp->total_ttc;
1150
                $deplig->total_localtax1  = $objp->total_localtax1;
1151
                $deplig->total_localtax2  = $objp->total_localtax2;
1152
1153
                $deplig->type_fees_code     = empty($objp->code_type_fees) ? 'TF_OTHER' : $objp->code_type_fees;
1154
                $deplig->type_fees_libelle  = $objp->label_type_fees;
1155
                $deplig->type_fees_accountancy_code = $objp->accountancy_code_type_fees;
1156
1157
                $deplig->tva_tx             = $objp->tva_tx;
1158
                $deplig->vatrate            = $objp->tva_tx;
1159
                $deplig->vat_src_code       = $objp->vat_src_code;
1160
                $deplig->localtax1_tx       = $objp->localtax1_tx;
1161
                $deplig->localtax2_tx       = $objp->localtax2_tx;
1162
                $deplig->localtax1_type     = $objp->localtax1_type;
1163
                $deplig->localtax2_type     = $objp->localtax2_type;
1164
1165
                $deplig->projet_ref         = $objp->ref_projet;
1166
                $deplig->projet_title       = $objp->title_projet;
1167
1168
                $deplig->rule_warning_message = $objp->rule_warning_message;
1169
1170
                $deplig->rang               = $objp->rang;
1171
1172
                $this->lines[$i] = $deplig;
1173
1174
                $i++;
1175
            }
1176
            $this->db->free($resql);
1177
            return 1;
1178
        } else {
1179
            $this->error = $this->db->lasterror();
1180
            dol_syslog(get_class($this) . "::fetch_lines: Error " . $this->error, LOG_ERR);
1181
            return -3;
1182
        }
1183
    }
1184
1185
1186
    /**
1187
     * Delete object in database
1188
     *
1189
     * @param   User|null   $user       User that delete
1190
     * @param   int         $notrigger  0=launch triggers after, 1=disable triggers
1191
     * @return  int                     Return integer <0 if KO, >0 if OK
1192
     */
1193
    public function delete(User $user = null, $notrigger = 0)
1194
    {
1195
        global $conf;
1196
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
1197
1198
        $error = 0;
1199
1200
        $this->db->begin();
1201
1202
        if (!$notrigger) {
1203
            // Call trigger
1204
            $result = $this->call_trigger('EXPENSE_REPORT_DELETE', $user);
1205
            if ($result < 0) {
1206
                $error++;
1207
            }
1208
            // End call triggers
1209
        }
1210
1211
        // Delete extrafields of lines and lines
1212
        if (!$error && !empty($this->table_element_line)) {
1213
            $tabletodelete = $this->table_element_line;
1214
            //$sqlef = "DELETE FROM ".MAIN_DB_PREFIX.$tabletodelete."_extrafields WHERE fk_object IN (SELECT rowid FROM ".MAIN_DB_PREFIX.$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id).")";
1215
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . $tabletodelete . " WHERE " . $this->fk_element . " = " . ((int) $this->id);
1216
            if (!$this->db->query($sql)) {
1217
                $error++;
1218
                $this->error = $this->db->lasterror();
1219
                $this->errors[] = $this->error;
1220
                dol_syslog(get_class($this) . "::delete error " . $this->error, LOG_ERR);
1221
            }
1222
        }
1223
1224
        if (!$error) {
1225
            // Delete linked object
1226
            $res = $this->deleteObjectLinked();
1227
            if ($res < 0) {
1228
                $error++;
1229
            }
1230
        }
1231
1232
        if (!$error) {
1233
            // Delete linked contacts
1234
            $res = $this->delete_linked_contact();
1235
            if ($res < 0) {
1236
                $error++;
1237
            }
1238
        }
1239
1240
        // Removed extrafields of object
1241
        if (!$error) {
1242
            $result = $this->deleteExtraFields();
1243
            if ($result < 0) {
1244
                $error++;
1245
                dol_syslog(get_class($this) . "::delete error " . $this->error, LOG_ERR);
1246
            }
1247
        }
1248
1249
        // Delete main record
1250
        if (!$error) {
1251
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element . " WHERE rowid = " . ((int) $this->id);
1252
            $res = $this->db->query($sql);
1253
            if (!$res) {
1254
                $error++;
1255
                $this->error = $this->db->lasterror();
1256
                $this->errors[] = $this->error;
1257
                dol_syslog(get_class($this) . "::delete error " . $this->error, LOG_ERR);
1258
            }
1259
        }
1260
1261
        // Delete record into ECM index and physically
1262
        if (!$error) {
1263
            $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1264
            $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1265
            if (!$res) {
1266
                $error++;
1267
            }
1268
        }
1269
1270
        if (!$error) {
1271
            // We remove directory
1272
            $ref = dol_sanitizeFileName($this->ref);
1273
            if ($conf->expensereport->multidir_output[$this->entity] && !empty($this->ref)) {
1274
                $dir = $conf->expensereport->multidir_output[$this->entity] . "/" . $ref;
1275
                $file = $dir . "/" . $ref . ".pdf";
1276
                if (file_exists($file)) {
1277
                    dol_delete_preview($this);
1278
1279
                    if (!dol_delete_file($file, 0, 0, 0, $this)) {
1280
                        $this->error = 'ErrorFailToDeleteFile';
1281
                        $this->errors[] = $this->error;
1282
                        $this->db->rollback();
1283
                        return 0;
1284
                    }
1285
                }
1286
                if (file_exists($dir)) {
1287
                    $res = @dol_delete_dir_recursive($dir);
1288
                    if (!$res) {
1289
                        $this->error = 'ErrorFailToDeleteDir';
1290
                        $this->errors[] = $this->error;
1291
                        $this->db->rollback();
1292
                        return 0;
1293
                    }
1294
                }
1295
            }
1296
        }
1297
1298
        if (!$error) {
1299
            dol_syslog(get_class($this) . "::delete " . $this->id . " by " . $user->id, LOG_DEBUG);
1300
            $this->db->commit();
1301
            return 1;
1302
        } else {
1303
            $this->db->rollback();
1304
            return -1;
1305
        }
1306
    }
1307
1308
    /**
1309
     * Set to status validate
1310
     *
1311
     * @param   User    $fuser      User
1312
     * @param   int     $notrigger  Disable triggers
1313
     * @return  int                 Return integer <0 if KO, 0 if nothing done, >0 if OK
1314
     */
1315
    public function setValidate($fuser, $notrigger = 0)
1316
    {
1317
        global $conf, $langs, $user;
1318
1319
        $error = 0;
1320
        $now = dol_now();
1321
1322
        // Protection
1323
        if ($this->status == self::STATUS_VALIDATED) {
1324
            dol_syslog(get_class($this) . "::valid action abandoned: already validated", LOG_WARNING);
1325
            return 0;
1326
        }
1327
1328
        $this->date_valid = $now; // Required for the getNextNum later.
1329
1330
        // Define new ref
1331
        if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
1332
            $num = $this->getNextNumRef();
1333
        } else {
1334
            $num = $this->ref;
1335
        }
1336
        if (empty($num) || $num < 0) {
1337
            return -1;
1338
        }
1339
1340
        $this->newref = dol_sanitizeFileName($num);
1341
1342
        $this->db->begin();
1343
1344
        // Validate
1345
        $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element;
1346
        $sql .= " SET ref = '" . $this->db->escape($num) . "',";
1347
        $sql .= " fk_statut = " . self::STATUS_VALIDATED . ",";
1348
        $sql .= " date_valid = '" . $this->db->idate($this->date_valid) . "',";
1349
        $sql .= " fk_user_valid = " . ((int) $user->id);
1350
        $sql .= " WHERE rowid = " . ((int) $this->id);
1351
1352
        $resql = $this->db->query($sql);
1353
        if ($resql) {
1354
            if (!$error && !$notrigger) {
1355
                // Call trigger
1356
                $result = $this->call_trigger('EXPENSE_REPORT_VALIDATE', $fuser);
1357
                if ($result < 0) {
1358
                    $error++;
1359
                }
1360
                // End call triggers
1361
            }
1362
1363
            if (!$error) {
1364
                $this->oldref = $this->ref;
1365
1366
                // Rename directory if dir was a temporary ref
1367
                if (preg_match('/^[\(]?PROV/i', $this->ref)) {
1368
                    require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
1369
1370
                    // Now we rename also files into index
1371
                    $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filename = CONCAT('" . $this->db->escape($this->newref) . "', SUBSTR(filename, " . (strlen($this->ref) + 1) . ")), filepath = 'expensereport/" . $this->db->escape($this->newref) . "'";
1372
                    $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'expensereport/" . $this->db->escape($this->ref) . "' AND entity = " . ((int) $this->entity);
1373
                    $resql = $this->db->query($sql);
1374
                    if (!$resql) {
1375
                        $error++;
1376
                        $this->error = $this->db->lasterror();
1377
                    }
1378
                    $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'expensereport/" . $this->db->escape($this->newref) . "'";
1379
                    $sql .= " WHERE filepath = 'expensereport/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
1380
                    $resql = $this->db->query($sql);
1381
                    if (!$resql) {
1382
                        $error++;
1383
                        $this->error = $this->db->lasterror();
1384
                    }
1385
1386
                    // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1387
                    $oldref = dol_sanitizeFileName($this->ref);
1388
                    $newref = dol_sanitizeFileName($num);
1389
                    $dirsource = $conf->expensereport->multidir_output[$this->entity] . '/' . $oldref;
1390
                    $dirdest = $conf->expensereport->multidir_output[$this->entity] . '/' . $newref;
1391
                    if (!$error && file_exists($dirsource)) {
1392
                        dol_syslog(get_class($this) . "::setValidate() rename dir " . $dirsource . " into " . $dirdest);
1393
1394
                        if (@rename($dirsource, $dirdest)) {
1395
                            dol_syslog("Rename ok");
1396
                            // Rename docs starting with $oldref with $newref
1397
                            $listoffiles = dol_dir_list($dirdest, 'files', 1, '^' . preg_quote($oldref, '/'));
1398
                            foreach ($listoffiles as $fileentry) {
1399
                                $dirsource = $fileentry['name'];
1400
                                $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
1401
                                $dirsource = $fileentry['path'] . '/' . $dirsource;
1402
                                $dirdest = $fileentry['path'] . '/' . $dirdest;
1403
                                @rename($dirsource, $dirdest);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rename(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

1403
                                /** @scrutinizer ignore-unhandled */ @rename($dirsource, $dirdest);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1404
                            }
1405
                        }
1406
                    }
1407
                }
1408
            }
1409
1410
            // Set new ref and current status
1411
            if (!$error) {
1412
                $this->ref = $num;
1413
                $this->status = self::STATUS_VALIDATED;
1414
            }
1415
1416
            if (empty($error)) {
1417
                $this->db->commit();
1418
                return 1;
1419
            } else {
1420
                $this->db->rollback();
1421
                $this->error = $this->db->error();
1422
                return -2;
1423
            }
1424
        } else {
1425
            $this->db->rollback();
1426
            $this->error = $this->db->lasterror();
1427
            return -1;
1428
        }
1429
    }
1430
1431
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1432
    /**
1433
     * set_save_from_refuse
1434
     *
1435
     * @param   User    $fuser      User
1436
     * @return  int                 Return integer <0 if KO, >0 if OK
1437
     */
1438
    public function set_save_from_refuse($fuser)
1439
    {
1440
		// phpcs:enable
1441
        // Sélection de la date de début de la NDF
1442
        $sql = 'SELECT date_debut';
1443
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element;
1444
        $sql .= " WHERE rowid = " . ((int) $this->id);
1445
1446
        $result = $this->db->query($sql);
1447
1448
        $objp = $this->db->fetch_object($result);
1449
1450
        $this->date_debut = $this->db->jdate($objp->date_debut);
1451
1452
        if ($this->status != self::STATUS_VALIDATED) {
1453
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
1454
            $sql .= " SET fk_statut = " . self::STATUS_VALIDATED;
1455
            $sql .= " WHERE rowid = " . ((int) $this->id);
1456
1457
            dol_syslog(get_class($this) . "::set_save_from_refuse", LOG_DEBUG);
1458
1459
            if ($this->db->query($sql)) {
1460
                return 1;
1461
            } else {
1462
                $this->error = $this->db->lasterror();
1463
                return -1;
1464
            }
1465
        } else {
1466
            dol_syslog(get_class($this) . "::set_save_from_refuse expensereport already with save status", LOG_WARNING);
1467
        }
1468
1469
        return 0;
1470
    }
1471
1472
    /**
1473
     * Set status to approved
1474
     *
1475
     * @param   User    $fuser      User
1476
     * @param   int     $notrigger  Disable triggers
1477
     * @return  int                 Return integer <0 if KO, 0 if nothing done, >0 if OK
1478
     */
1479
    public function setApproved($fuser, $notrigger = 0)
1480
    {
1481
        $now = dol_now();
1482
        $error = 0;
1483
1484
        // date approval
1485
        $this->date_approve = $now;
1486
        if ($this->status != self::STATUS_APPROVED) {
1487
            $this->db->begin();
1488
1489
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
1490
            $sql .= " SET ref = '" . $this->db->escape($this->ref) . "', fk_statut = " . self::STATUS_APPROVED . ", fk_user_approve = " . ((int) $fuser->id) . ",";
1491
            $sql .= " date_approve='" . $this->db->idate($this->date_approve) . "'";
1492
            $sql .= " WHERE rowid = " . ((int) $this->id);
1493
            if ($this->db->query($sql)) {
1494
                if (!$notrigger) {
1495
                    // Call trigger
1496
                    $result = $this->call_trigger('EXPENSE_REPORT_APPROVE', $fuser);
1497
1498
                    if ($result < 0) {
1499
                        $error++;
1500
                    }
1501
                    // End call triggers
1502
                }
1503
1504
                if (empty($error)) {
1505
                    $this->db->commit();
1506
                    return 1;
1507
                } else {
1508
                    $this->db->rollback();
1509
                    $this->error = $this->db->error();
1510
                    return -2;
1511
                }
1512
            } else {
1513
                $this->db->rollback();
1514
                $this->error = $this->db->lasterror();
1515
                return -1;
1516
            }
1517
        } else {
1518
            dol_syslog(get_class($this) . "::setApproved expensereport already with approve status", LOG_WARNING);
1519
        }
1520
1521
        return 0;
1522
    }
1523
1524
    /**
1525
     * setDeny
1526
     *
1527
     * @param User      $fuser      User
1528
     * @param string    $details    Details
1529
     * @param int       $notrigger  Disable triggers
1530
     * @return int
1531
     */
1532
    public function setDeny($fuser, $details, $notrigger = 0)
1533
    {
1534
        $now = dol_now();
1535
        $error = 0;
1536
1537
        // date de refus
1538
        if ($this->status != self::STATUS_REFUSED) {
1539
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
1540
            $sql .= " SET ref = '" . $this->db->escape($this->ref) . "', fk_statut = " . self::STATUS_REFUSED . ", fk_user_refuse = " . ((int) $fuser->id) . ",";
1541
            $sql .= " date_refuse='" . $this->db->idate($now) . "',";
1542
            $sql .= " detail_refuse='" . $this->db->escape($details) . "',";
1543
            $sql .= " fk_user_approve = NULL";
1544
            $sql .= " WHERE rowid = " . ((int) $this->id);
1545
            if ($this->db->query($sql)) {
1546
                $this->fk_statut = 99; // deprecated
1547
                $this->status = 99;
1548
                $this->fk_user_refuse = $fuser->id;
1549
                $this->detail_refuse = $details;
1550
                $this->date_refuse = $now;
1551
1552
                if (!$notrigger) {
1553
                    // Call trigger
1554
                    $result = $this->call_trigger('EXPENSE_REPORT_DENY', $fuser);
1555
1556
                    if ($result < 0) {
1557
                        $error++;
1558
                    }
1559
                    // End call triggers
1560
                }
1561
1562
                if (empty($error)) {
1563
                    $this->db->commit();
1564
                    return 1;
1565
                } else {
1566
                    $this->db->rollback();
1567
                    $this->error = $this->db->error();
1568
                    return -2;
1569
                }
1570
            } else {
1571
                $this->db->rollback();
1572
                $this->error = $this->db->lasterror();
1573
                return -1;
1574
            }
1575
        } else {
1576
            dol_syslog(get_class($this) . "::setDeny expensereport already with refuse status", LOG_WARNING);
1577
        }
1578
1579
        return 0;
1580
    }
1581
1582
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1583
    /**
1584
     * set_unpaid
1585
     *
1586
     *  @deprecated
1587
     *  @see setUnpaid()
1588
     * @param   User    $fuser      User
1589
     * @param   int     $notrigger  Disable triggers
1590
     * @return  int                 Return integer <0 if KO, >0 if OK
1591
     */
1592
    public function set_unpaid($fuser, $notrigger = 0)
1593
    {
1594
		// phpcs:enable
1595
        dol_syslog(get_class($this) . "::set_unpaid is deprecated, use setUnpaid instead", LOG_NOTICE);
1596
        return $this->setUnpaid($fuser, $notrigger);
1597
    }
1598
1599
    /**
1600
     * set_unpaid
1601
     *
1602
     * @param   User    $fuser      User
1603
     * @param   int     $notrigger  Disable triggers
1604
     * @return  int                 Return integer <0 if KO, >0 if OK
1605
     */
1606
    public function setUnpaid($fuser, $notrigger = 0)
1607
    {
1608
        $error = 0;
1609
1610
        if ($this->paid) {
1611
            $this->db->begin();
1612
1613
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
1614
            $sql .= " SET paid = 0, fk_statut = " . self::STATUS_APPROVED;
1615
            $sql .= " WHERE rowid = " . ((int) $this->id);
1616
1617
            dol_syslog(get_class($this) . "::set_unpaid", LOG_DEBUG);
1618
1619
            if ($this->db->query($sql)) {
1620
                if (!$notrigger) {
1621
                    // Call trigger
1622
                    $result = $this->call_trigger('EXPENSE_REPORT_UNPAID', $fuser);
1623
1624
                    if ($result < 0) {
1625
                        $error++;
1626
                    }
1627
                    // End call triggers
1628
                }
1629
1630
                if (empty($error)) {
1631
                    $this->db->commit();
1632
                    return 1;
1633
                } else {
1634
                    $this->db->rollback();
1635
                    $this->error = $this->db->error();
1636
                    return -2;
1637
                }
1638
            } else {
1639
                $this->db->rollback();
1640
                $this->error = $this->db->error();
1641
                return -1;
1642
            }
1643
        } else {
1644
            dol_syslog(get_class($this) . "::set_unpaid expensereport already with unpaid status", LOG_WARNING);
1645
        }
1646
1647
        return 0;
1648
    }
1649
1650
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1651
    /**
1652
     * set_cancel
1653
     *
1654
     * @param   User    $fuser      User
1655
     * @param   string  $detail     Detail
1656
     * @param   int     $notrigger  Disable triggers
1657
     * @return  int                 Return integer <0 if KO, >0 if OK
1658
     */
1659
    public function set_cancel($fuser, $detail, $notrigger = 0)
1660
    {
1661
		// phpcs:enable
1662
        $error = 0;
1663
        $this->date_cancel = $this->db->idate(dol_now());
1664
        if ($this->status != self::STATUS_CANCELED) {
1665
            $this->db->begin();
1666
1667
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element;
1668
            $sql .= " SET fk_statut = " . self::STATUS_CANCELED . ", fk_user_cancel = " . ((int) $fuser->id);
1669
            $sql .= ", date_cancel='" . $this->db->idate($this->date_cancel) . "'";
1670
            $sql .= ", detail_cancel='" . $this->db->escape($detail) . "'";
1671
            $sql .= " WHERE rowid = " . ((int) $this->id);
1672
1673
            dol_syslog(get_class($this) . "::set_cancel", LOG_DEBUG);
1674
1675
            if ($this->db->query($sql)) {
1676
                if (!$notrigger) {
1677
                    // Call trigger
1678
                    $result = $this->call_trigger('EXPENSE_REPORT_CANCEL', $fuser);
1679
1680
                    if ($result < 0) {
1681
                        $error++;
1682
                    }
1683
                    // End call triggers
1684
                }
1685
1686
                if (empty($error)) {
1687
                    $this->db->commit();
1688
                    return 1;
1689
                } else {
1690
                    $this->db->rollback();
1691
                    $this->error = $this->db->error();
1692
                    return -2;
1693
                }
1694
            } else {
1695
                $this->db->rollback();
1696
                $this->error = $this->db->error();
1697
                return -1;
1698
            }
1699
        } else {
1700
            dol_syslog(get_class($this) . "::set_cancel expensereport already with cancel status", LOG_WARNING);
1701
        }
1702
        return 0;
1703
    }
1704
1705
    /**
1706
     * Return next reference of expense report not already used
1707
     *
1708
     * @return    string|int<-2,-1>            free ref, or <0 if error
1709
     */
1710
    public function getNextNumRef()
1711
    {
1712
        global $langs, $conf;
1713
        $langs->load("trips");
1714
1715
        if (getDolGlobalString('EXPENSEREPORT_ADDON')) {
1716
            $mybool = false;
1717
1718
            $file = getDolGlobalString('EXPENSEREPORT_ADDON') . ".php";
1719
            $classname = getDolGlobalString('EXPENSEREPORT_ADDON');
1720
1721
            // Include file with class
1722
            $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
1723
            foreach ($dirmodels as $reldir) {
1724
                $dir = dol_buildpath($reldir . "core/modules/expensereport/");
1725
1726
                // Load file with numbering class (if found)
1727
                $mybool = ((bool) @include_once $dir . $file) || $mybool;
1728
            }
1729
1730
            if (!$mybool) {
1731
                dol_print_error(null, "Failed to include file " . $file);
1732
                return '';
1733
            }
1734
1735
            $obj = new $classname();
1736
            $numref = $obj->getNextValue($this);
1737
1738
            if ($numref != "") {
1739
                return $numref;
1740
            } else {
1741
                $this->error = $obj->error;
1742
                $this->errors = $obj->errors;
1743
                //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
1744
                return -1;
1745
            }
1746
        } else {
1747
            $this->error = "Error_EXPENSEREPORT_ADDON_NotDefined";
1748
            return -2;
1749
        }
1750
    }
1751
1752
    /**
1753
     * getTooltipContentArray
1754
     *
1755
     * @param array $params ex option, infologin
1756
     * @since v18
1757
     * @return array{picto:string,ref?:string,total_ht?:string,total_tva?:string,total_ttc?:string}
1758
     */
1759
    public function getTooltipContentArray($params)
1760
    {
1761
        global $conf, $langs;
1762
1763
        $langs->load('trips');
1764
1765
        $nofetch = !empty($params['nofetch']);
1766
        $moretitle = $params['moretitle'] ?? '';
1767
1768
        $datas = array();
1769
        $datas['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("ExpenseReport") . '</u>';
1770
        if (isset($this->status)) {
1771
            $datas['picto'] .= ' ' . $this->getLibStatut(5);
1772
        }
1773
        if ($moretitle) {
1774
            $datas['picto'] .= ' - ' . $moretitle;
1775
        }
1776
        if (!empty($this->ref)) {
1777
            $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
1778
        }
1779
        if (!empty($this->total_ht)) {
1780
            $datas['total_ht'] = '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1781
        }
1782
        if (!empty($this->total_tva)) {
1783
            $datas['total_tva'] = '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1784
        }
1785
        if (!empty($this->total_ttc)) {
1786
            $datas['total_ttc'] = '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1787
        }
1788
1789
        return $datas;
1790
    }
1791
1792
    /**
1793
     *  Return clicable name (with picto eventually)
1794
     *
1795
     *  @param      int     $withpicto                  0=No picto, 1=Include picto into link, 2=Only picto
1796
     *  @param      string  $option                     Where point the link ('', 'document', ..)
1797
     *  @param      int     $max                        Max length of shown ref
1798
     *  @param      int     $short                      1=Return just URL
1799
     *  @param      string  $moretitle                  Add more text to title tooltip
1800
     *  @param      int     $notooltip                  1=Disable tooltip
1801
     *  @param      int     $save_lastsearch_value      -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1802
     *  @return     string                              String with URL
1803
     */
1804
    public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1)
1805
    {
1806
        global $langs, $hookmanager;
1807
1808
        $result = '';
1809
1810
        $url = constant('BASE_URL') . '/expensereport/card.php?id=' . $this->id;
1811
1812
        if ($short) {
1813
            return $url;
1814
        }
1815
1816
        $params = [
1817
            'id' => $this->id,
1818
            'objecttype' => $this->element,
1819
            'option' => $option,
1820
            'moretitle' => $moretitle,
1821
            'nofetch' => 1,
1822
        ];
1823
        $classfortooltip = 'classfortooltip';
1824
        $dataparams = '';
1825
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1826
            $classfortooltip = 'classforajaxtooltip';
1827
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
1828
            $label = '';
1829
        } else {
1830
            $label = implode($this->getTooltipContentArray($params));
1831
        }
1832
1833
        if ($option != 'nolink') {
1834
            // Add param to save lastsearch_values or not
1835
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1836
            if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1837
                $add_save_lastsearch_values = 1;
1838
            }
1839
            if ($add_save_lastsearch_values) {
1840
                $url .= '&save_lastsearch_values=1';
1841
            }
1842
        }
1843
1844
        $ref = $this->ref;
1845
        if (empty($ref)) {
1846
            $ref = $this->id;
1847
        }
1848
1849
        $linkclose = '';
1850
        if (empty($notooltip)) {
1851
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1852
                $label = $langs->trans("ShowExpenseReport");
1853
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
1854
            }
1855
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
1856
            $linkclose .= $dataparams . ' class="' . $classfortooltip . '"';
1857
        }
1858
1859
        $linkstart = '<a href="' . $url . '"';
1860
        $linkstart .= $linkclose . '>';
1861
        $linkend = '</a>';
1862
1863
        $result .= $linkstart;
1864
        if ($withpicto) {
1865
            $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . '"'), 0, 0, $notooltip ? 0 : 1);
1866
        }
1867
        if ($withpicto != 2) {
1868
            $result .= ($max ? dol_trunc($ref, $max) : $ref);
1869
        }
1870
        $result .= $linkend;
1871
1872
        global $action;
1873
        $hookmanager->initHooks(array($this->element . 'dao'));
1874
        $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1875
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1876
        if ($reshook > 0) {
1877
            $result = $hookmanager->resPrint;
1878
        } else {
1879
            $result .= $hookmanager->resPrint;
1880
        }
1881
        return $result;
1882
    }
1883
1884
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1885
    /**
1886
     *  Update total of an expense report when you add a line.
1887
     *
1888
     *  @param    string    $ligne_total_ht     Amount without taxes
1889
     *  @param    string    $ligne_total_tva    Amount of all taxes
1890
     *  @return   int
1891
     */
1892
    public function update_totaux_add($ligne_total_ht, $ligne_total_tva)
1893
    {
1894
		// phpcs:enable
1895
        $this->total_ht += (float) $ligne_total_ht;
1896
        $this->total_tva += (float) $ligne_total_tva;
1897
        $this->total_ttc += $this->total_tva;
1898
1899
        $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET";
1900
        $sql .= " total_ht = " . $this->total_ht;
1901
        $sql .= " , total_ttc = " . $this->total_ttc;
1902
        $sql .= " , total_tva = " . $this->total_tva;
1903
        $sql .= " WHERE rowid = " . ((int) $this->id);
1904
1905
        $result = $this->db->query($sql);
1906
        if ($result) {
1907
            return 1;
1908
        } else {
1909
            $this->error = $this->db->error();
1910
            return -1;
1911
        }
1912
    }
1913
1914
    /**
1915
     * Add expense report line
1916
     *
1917
     * @param    float       $qty                      Qty
1918
     * @param    double      $up                       Unit price (price with tax)
1919
     * @param    int         $fk_c_type_fees           Type payment
1920
     * @param    int<-1,0>|string   $vatrate                  Vat rate (Can be '10' or '10 (ABC)')
1921
     * @param    string      $date                     Date
1922
     * @param    string      $comments                 Description
1923
     * @param    int         $fk_project               Project id
1924
     * @param    int         $fk_c_exp_tax_cat         Car category id
1925
     * @param    int         $type                     Type line
1926
     * @param    int         $fk_ecm_files             Id of ECM file to link to this expensereport line
1927
     * @return   int                                   Return integer <0 if KO, >0 if OK
1928
     */
1929
    public function addline($qty = 0, $up = 0, $fk_c_type_fees = 0, $vatrate = 0, $date = '', $comments = '', $fk_project = 0, $fk_c_exp_tax_cat = 0, $type = 0, $fk_ecm_files = 0)
1930
    {
1931
        global $langs, $mysoc;
1932
1933
        dol_syslog(get_class($this) . "::addline qty=$qty, up=$up, fk_c_type_fees=$fk_c_type_fees, vatrate=$vatrate, date=$date, fk_project=$fk_project, type=$type, comments=$comments", LOG_DEBUG);
1934
1935
        if ($this->status == self::STATUS_DRAFT) {
1936
            if (empty($qty)) {
1937
                $qty = 0;
1938
            }
1939
            if (empty($fk_c_type_fees) || $fk_c_type_fees < 0) {
1940
                $fk_c_type_fees = 0;
1941
            }
1942
            if (empty($fk_c_exp_tax_cat) || $fk_c_exp_tax_cat < 0) {
1943
                $fk_c_exp_tax_cat = 0;
1944
            }
1945
            if (empty($vatrate) || $vatrate < 0) {
1946
                $vatrate = 0;
1947
            }
1948
            if (empty($date)) {
1949
                $date = '';
1950
            }
1951
            if (empty($fk_project)) {
1952
                $fk_project = 0;
1953
            }
1954
1955
            $qty = (float) price2num($qty);
1956
            if (!preg_match('/\s*\((.*)\)/', $vatrate)) {
1957
                $vatrate = price2num($vatrate); // $txtva can have format '5.0 (XXX)' or '5'
1958
            }
1959
            $up = price2num($up);
1960
1961
            $this->db->begin();
1962
1963
            $this->line = new ExpenseReportLine($this->db);
1964
1965
            // We don't know seller and buyer for expense reports
1966
            $seller = $mysoc;           // We use same than current company (expense report are often done in same country)
1967
            $seller->tva_assuj = 1;     // Most seller uses vat
1968
            $buyer = new Societe($this->db);
0 ignored issues
show
Bug introduced by
The type Dolibarr\Code\ExpenseReport\Classes\Societe was not found. Did you mean Societe? If so, make sure to prefix the type with \.
Loading history...
1969
1970
            $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
1971
1972
            $vat_src_code = '';
1973
            $reg = array();
1974
            if (preg_match('/\s*\((.*)\)/', $vatrate, $reg)) {
1975
                $vat_src_code = $reg[1];
1976
                $vatrate = preg_replace('/\s*\(.*\)/', '', $vatrate); // Remove code into vatrate.
1977
            }
1978
            $vatrate = preg_replace('/\*/', '', $vatrate);
1979
1980
            $tmp = calcul_price_total($qty, $up, 0, $vatrate, -1, -1, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
1981
1982
            $this->line->value_unit = $up;
1983
1984
            $this->line->vat_src_code = $vat_src_code;
1985
            $this->line->vatrate = price2num($vatrate);
1986
            $this->line->localtax1_tx = $localtaxes_type[1];
1987
            $this->line->localtax2_tx = $localtaxes_type[3];
1988
            $this->line->localtax1_type = $localtaxes_type[0];
1989
            $this->line->localtax2_type = $localtaxes_type[2];
1990
1991
            $this->line->total_ttc = $tmp[2];
1992
            $this->line->total_ht = $tmp[0];
1993
            $this->line->total_tva = $tmp[1];
1994
            $this->line->total_localtax1 = $tmp[9];
1995
            $this->line->total_localtax2 = $tmp[10];
1996
1997
            $this->line->fk_expensereport = $this->id;
1998
            $this->line->qty = $qty;
1999
            $this->line->date = $date;
2000
            $this->line->fk_c_type_fees = $fk_c_type_fees;
2001
            $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
2002
            $this->line->comments = $comments;
2003
            $this->line->fk_projet = $fk_project; // deprecated
2004
            $this->line->fk_project = $fk_project;
2005
2006
            $this->line->fk_ecm_files = $fk_ecm_files;
2007
2008
            $this->applyOffset();
2009
            $this->checkRules($type, $seller);
2010
2011
            $result = $this->line->insert(0, true);
2012
            if ($result > 0) {
2013
                $result = $this->update_price(1); // This method is designed to add line from user input so total calculation must be done using 'auto' mode.
2014
                if ($result > 0) {
2015
                    $this->db->commit();
2016
                    return $this->line->id;
2017
                } else {
2018
                    $this->db->rollback();
2019
                    return -1;
2020
                }
2021
            } else {
2022
                $this->error = $this->line->error;
2023
                dol_syslog(get_class($this) . "::addline error=" . $this->error, LOG_ERR);
2024
                $this->db->rollback();
2025
                return -2;
2026
            }
2027
        } else {
2028
            dol_syslog(get_class($this) . "::addline status of expense report must be Draft to allow use of ->addline()", LOG_ERR);
2029
            $this->error = 'ErrorExpenseNotDraft';
2030
            return -3;
2031
        }
2032
    }
2033
2034
    /**
2035
     * Check constraint of rules and update price if needed
2036
     *
2037
     * @param   int     $type       Type of line
2038
     * @param   string  $seller     Seller, but actually he is unknown
2039
     * @return  boolean             true or false
2040
     */
2041
    public function checkRules($type = 0, $seller = '')
2042
    {
2043
        global $conf, $db, $langs, $mysoc;
2044
2045
        $langs->load('trips');
2046
2047
        // We don't know seller and buyer for expense reports
2048
        if (!is_object($seller)) {
2049
            $seller = $mysoc;           // We use same than current company (expense report are often done in same country)
2050
            $seller->tva_assuj = 1;     // Most seller uses vat
0 ignored issues
show
Bug introduced by
The property tva_assuj does not exist on string.
Loading history...
2051
        }
2052
2053
        $expensereportrule = new ExpenseReportRule($db);
2054
        $rulestocheck = $expensereportrule->getAllRule($this->line->fk_c_type_fees, $this->line->date, $this->fk_user_author);
2055
2056
        $violation = 0;
2057
        $rule_warning_message_tab = array();
2058
2059
        $current_total_ttc = $this->line->total_ttc;
2060
        $new_current_total_ttc = $this->line->total_ttc;
2061
2062
        // check if one is violated
2063
        foreach ($rulestocheck as $rule) {
2064
            if (in_array($rule->code_expense_rules_type, array('EX_DAY', 'EX_MON', 'EX_YEA'))) {
2065
                $amount_to_test = $this->line->getExpAmount($rule, $this->fk_user_author, $rule->code_expense_rules_type);
2066
            } else {
2067
                $amount_to_test = $current_total_ttc; // EX_EXP
2068
            }
2069
2070
            $amount_to_test = $amount_to_test - $current_total_ttc + $new_current_total_ttc; // if amount as been modified by a previous rule
2071
2072
            if ($amount_to_test > $rule->amount) {
2073
                $violation++;
2074
2075
                if ($rule->restrictive) {
2076
                    $this->error = 'ExpenseReportConstraintViolationError';
2077
                    $this->errors[] = $this->error;
2078
2079
                    $new_current_total_ttc -= $amount_to_test - $rule->amount; // ex, entered 16€, limit 12€, subtracts 4€;
2080
                    $rule_warning_message_tab[] = $langs->trans('ExpenseReportConstraintViolationError', $rule->id, price($amount_to_test, 0, $langs, 1, -1, -1, $conf->currency), price($rule->amount, 0, $langs, 1, -1, -1, $conf->currency));
2081
                } else {
2082
                    $this->error = 'ExpenseReportConstraintViolationWarning';
2083
                    $this->errors[] = $this->error;
2084
2085
                    $rule_warning_message_tab[] = $langs->trans('ExpenseReportConstraintViolationWarning', $rule->id, price($amount_to_test, 0, $langs, 1, -1, -1, $conf->currency), price($rule->amount, 0, $langs, 1, -1, -1, $conf->currency));
2086
                }
2087
2088
                // No break, we should test if another rule is violated
2089
            }
2090
        }
2091
2092
        $this->line->rule_warning_message = implode('\n', $rule_warning_message_tab);
2093
2094
        if ($violation > 0) {
2095
            $tmp = calcul_price_total($this->line->qty, $new_current_total_ttc / $this->line->qty, 0, $this->line->vatrate, 0, 0, 0, 'TTC', 0, $type, $seller);
2096
2097
            $this->line->value_unit = $tmp[5];
2098
            $this->line->total_ttc = $tmp[2];
2099
            $this->line->total_ht = $tmp[0];
2100
            $this->line->total_tva = $tmp[1];
2101
            $this->line->total_localtax1 = $tmp[9];
2102
            $this->line->total_localtax2 = $tmp[10];
2103
2104
            return false;
2105
        } else {
2106
            return true;
2107
        }
2108
    }
2109
2110
    /**
2111
     * Method to apply the offset if needed
2112
     *
2113
     * @param   int     $type       Type of line
2114
     * @param   string  $seller     Seller, but actually he is unknown
2115
     * @return  boolean             True=applied, False=not applied
2116
     */
2117
    public function applyOffset($type = 0, $seller = '')
2118
    {
2119
        global $mysoc;
2120
2121
        if (!getDolGlobalString('MAIN_USE_EXPENSE_IK')) {
2122
            return false;
2123
        }
2124
2125
        $userauthor = new User($this->db);
2126
        if ($userauthor->fetch($this->fk_user_author) <= 0) {
2127
            $this->error = 'ErrorCantFetchUser';
2128
            $this->errors[] = 'ErrorCantFetchUser';
2129
            return false;
2130
        }
2131
2132
        // We don't know seller and buyer for expense reports
2133
        if (!is_object($seller)) {
2134
            $seller = $mysoc;           // We use same than current company (expense report are often done in same country)
2135
            $seller->tva_assuj = 1;     // Most seller uses vat
0 ignored issues
show
Bug introduced by
The property tva_assuj does not exist on string.
Loading history...
2136
        }
2137
2138
        $expenseik = new ExpenseReportIk($this->db);
2139
        $range = $expenseik->getRangeByUser($userauthor, $this->line->fk_c_exp_tax_cat);
2140
2141
        if (empty($range)) {
2142
            $this->error = 'ErrorNoRangeAvailable';
2143
            $this->errors[] = 'ErrorNoRangeAvailable';
2144
            return false;
2145
        }
2146
2147
        if (getDolGlobalString('MAIN_EXPENSE_APPLY_ENTIRE_OFFSET')) {
2148
            $ikoffset = $range->ikoffset;
0 ignored issues
show
Bug introduced by
The property ikoffset does not exist on boolean.
Loading history...
2149
        } else {
2150
            $ikoffset = $range->ikoffset / 12; // The amount of offset is a global value for the year
2151
        }
2152
2153
        // Test if ikoffset has been applied for the current month
2154
        if (!$this->offsetAlreadyGiven()) {
2155
            $new_up = $range->coef + ($ikoffset / $this->line->qty);
0 ignored issues
show
Bug introduced by
The property coef does not exist on boolean.
Loading history...
2156
            $tmp = calcul_price_total($this->line->qty, $new_up, 0, $this->line->vatrate, 0, 0, 0, 'TTC', 0, $type, $seller);
2157
2158
            $this->line->value_unit = $tmp[5];
2159
            $this->line->total_ttc = $tmp[2];
2160
            $this->line->total_ht = $tmp[0];
2161
            $this->line->total_tva = $tmp[1];
2162
            $this->line->total_localtax1 = $tmp[9];
2163
            $this->line->total_localtax2 = $tmp[10];
2164
2165
            return true;
2166
        }
2167
2168
        return false;
2169
    }
2170
2171
    /**
2172
     * If the sql find any rows then the ikoffset is already given (ikoffset is applied at the first expense report line)
2173
     *
2174
     * @return bool
2175
     */
2176
    public function offsetAlreadyGiven()
2177
    {
2178
        $sql = 'SELECT e.rowid FROM ' . MAIN_DB_PREFIX . 'expensereport e';
2179
        $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "expensereport_det d ON (e.rowid = d.fk_expensereport)";
2180
        $sql .= " INNER JOIN " . MAIN_DB_PREFIX . "c_type_fees f ON (d.fk_c_type_fees = f.id AND f.code = 'EX_KME')";
2181
        $sql .= " WHERE e.fk_user_author = " . (int) $this->fk_user_author;
2182
        $sql .= " AND YEAR(d.date) = '" . dol_print_date($this->line->date, '%Y') . "' AND MONTH(d.date) = '" . dol_print_date($this->line->date, '%m') . "'";
2183
        if (!empty($this->line->id)) {
2184
            $sql .= ' AND d.rowid <> ' . ((int) $this->line->id);
2185
        }
2186
2187
        dol_syslog(get_class($this) . "::offsetAlreadyGiven");
2188
        $resql = $this->db->query($sql);
2189
        if ($resql) {
2190
            $num = $this->db->num_rows($resql);
2191
            if ($num > 0) {
2192
                return true;
2193
            }
2194
        } else {
2195
            dol_print_error($this->db);
2196
        }
2197
2198
        return false;
2199
    }
2200
2201
    /**
2202
     * Update an expense report line.
2203
     *
2204
     * @param   int         $rowid                  Line to edit
2205
     * @param   int         $type_fees_id           Type payment
2206
     * @param   int         $projet_id              Project id
2207
     * @param   double      $vatrate                Vat rate. Can be '8.5' or '8.5* (8.5NPROM...)'
2208
     * @param   string      $comments               Description
2209
     * @param   float       $qty                    Qty
2210
     * @param   double      $value_unit             Unit price (with taxes)
2211
     * @param   int         $date                   Date
2212
     * @param   int         $expensereport_id       Expense report id
2213
     * @param   int         $fk_c_exp_tax_cat       Id of category of car
2214
     * @param   int         $fk_ecm_files           Id of ECM file to link to this expensereport line
2215
     * @param   int         $notrigger              1=No trigger
2216
     * @return  int                                 Return integer <0 if KO, >0 if OK
2217
     */
2218
    public function updateline($rowid, $type_fees_id, $projet_id, $vatrate, $comments, $qty, $value_unit, $date, $expensereport_id, $fk_c_exp_tax_cat = 0, $fk_ecm_files = 0, $notrigger = 0)
2219
    {
2220
        global $user, $mysoc;
2221
2222
        if ($this->status == self::STATUS_DRAFT || $this->status == self::STATUS_REFUSED) {
2223
            $this->db->begin();
2224
2225
            $error = 0;
2226
            $type = 0; // TODO What if type is service ?
2227
2228
            // We don't know seller and buyer for expense reports
2229
            $seller = $mysoc;           // We use same than current company (expense report are often done in same country)
2230
            $seller->tva_assuj = 1;     // Most seller uses vat
2231
            $seller->localtax1_assuj = $mysoc->localtax1_assuj;     // We don't know, we reuse the state of company
2232
            $seller->localtax2_assuj = $mysoc->localtax1_assuj;     // We don't know, we reuse the state of company
2233
            $buyer = new Societe($this->db);
2234
2235
            $localtaxes_type = getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
2236
2237
            // Clean vat code
2238
            $reg = array();
2239
            $vat_src_code = '';
2240
            if (preg_match('/\((.*)\)/', (string) $vatrate, $reg)) {
2241
                $vat_src_code = $reg[1];
2242
                $vatrate = preg_replace('/\s*\(.*\)/', '', (string) $vatrate); // Remove code into vatrate.
2243
            }
2244
            $vatrate = preg_replace('/\*/', '', $vatrate);
2245
2246
            $tmp = calcul_price_total($qty, $value_unit, 0, $vatrate, -1, -1, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
2247
            //var_dump($vatrate);var_dump($localtaxes_type);var_dump($tmp);exit;
2248
            // calcul total of line
2249
            //$total_ttc  = price2num($qty*$value_unit, 'MT');
2250
2251
            $tx_tva = 1 + (float) $vatrate / 100;
2252
2253
            $this->line = new ExpenseReportLine($this->db);
2254
            $this->line->comments        = $comments;
2255
            $this->line->qty             = $qty;
2256
            $this->line->value_unit      = $value_unit;
2257
            $this->line->date            = $date;
2258
2259
            $this->line->fk_expensereport = $expensereport_id;
2260
            $this->line->fk_c_type_fees  = $type_fees_id;
2261
            $this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
2262
            $this->line->fk_projet       = $projet_id; // deprecated
2263
            $this->line->fk_project      = $projet_id;
2264
2265
            $this->line->vat_src_code = $vat_src_code;
2266
            $this->line->vatrate = price2num($vatrate);
2267
            $this->line->localtax1_tx = $localtaxes_type[1];
2268
            $this->line->localtax2_tx = $localtaxes_type[3];
2269
            $this->line->localtax1_type = $localtaxes_type[0];
2270
            $this->line->localtax2_type = $localtaxes_type[2];
2271
2272
            $this->line->total_ttc = $tmp[2];
2273
            $this->line->total_ht = $tmp[0];
2274
            $this->line->total_tva = $tmp[1];
2275
            $this->line->total_localtax1 = $tmp[9];
2276
            $this->line->total_localtax2 = $tmp[10];
2277
2278
            $this->line->fk_ecm_files = $fk_ecm_files;
2279
2280
            $this->line->id = ((int) $rowid);
2281
2282
            // Select des infos sur le type fees
2283
            $sql = "SELECT c.code as code_type_fees, c.label as label_type_fees";
2284
            $sql .= " FROM " . MAIN_DB_PREFIX . "c_type_fees as c";
2285
            $sql .= " WHERE c.id = " . ((int) $type_fees_id);
2286
            $resql = $this->db->query($sql);
2287
            if ($resql) {
2288
                $objp_fees = $this->db->fetch_object($resql);
2289
                $this->line->type_fees_code      = $objp_fees->code_type_fees;
2290
                $this->line->type_fees_libelle   = $objp_fees->label_type_fees;
2291
                $this->db->free($resql);
2292
            }
2293
2294
            // Select des information du projet
2295
            $sql = "SELECT p.ref as ref_projet, p.title as title_projet";
2296
            $sql .= " FROM " . MAIN_DB_PREFIX . "projet as p";
2297
            $sql .= " WHERE p.rowid = " . ((int) $projet_id);
2298
            $resql = $this->db->query($sql);
2299
            if ($resql) {
2300
                $objp_projet = $this->db->fetch_object($resql);
2301
                $this->line->projet_ref          = $objp_projet->ref_projet;
2302
                $this->line->projet_title        = $objp_projet->title_projet;
2303
                $this->db->free($resql);
2304
            }
2305
2306
            $this->applyOffset();
2307
            $this->checkRules();
2308
2309
            $result = $this->line->update($user);
2310
            if ($result < 0) {
2311
                $error++;
2312
            }
2313
2314
            if (!$error && !$notrigger) {
2315
                // Call triggers
2316
                $result = $this->call_trigger('EXPENSE_REPORT_DET_MODIFY', $user);
2317
                if ($result < 0) {
2318
                    $error++;
2319
                }
2320
                // End call triggers
2321
            }
2322
2323
            if (!$error) {
2324
                $this->db->commit();
2325
                return 1;
2326
            } else {
2327
                $this->error = $this->line->error;
2328
                $this->errors = $this->line->errors;
2329
                $this->db->rollback();
2330
                return -2;
2331
            }
2332
        }
2333
2334
        return 0;
2335
    }
2336
2337
    /**
2338
     * deleteline
2339
     *
2340
     * @param   int         $rowid          Row id
2341
     * @param   User|string $fuser          User
2342
     * @param   int<0,1>    $notrigger      1=No trigger
2343
     * @return  int<-1,1>                   Return integer <0 if KO, >0 if OK
2344
     */
2345
    public function deleteLine($rowid, $fuser = '', $notrigger = 0)
2346
    {
2347
        $error = 0;
2348
2349
        $this->db->begin();
2350
2351
        if (!$notrigger) {
2352
            // Call triggers
2353
            $result = $this->call_trigger('EXPENSE_REPORT_DET_DELETE', $fuser);
2354
            if ($result < 0) {
2355
                $error++;
2356
            }
2357
            // End call triggers
2358
        }
2359
2360
        $sql = ' DELETE FROM ' . MAIN_DB_PREFIX . $this->table_element_line;
2361
        $sql .= ' WHERE rowid = ' . ((int) $rowid);
2362
2363
        dol_syslog(get_class($this) . "::deleteline sql=" . $sql);
2364
        $result = $this->db->query($sql);
2365
2366
        if (!$result || $error > 0) {
2367
            $this->error = $this->db->error();
2368
            dol_syslog(get_class($this) . "::deleteline  Error " . $this->error, LOG_ERR);
2369
            $this->db->rollback();
2370
            return -1;
2371
        }
2372
2373
        $this->update_price(1);
2374
2375
        $this->db->commit();
2376
2377
        return 1;
2378
    }
2379
2380
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2381
    /**
2382
     * periode_existe
2383
     *
2384
     * @param   User       $fuser          User
2385
     * @param   integer    $date_debut     Start date
2386
     * @param   integer    $date_fin       End date
2387
     * @return  int                        Return integer <0 if KO, >0 if OK
2388
     */
2389
    public function periode_existe($fuser, $date_debut, $date_fin)
2390
    {
2391
        global $conf;
2392
2393
		// phpcs:enable
2394
        $sql = "SELECT rowid, date_debut, date_fin";
2395
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
2396
        $sql .= " WHERE entity = " . ((int) $conf->entity); // not shared, only for the current entity
2397
        $sql .= " AND fk_user_author = " . ((int) $fuser->id);
2398
2399
        dol_syslog(get_class($this) . "::periode_existe sql=" . $sql);
2400
        $result = $this->db->query($sql);
2401
        if ($result) {
2402
            $num_rows = $this->db->num_rows($result);
2403
            $i = 0;
2404
2405
            if ($num_rows > 0) {
2406
                $date_d_form = $date_debut;
2407
                $date_f_form = $date_fin;
2408
2409
                while ($i < $num_rows) {
2410
                    $objp = $this->db->fetch_object($result);
2411
2412
                    $date_d_req = $this->db->jdate($objp->date_debut); // 3
2413
                    $date_f_req = $this->db->jdate($objp->date_fin); // 4
2414
2415
                    if (!($date_f_form < $date_d_req || $date_d_form > $date_f_req)) {
2416
                        return $objp->rowid;
2417
                    }
2418
2419
                    $i++;
2420
                }
2421
2422
                return 0;
2423
            } else {
2424
                return 0;
2425
            }
2426
        } else {
2427
            $this->error = $this->db->lasterror();
2428
            dol_syslog(get_class($this) . "::periode_existe  Error " . $this->error, LOG_ERR);
2429
            return -1;
2430
        }
2431
    }
2432
2433
2434
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2435
    /**
2436
     * Return list of people with permission to validate expense reports.
2437
     * Search for permission "approve expense report"
2438
     *
2439
     * @return  array|int       Array of user ids, <0 if KO
2440
     */
2441
    public function fetch_users_approver_expensereport()
2442
    {
2443
		// phpcs:enable
2444
        $users_validator = array();
2445
2446
        $sql = "SELECT DISTINCT ur.fk_user";
2447
        $sql .= " FROM " . MAIN_DB_PREFIX . "user_rights as ur, " . MAIN_DB_PREFIX . "rights_def as rd";
2448
        $sql .= " WHERE ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
2449
        $sql .= " UNION";
2450
        $sql .= " SELECT DISTINCT ugu.fk_user";
2451
        $sql .= " FROM " . MAIN_DB_PREFIX . "usergroup_user as ugu, " . MAIN_DB_PREFIX . "usergroup_rights as ur, " . MAIN_DB_PREFIX . "rights_def as rd";
2452
        $sql .= " WHERE ugu.fk_usergroup = ur.fk_usergroup AND ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'"; // Permission 'Approve';
2453
        //print $sql;
2454
2455
        dol_syslog(get_class($this) . "::fetch_users_approver_expensereport sql=" . $sql);
2456
        $result = $this->db->query($sql);
2457
        if ($result) {
2458
            $num_rows = $this->db->num_rows($result);
2459
            $i = 0;
2460
            while ($i < $num_rows) {
2461
                $objp = $this->db->fetch_object($result);
2462
                array_push($users_validator, $objp->fk_user);
2463
                $i++;
2464
            }
2465
            return $users_validator;
2466
        } else {
2467
            $this->error = $this->db->lasterror();
2468
            dol_syslog(get_class($this) . "::fetch_users_approver_expensereport  Error " . $this->error, LOG_ERR);
2469
            return -1;
2470
        }
2471
    }
2472
2473
    /**
2474
     *  Create a document onto disk according to template module.
2475
     *
2476
     *  @param      string      $modele         Force le mnodele a utiliser ('' to not force)
2477
     *  @param      Translate   $outputlangs    object lang a utiliser pour traduction
2478
     *  @param      int         $hidedetails    Hide details of lines
2479
     *  @param      int         $hidedesc       Hide description
2480
     *  @param      int         $hideref        Hide ref
2481
     *  @param      ?array  $moreparams     Array to provide more information
2482
     *  @return     int                         0 if KO, 1 if OK
2483
     */
2484
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2485
    {
2486
        $outputlangs->load("trips");
2487
2488
        if (!dol_strlen($modele)) {
2489
            if (!empty($this->model_pdf)) {
2490
                $modele = $this->model_pdf;
2491
            } elseif (getDolGlobalString('EXPENSEREPORT_ADDON_PDF')) {
2492
                $modele = getDolGlobalString('EXPENSEREPORT_ADDON_PDF');
2493
            }
2494
        }
2495
2496
        if (!empty($modele)) {
2497
            $modelpath = "core/modules/expensereport/doc/";
2498
2499
            return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2500
        } else {
2501
            return 0;
2502
        }
2503
    }
2504
2505
    /**
2506
     * List of types
2507
     *
2508
     * @param   int     $active     Active or not
2509
     * @return  array
2510
     */
2511
    public function listOfTypes($active = 1)
2512
    {
2513
        global $langs;
2514
        $ret = array();
2515
        $sql = "SELECT id, code, label";
2516
        $sql .= " FROM " . MAIN_DB_PREFIX . "c_type_fees";
2517
        $sql .= " WHERE active = " . ((int) $active);
2518
        dol_syslog(get_class($this) . "::listOfTypes", LOG_DEBUG);
2519
        $result = $this->db->query($sql);
2520
        if ($result) {
2521
            $num = $this->db->num_rows($result);
2522
            $i = 0;
2523
            while ($i < $num) {
2524
                $obj = $this->db->fetch_object($result);
2525
                $ret[$obj->code] = (($langs->transnoentitiesnoconv($obj->code) != $obj->code) ? $langs->transnoentitiesnoconv($obj->code) : $obj->label);
2526
                $i++;
2527
            }
2528
        } else {
2529
            dol_print_error($this->db);
2530
        }
2531
        return $ret;
2532
    }
2533
2534
    /**
2535
     *      Load the indicators this->nb for the state board
2536
     *
2537
     *      @return     int         Return integer <0 if KO, >0 if OK
2538
     */
2539
    public function loadStateBoard()
2540
    {
2541
        global $user;
2542
2543
        $this->nb = array();
2544
2545
        $sql = "SELECT count(ex.rowid) as nb";
2546
        $sql .= " FROM " . MAIN_DB_PREFIX . "expensereport as ex";
2547
        $sql .= " WHERE ex.fk_statut > 0";
2548
        $sql .= " AND ex.entity IN (" . getEntity('expensereport') . ")";
2549
        if (!$user->hasRight('expensereport', 'readall')) {
2550
            $userchildids = $user->getAllChildIds(1);
2551
            $sql .= " AND (ex.fk_user_author IN (" . $this->db->sanitize(implode(',', $userchildids)) . ")";
2552
            $sql .= " OR ex.fk_user_validator IN (" . $this->db->sanitize(implode(',', $userchildids)) . "))";
2553
        }
2554
2555
        $resql = $this->db->query($sql);
2556
        if ($resql) {
2557
            while ($obj = $this->db->fetch_object($resql)) {
2558
                $this->nb["expensereports"] = $obj->nb;
2559
            }
2560
            $this->db->free($resql);
2561
            return 1;
2562
        } else {
2563
            dol_print_error($this->db);
2564
            $this->error = $this->db->error();
2565
            return -1;
2566
        }
2567
    }
2568
2569
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2570
    /**
2571
     *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2572
     *
2573
     *      @param  User    $user           Object user
2574
     *      @param  string  $option         'topay' or 'toapprove'
2575
     *      @return WorkboardResponse|int   Return integer <0 if KO, WorkboardResponse if OK
2576
     */
2577
    public function load_board($user, $option = 'topay')
2578
    {
2579
		// phpcs:enable
2580
        global $conf, $langs;
2581
2582
        if ($user->socid) {
2583
            return -1; // protection pour eviter appel par utilisateur externe
2584
        }
2585
2586
        $now = dol_now();
2587
2588
        $sql = "SELECT ex.rowid, ex.date_valid";
2589
        $sql .= " FROM " . MAIN_DB_PREFIX . "expensereport as ex";
2590
        if ($option == 'toapprove') {
2591
            $sql .= " WHERE ex.fk_statut = " . self::STATUS_VALIDATED;
2592
        } else {
2593
            $sql .= " WHERE ex.fk_statut = " . self::STATUS_APPROVED;
2594
        }
2595
        $sql .= " AND ex.entity IN (" . getEntity('expensereport') . ")";
2596
        if (!$user->hasRight('expensereport', 'readall')) {
2597
            $userchildids = $user->getAllChildIds(1);
2598
            $sql .= " AND (ex.fk_user_author IN (" . $this->db->sanitize(implode(',', $userchildids)) . ")";
2599
            $sql .= " OR ex.fk_user_validator IN (" . $this->db->sanitize(implode(',', $userchildids)) . "))";
2600
        }
2601
2602
        $resql = $this->db->query($sql);
2603
        if ($resql) {
2604
            $langs->load("trips");
2605
2606
            $response = new WorkboardResponse();
2607
            if ($option == 'toapprove') {
2608
                $response->warning_delay = $conf->expensereport->approve->warning_delay / 60 / 60 / 24;
2609
                $response->label = $langs->trans("ExpenseReportsToApprove");
2610
                $response->labelShort = $langs->trans("ToApprove");
2611
                $response->url = constant('BASE_URL') . '/expensereport/list.php?mainmenu=hrm&amp;statut=' . self::STATUS_VALIDATED;
2612
            } else {
2613
                $response->warning_delay = $conf->expensereport->payment->warning_delay / 60 / 60 / 24;
2614
                $response->label = $langs->trans("ExpenseReportsToPay");
2615
                $response->labelShort = $langs->trans("StatusToPay");
2616
                $response->url = constant('BASE_URL') . '/expensereport/list.php?mainmenu=hrm&amp;statut=' . self::STATUS_APPROVED;
2617
            }
2618
            $response->img = img_object('', "trip");
2619
2620
            while ($obj = $this->db->fetch_object($resql)) {
2621
                $response->nbtodo++;
2622
2623
                if ($option == 'toapprove') {
2624
                    if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->approve->warning_delay)) {
2625
                        $response->nbtodolate++;
2626
                    }
2627
                } else {
2628
                    if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->payment->warning_delay)) {
2629
                        $response->nbtodolate++;
2630
                    }
2631
                }
2632
            }
2633
2634
            return $response;
2635
        } else {
2636
            dol_print_error($this->db);
2637
            $this->error = $this->db->error();
2638
            return -1;
2639
        }
2640
    }
2641
2642
    /**
2643
     * Return if an expense report is late or not
2644
     *
2645
     * @param  string  $option          'topay' or 'toapprove'
2646
     * @return boolean                  True if late, False if not late
2647
     */
2648
    public function hasDelay($option)
2649
    {
2650
        global $conf;
2651
2652
        // Only valid expenses reports
2653
        if ($option == 'toapprove' && $this->status != 2) {
2654
            return false;
2655
        }
2656
        if ($option == 'topay' && $this->status != 5) {
2657
            return false;
2658
        }
2659
2660
        $now = dol_now();
2661
        if ($option == 'toapprove') {
2662
            return (!empty($this->datevalid) ? $this->datevalid : $this->date_valid) < ($now - $conf->expensereport->approve->warning_delay);
2663
        } else {
2664
            return (!empty($this->datevalid) ? $this->datevalid : $this->date_valid) < ($now - $conf->expensereport->payment->warning_delay);
2665
        }
2666
    }
2667
2668
    /**
2669
     *  Return if object was dispatched into bookkeeping
2670
     *
2671
     *  @return     int         Return integer <0 if KO, 0=no, 1=yes
2672
     */
2673
    public function getVentilExportCompta()
2674
    {
2675
        $alreadydispatched = 0;
2676
2677
        $type = 'expense_report';
2678
2679
        $sql = " SELECT COUNT(ab.rowid) as nb FROM " . MAIN_DB_PREFIX . "accounting_bookkeeping as ab WHERE ab.doc_type='" . $this->db->escape($type) . "' AND ab.fk_doc = " . ((int) $this->id);
2680
        $resql = $this->db->query($sql);
2681
        if ($resql) {
2682
            $obj = $this->db->fetch_object($resql);
2683
            if ($obj) {
2684
                $alreadydispatched = $obj->nb;
2685
            }
2686
        } else {
2687
            $this->error = $this->db->lasterror();
2688
            return -1;
2689
        }
2690
2691
        if ($alreadydispatched) {
2692
            return 1;
2693
        }
2694
        return 0;
2695
    }
2696
2697
    /**
2698
     *  Return amount of payments already done
2699
     *
2700
     *  @return     int                     Amount of payment already done, <0 if KO
2701
     */
2702
    public function getSumPayments()
2703
    {
2704
        $table = 'payment_expensereport';
2705
        $field = 'fk_expensereport';
2706
2707
        $sql = 'SELECT sum(amount) as amount';
2708
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $table;
2709
        $sql .= " WHERE " . $field . " = " . ((int) $this->id);
2710
2711
        dol_syslog(get_class($this) . "::getSumPayments", LOG_DEBUG);
2712
        $resql = $this->db->query($sql);
2713
        if ($resql) {
2714
            $obj = $this->db->fetch_object($resql);
2715
            $this->db->free($resql);
2716
            return (empty($obj->amount) ? 0 : $obj->amount);
2717
        } else {
2718
            $this->error = $this->db->lasterror();
2719
            return -1;
2720
        }
2721
    }
2722
2723
    /**
2724
     *  \brief Compute the cost of the kilometers expense based on the number of kilometers and the vehicle category
2725
     *
2726
     *  @param     int      $fk_cat           Category of the vehicle used
2727
     *  @param     float    $qty              Number of kilometers
2728
     *  @param     float    $tva              VAT rate
2729
     *  @return    int                        Return integer <0 if KO, total ttc if OK
2730
     */
2731
    public function computeTotalKm($fk_cat, $qty, $tva)
2732
    {
2733
        global $langs, $db, $conf;
2734
2735
        $cumulYearQty = 0;
2736
        $ranges = array();
2737
        $coef = 0;
2738
2739
2740
        if ($fk_cat < 0) {
2741
            $this->error = $langs->trans('ErrorBadParameterCat');
2742
            return -1;
2743
        }
2744
2745
        if ($qty <= 0) {
2746
            $this->error = $langs->trans('ErrorBadParameterQty');
2747
            return -1;
2748
        }
2749
2750
        $currentUser = new User($db);
2751
        $currentUser->fetch($this->fk_user);
2752
        $currentUser->getrights('expensereport');
2753
        //Clean
2754
        $qty = (float) price2num($qty);
2755
2756
        $sql  = " SELECT r.range_ik, t.ikoffset, t.coef";
2757
        $sql .= " FROM " . MAIN_DB_PREFIX . "expensereport_ik t";
2758
        $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_exp_tax_range r ON r.rowid = t.fk_range";
2759
        $sql .= " WHERE t.fk_c_exp_tax_cat = " . (int) $fk_cat;
2760
        $sql .= " ORDER BY r.range_ik ASC";
2761
2762
        dol_syslog("expenseReport::computeTotalkm sql=" . $sql, LOG_DEBUG);
2763
2764
        $result = $this->db->query($sql);
2765
2766
        if ($result) {
2767
            if ($conf->global->EXPENSEREPORT_CALCULATE_MILEAGE_EXPENSE_COEFFICIENT_ON_CURRENT_YEAR) {
2768
                $arrayDate = dol_getdate(dol_now());
2769
                $sql = " SELECT count(n.qty) as cumul FROM " . MAIN_DB_PREFIX . "expensereport_det n";
2770
                $sql .= " LEFT JOIN  " . MAIN_DB_PREFIX . "expensereport e ON e.rowid = n.fk_expensereport";
2771
                $sql .= " LEFT JOIN  " . MAIN_DB_PREFIX . "c_type_fees tf ON tf.id = n.fk_c_type_fees";
2772
                $sql .= " WHERE e.fk_user_author = " . (int) $this->fk_user_author;
2773
                $sql .= " AND YEAR(n.date) = " . (int) $arrayDate['year'];
2774
                $sql .= " AND tf.code = 'EX_KME' ";
2775
                $sql .= " AND e.fk_statut = " . (int) ExpenseReport::STATUS_VALIDATED;
2776
2777
                $resql = $this->db->query($sql);
2778
2779
                if ($resql) {
2780
                    $obj = $this->db->fetch_object($resql);
2781
                    $cumulYearQty = $obj->cumul;
2782
                }
2783
2784
                $qty += (float) $cumulYearQty;
2785
            }
2786
2787
            $num = $this->db->num_rows($result);
2788
2789
            if ($num) {
2790
                for ($i = 0; $i < $num; $i++) {
2791
                    $obj = $this->db->fetch_object($result);
2792
2793
                    $ranges[$i] = $obj;
2794
                }
2795
2796
2797
                for ($i = 0; $i < $num; $i++) {
2798
                    if ($i < ($num - 1)) {
2799
                        if ($qty > $ranges[$i]->range_ik && $qty < $ranges[$i + 1]->range_ik) {
2800
                            $coef = $ranges[$i]->coef;
2801
                            $offset = $ranges[$i]->ikoffset;
2802
                        }
2803
                    } else {
2804
                        if ($qty > $ranges[$i]->range_ik) {
2805
                            $coef = $ranges[$i]->coef;
2806
                            $offset = $ranges[$i]->ikoffset;
2807
                        }
2808
                    }
2809
                }
2810
                $total_ht = $coef;
2811
                return $total_ht;
2812
            } else {
2813
                $this->error = $langs->trans('TaxUndefinedForThisCategory');
2814
                return 0;
2815
            }
2816
        } else {
2817
            $this->error = $this->db->error() . " sql=" . $sql;
2818
2819
            return -1;
2820
        }
2821
    }
2822
2823
    /**
2824
     *  Return clickable link of object (with optional picto)
2825
     *
2826
     *  @param      string      $option                 Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link)
2827
     *  @param      array       $arraydata              Array of data
2828
     *  @return     string                              HTML Code for Kanban thumb.
2829
     */
2830
    public function getKanbanView($option = '', $arraydata = null)
2831
    {
2832
        global $langs;
2833
2834
        $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
2835
2836
        $return = '<div class="box-flex-item box-flex-grow-zero">';
2837
        $return .= '<div class="info-box info-box-sm">';
2838
        $return .= '<span class="info-box-icon bg-infobox-action">';
2839
        $return .= img_picto('', $this->picto);
2840
        $return .= '</span>';
2841
        $return .= '<div class="info-box-content">';
2842
        $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">' . (method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref) . '</span>';
2843
        if ($selected >= 0) {
2844
            $return .= '<input id="cb' . $this->id . '" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="' . $this->id . '"' . ($selected ? ' checked="checked"' : '') . '>';
2845
        }
2846
        if (array_key_exists('userauthor', $arraydata)) {
2847
            $return .= '<br><span class="info-box-label">' . $arraydata['userauthor']->getNomUrl(-1) . '</span>';
2848
        }
2849
        if (property_exists($this, 'date_debut') && property_exists($this, 'date_fin')) {
2850
            $return .= '<br><span class="info-box-label">' . dol_print_date($this->date_debut, 'day') . '</span>';
2851
            $return .= ' <span class="opacitymedium">' . $langs->trans("To") . '</span> ';
2852
            $return .= '<span class="info-box-label">' . dol_print_date($this->date_fin, 'day') . '</span>';
2853
        }
2854
        if (method_exists($this, 'getLibStatut')) {
2855
            $return .= '<br><div class="info-box-status">' . $this->getLibStatut(3) . '</div>';
2856
        }
2857
        $return .= '</div>';
2858
        $return .= '</div>';
2859
        $return .= '</div>';
2860
        return $return;
2861
    }
2862
}
2863