Passed
Pull Request — dev (#6)
by Rafael
79:24 queued 24:08
created

ExpenseReport::periode_existe()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 41
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 27
nc 5
nop 3
dl 0
loc 41
rs 8.8657
c 0
b 0
f 0
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\Code\Core\Classes\WorkboardResponse;
30
use Dolibarr\Core\Base\CommonObject;
31
32
/**
33
 *       \file       htdocs/expensereport/class/expensereport.class.php
34
 *       \ingroup    expensereport
35
 *       \brief      File to manage Expense Reports
36
 */
37
38
/**
39
 * Class to manage Trips and Expenses
40
 */
41
class ExpenseReport extends CommonObject
42
{
43
    /**
44
     * @var string ID to identify managed object
45
     */
46
    public $element = 'expensereport';
47
48
    /**
49
     * @var string Name of table without prefix where object is stored
50
     */
51
    public $table_element = 'expensereport';
52
53
    /**
54
     * @var string table element line name
55
     */
56
    public $table_element_line = 'expensereport_det';
57
58
    /**
59
     * @var string Fieldname with ID of parent key if this field has a parent
60
     */
61
    public $fk_element = 'fk_expensereport';
62
63
    /**
64
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
65
     */
66
    public $picto = 'trip';
67
68
    /**
69
     * @var ExpenseReportLine[] array of expensereport lines
70
     */
71
    public $lines = array();
72
73
    /**
74
     * @var ExpenseReportLine expensereport lines
75
     */
76
    public $line;
77
78
    /**
79
     * @var int|string
80
     */
81
    public $date_debut;
82
83
    /**
84
     * @var int|string
85
     */
86
    public $date_fin;
87
88
    /**
89
     * @var int|string
90
     */
91
    public $date_approbation;
92
93
    /**
94
     * @var int ID
95
     */
96
    public $fk_user;
97
98
    /**
99
     * @var int ID
100
     */
101
    public $user_approve_id;
102
103
    /**
104
     * 0=draft, 2=validated (attente approb), 4=canceled, 5=approved, 6=paid, 99=denied
105
     *
106
     * @var int     Status
107
     */
108
    public $status;
109
110
    /**
111
     * 0=draft, 2=validated (attente approb), 4=canceled, 5=approved, 6=paid, 99=denied
112
     *
113
     * @var int     Status
114
     * @deprecated
115
     */
116
    public $fk_statut;
117
118
    /**
119
     * @var int ID
120
     */
121
    public $fk_c_paiement;
122
123
    /**
124
     * @var int ID
125
     */
126
    public $modepaymentid;
127
128
    public $paid;
129
130
    // Paiement
131
    /**
132
     * @var string Firstname Lastname
133
     */
134
    public $user_paid_infos;
135
136
    /**
137
     * @var string Firstname Lastname
138
     */
139
    public $user_author_infos;
140
141
    /**
142
     * @var string Firstname Lastname
143
     */
144
    public $user_validator_infos;
145
146
    public $rule_warning_message;
147
148
    // ACTIONS
149
150
    // Create
151
    /**
152
     * @var int|string
153
     */
154
    public $date_create;
155
156
    /**
157
     * @var int ID of user creator
158
     */
159
    public $fk_user_creat;
160
161
    /**
162
     * @var int ID of user who reclaim expense report
163
     */
164
    public $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
165
166
    // Update
167
    /**
168
     * @var int|string
169
     */
170
    public $date_modif;
171
172
    /**
173
     * @var int ID
174
     */
175
    public $fk_user_modif;
176
177
    // Refus
178
    /**
179
     * @var int|string
180
     */
181
    public $date_refuse;
182
183
    /**
184
     * @var string
185
     */
186
    public $detail_refuse;
187
188
    /**
189
     * @var int ID
190
     */
191
    public $fk_user_refuse;
192
193
    // Annulation
194
    /**
195
     * @var int|string
196
     */
197
    public $date_cancel;
198
199
    /**
200
     * @var string
201
     */
202
    public $detail_cancel;
203
204
    /**
205
     * @var int ID of user who cancel expense report
206
     */
207
    public $fk_user_cancel;
208
209
    /**
210
     * @var int User that is defined to approve
211
     */
212
    public $fk_user_validator;
213
214
    /**
215
     * Validation date
216
     * @var int
217
     * @deprecated
218
     * @see $date_valid
219
     */
220
    public $datevalid;
221
222
    /**
223
     * Validation date
224
     * @var int
225
     */
226
    public $date_valid;
227
228
    /**
229
     * @var int ID of User making validation
230
     */
231
    public $fk_user_valid;
232
233
    /**
234
     * @var string Firstname Lastname
235
     */
236
    public $user_valid_infos;
237
238
    // Approve
239
    /**
240
     * @var int|string
241
     */
242
    public $date_approve;
243
244
    /**
245
     * @var int ID User that has approved
246
     */
247
    public $fk_user_approve;
248
249
    public $localtax1;  // for backward compatibility (real field should be total_localtax1 defined into CommonObject)
250
    public $localtax2;  // for backward compatibility (real field should be total_localtax2 defined into CommonObject)
251
252
    /**
253
     * Draft status
254
     */
255
    const STATUS_DRAFT = 0;
256
257
    /**
258
     * Validated (need to be paid)
259
     */
260
    const STATUS_VALIDATED = 2;
261
262
    /**
263
     * Classified canceled
264
     */
265
    const STATUS_CANCELED = 4;
266
267
    /**
268
     * Classified approved
269
     */
270
    const STATUS_APPROVED = 5;
271
272
    /**
273
     * Classified paid.
274
     */
275
    const STATUS_CLOSED = 6;
276
277
    /**
278
     * Classified refused
279
     */
280
    const STATUS_REFUSED = 99;
281
282
    public $fields = array(
283
        'rowid' => array('type' => 'integer', 'label' => 'ID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
284
        'ref' => array('type' => 'varchar(50)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15),
285
        'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 20),
286
        'ref_number_int' => array('type' => 'integer', 'label' => 'Ref number int', 'enabled' => 1, 'visible' => -1, 'position' => 25),
287
        'ref_ext' => array('type' => 'integer', 'label' => 'Ref ext', 'enabled' => 1, 'visible' => -1, 'position' => 30),
288
        'total_ht' => array('type' => 'double(24,8)', 'label' => 'Total ht', 'enabled' => 1, 'visible' => -1, 'position' => 35),
289
        'total_tva' => array('type' => 'double(24,8)', 'label' => 'Total tva', 'enabled' => 1, 'visible' => -1, 'position' => 40),
290
        'localtax1' => array('type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 45),
291
        'localtax2' => array('type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 50),
292
        'total_ttc' => array('type' => 'double(24,8)', 'label' => 'Total ttc', 'enabled' => 1, 'visible' => -1, 'position' => 55),
293
        'date_debut' => array('type' => 'date', 'label' => 'Date debut', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 60),
294
        'date_fin' => array('type' => 'date', 'label' => 'Date fin', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 65),
295
        'date_valid' => array('type' => 'datetime', 'label' => 'Date valid', 'enabled' => 1, 'visible' => -1, 'position' => 75),
296
        'date_approve' => array('type' => 'datetime', 'label' => 'Date approve', 'enabled' => 1, 'visible' => -1, 'position' => 80),
297
        'date_refuse' => array('type' => 'datetime', 'label' => 'Date refuse', 'enabled' => 1, 'visible' => -1, 'position' => 85),
298
        'date_cancel' => array('type' => 'datetime', 'label' => 'Date cancel', 'enabled' => 1, 'visible' => -1, 'position' => 90),
299
        'fk_user_author' => array('type' => 'integer', 'label' => 'Fk user author', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 100),
300
        'fk_user_modif' => array('type' => 'integer', 'label' => 'Fk user modif', 'enabled' => 1, 'visible' => -1, 'position' => 105),
301
        'fk_user_valid' => array('type' => 'integer', 'label' => 'Fk user valid', 'enabled' => 1, 'visible' => -1, 'position' => 110),
302
        'fk_user_validator' => array('type' => 'integer', 'label' => 'Fk user validator', 'enabled' => 1, 'visible' => -1, 'position' => 115),
303
        'fk_user_approve' => array('type' => 'integer', 'label' => 'Fk user approve', 'enabled' => 1, 'visible' => -1, 'position' => 120),
304
        'fk_user_refuse' => array('type' => 'integer', 'label' => 'Fk user refuse', 'enabled' => 1, 'visible' => -1, 'position' => 125),
305
        'fk_user_cancel' => array('type' => 'integer', 'label' => 'Fk user cancel', 'enabled' => 1, 'visible' => -1, 'position' => 130),
306
        'fk_c_paiement' => array('type' => 'integer', 'label' => 'Fk c paiement', 'enabled' => 1, 'visible' => -1, 'position' => 140),
307
        'paid' => array('type' => 'integer', 'label' => 'Paid', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 145),
308
        'note_public' => array('type' => 'html', 'label' => 'Note public', 'enabled' => 1, 'visible' => 0, 'position' => 150),
309
        'note_private' => array('type' => 'html', 'label' => 'Note private', 'enabled' => 1, 'visible' => 0, 'position' => 155),
310
        'detail_refuse' => array('type' => 'varchar(255)', 'label' => 'Detail refuse', 'enabled' => 1, 'visible' => -1, 'position' => 160),
311
        'detail_cancel' => array('type' => 'varchar(255)', 'label' => 'Detail cancel', 'enabled' => 1, 'visible' => -1, 'position' => 165),
312
        'integration_compta' => array('type' => 'integer', 'label' => 'Integration compta', 'enabled' => 1, 'visible' => -1, 'position' => 170),
313
        'fk_bank_account' => array('type' => 'integer', 'label' => 'Fk bank account', 'enabled' => 1, 'visible' => -1, 'position' => 175),
314
        'fk_multicurrency' => array('type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 1, 'visible' => -1, 'position' => 185),
315
        'multicurrency_code' => array('type' => 'varchar(255)', 'label' => 'Multicurrency code', 'enabled' => 1, 'visible' => -1, 'position' => 190),
316
        'multicurrency_tx' => array('type' => 'double(24,8)', 'label' => 'Multicurrency tx', 'enabled' => 1, 'visible' => -1, 'position' => 195),
317
        'multicurrency_total_ht' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total ht', 'enabled' => 1, 'visible' => -1, 'position' => 200),
318
        'multicurrency_total_tva' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total tva', 'enabled' => 1, 'visible' => -1, 'position' => 205),
319
        'multicurrency_total_ttc' => array('type' => 'double(24,8)', 'label' => 'Multicurrency total ttc', 'enabled' => 1, 'visible' => -1, 'position' => 210),
320
        'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 220),
321
        'date_create' => array('type' => 'datetime', 'label' => 'Date create', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 300),
322
        'tms' => array('type' => 'timestamp', 'label' => 'Tms', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 305),
323
        'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -1, 'position' => 1000),
324
        'model_pdf' => array('type' => 'varchar(255)', 'label' => 'Model pdf', 'enabled' => 1, 'visible' => 0, 'position' => 1010),
325
        'fk_statut' => array('type' => 'integer', 'label' => 'Fk statut', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500),
326
    );
327
328
    /**
329
     *  Constructor
330
     *
331
     *  @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...
332
     */
333
    public function __construct($db)
334
    {
335
        $this->db = $db;
336
        $this->total_ht = 0;
337
        $this->total_ttc = 0;
338
        $this->total_tva = 0;
339
        $this->total_localtax1 = 0;
340
        $this->total_localtax2 = 0;
341
        $this->localtax1 = 0;   // For backward compatibility
342
        $this->localtax2 = 0;   // For backward compatibility
343
        $this->modepaymentid = 0;
344
345
        // List of language codes for status
346
        $this->labelStatusShort = array(0 => 'Draft', 2 => 'Validated', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
347
        $this->labelStatus = array(0 => 'Draft', 2 => 'ValidatedWaitingApproval', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
348
    }
349
350
    /**
351
     * Create object in database
352
     *
353
     * @param   User    $user   User that create
354
     * @param   int     $notrigger   Disable triggers
355
     * @return  int             Return integer <0 if KO, >0 if OK
356
     */
357
    public function create($user, $notrigger = 0)
358
    {
359
        global $conf, $langs;
360
361
        $now = dol_now();
362
363
        $error = 0;
364
365
        // Check parameters
366
        if (empty($this->date_debut) || empty($this->date_fin)) {
367
            $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Date'));
368
            return -1;
369
        }
370
371
        $fuserid = $this->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
372
        if (empty($fuserid)) {
373
            $fuserid = $user->id;
374
        }
375
376
        $this->db->begin();
377
378
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . $this->table_element . " (";
379
        $sql .= "ref";
380
        $sql .= ",total_ht";
381
        $sql .= ",total_ttc";
382
        $sql .= ",total_tva";
383
        $sql .= ",date_debut";
384
        $sql .= ",date_fin";
385
        $sql .= ",date_create";
386
        $sql .= ",fk_user_creat";
387
        $sql .= ",fk_user_author";
388
        $sql .= ",fk_user_validator";
389
        $sql .= ",fk_user_approve";
390
        $sql .= ",fk_user_modif";
391
        $sql .= ",fk_statut";
392
        $sql .= ",fk_c_paiement";
393
        $sql .= ",paid";
394
        $sql .= ",note_public";
395
        $sql .= ",note_private";
396
        $sql .= ",entity";
397
        $sql .= ") VALUES(";
398
        $sql .= "'(PROV)'";
399
        $sql .= ", " . price2num($this->total_ht, 'MT');
400
        $sql .= ", " . price2num($this->total_ttc, 'MT');
401
        $sql .= ", " . price2num($this->total_tva, 'MT');
402
        $sql .= ", '" . $this->db->idate($this->date_debut) . "'";
403
        $sql .= ", '" . $this->db->idate($this->date_fin) . "'";
404
        $sql .= ", '" . $this->db->idate($now) . "'";
405
        $sql .= ", " . ((int) $user->id);
406
        $sql .= ", " . ((int) $fuserid);
407
        $sql .= ", " . ($this->fk_user_validator > 0 ? ((int) $this->fk_user_validator) : "null");
408
        $sql .= ", " . ($this->fk_user_approve > 0 ? ((int) $this->fk_user_approve) : "null");
409
        $sql .= ", " . ($this->fk_user_modif > 0 ? ((int) $this->fk_user_modif) : "null");
410
        $sql .= ", " . ($this->fk_statut > 1 ? ((int) $this->fk_statut) : 0);
411
        $sql .= ", " . ($this->modepaymentid ? ((int) $this->modepaymentid) : "null");
412
        $sql .= ", 0";
413
        $sql .= ", " . ($this->note_public ? "'" . $this->db->escape($this->note_public) . "'" : "null");
414
        $sql .= ", " . ($this->note_private ? "'" . $this->db->escape($this->note_private) . "'" : "null");
415
        $sql .= ", " . ((int) $conf->entity);
416
        $sql .= ")";
417
418
        $result = $this->db->query($sql);
419
        if ($result) {
420
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
421
            $this->ref = '(PROV' . $this->id . ')';
422
423
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . " SET ref='" . $this->db->escape($this->ref) . "' WHERE rowid=" . ((int) $this->id);
424
            $resql = $this->db->query($sql);
425
            if (!$resql) {
426
                $this->error = $this->db->lasterror();
427
                $error++;
428
            }
429
430
            if (!$error) {
431
                if (is_array($this->lines) && count($this->lines) > 0) {
432
                    foreach ($this->lines as $line) {
433
                        // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
434
                        //if (! is_object($line)) $line=json_decode(json_encode($line), false);  // convert recursively array into object.
435
                        if (!is_object($line)) {
436
                            $line = (object) $line;
437
                            $newndfline = new ExpenseReportLine($this->db);
438
                            $newndfline->fk_expensereport = $line->fk_expensereport;
439
                            $newndfline->fk_c_type_fees = $line->fk_c_type_fees;
440
                            $newndfline->fk_project = $line->fk_project;
441
                            $newndfline->vatrate = $line->vatrate;
442
                            $newndfline->vat_src_code = $line->vat_src_code;
443
                            $newndfline->localtax1_tx = $line->localtax1_tx;
444
                            $newndfline->localtax2_tx = $line->localtax2_tx;
445
                            $newndfline->localtax1_type = $line->localtax1_type;
446
                            $newndfline->localtax2_type = $line->localtax2_type;
447
                            $newndfline->comments = $line->comments;
448
                            $newndfline->qty = $line->qty;
449
                            $newndfline->value_unit = $line->value_unit;
450
                            $newndfline->total_ht = $line->total_ht;
451
                            $newndfline->total_ttc = $line->total_ttc;
452
                            $newndfline->total_tva = $line->total_tva;
453
                            $newndfline->total_localtax1 = $line->total_localtax1;
454
                            $newndfline->total_localtax2 = $line->total_localtax2;
455
                            $newndfline->date = $line->date;
456
                            $newndfline->rule_warning_message = $line->rule_warning_message;
457
                            $newndfline->fk_c_exp_tax_cat = $line->fk_c_exp_tax_cat;
458
                            $newndfline->fk_ecm_files = $line->fk_ecm_files;
459
                        } else {
460
                            $newndfline = $line;
461
                        }
462
                        //$newndfline=new ExpenseReportLine($this->db);
463
                        $newndfline->fk_expensereport = $this->id;
464
                        $result = $newndfline->insert();
465
                        if ($result < 0) {
466
                            $this->error = $newndfline->error;
467
                            $this->errors = $newndfline->errors;
468
                            $error++;
469
                            break;
470
                        }
471
                    }
472
                }
473
            }
474
475
            if (!$error) {
476
                $result = $this->insertExtraFields();
477
                if ($result < 0) {
478
                    $error++;
479
                }
480
            }
481
482
            if (!$error) {
483
                $result = $this->update_price(1);
484
                if ($result > 0) {
485
                    if (!$notrigger) {
486
                        // Call trigger
487
                        $result = $this->call_trigger('EXPENSE_REPORT_CREATE', $user);
488
489
                        if ($result < 0) {
490
                            $error++;
491
                        }
492
                        // End call triggers
493
                    }
494
495
                    if (empty($error)) {
496
                        $this->db->commit();
497
                        return $this->id;
498
                    } else {
499
                        $this->db->rollback();
500
                        return -4;
501
                    }
502
                } else {
503
                    $this->db->rollback();
504
                    return -3;
505
                }
506
            } else {
507
                dol_syslog(get_class($this) . "::create error " . $this->error, LOG_ERR);
508
                $this->db->rollback();
509
                return -2;
510
            }
511
        } else {
512
            $this->error = $this->db->lasterror() . " sql=" . $sql;
513
            $this->db->rollback();
514
            return -1;
515
        }
516
    }
517
518
    /**
519
     *  Load an object from its id and create a new one in database
520
     *
521
     *  @param      User    $user               User making the clone
522
     *  @param      int     $fk_user_author     Id of new user
523
     *  @return     int                         New id of clone
524
     */
525
    public function createFromClone(User $user, $fk_user_author)
526
    {
527
        global $hookmanager;
528
529
        $error = 0;
530
531
        if (empty($fk_user_author)) {
532
            $fk_user_author = $user->id;
533
        }
534
535
        $this->db->begin();
536
537
        // get extrafields so they will be clone
538
        //foreach($this->lines as $line)
539
        //$line->fetch_optionals();
540
541
        // Load source object
542
        $objFrom = clone $this;
543
544
        $this->id = 0;
545
        $this->ref = '';
546
        $this->status = 0;
547
        $this->fk_statut = 0; // deprecated
548
549
        // Clear fields
550
        $this->fk_user_creat = $user->id;
551
        $this->fk_user_author = $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
552
        $this->fk_user_valid = 0;
553
        $this->date_create = '';
554
        $this->date_creation = '';
555
        $this->date_validation = '';
556
557
        // Remove link on lines to a joined file
558
        if (is_array($this->lines) && count($this->lines) > 0) {
559
            foreach ($this->lines as $key => $line) {
560
                $this->lines[$key]->fk_ecm_files = 0;
561
            }
562
        }
563
564
        // Create clone
565
        $this->context['createfromclone'] = 'createfromclone';
566
        $result = $this->create($user);
567
        if ($result < 0) {
568
            $error++;
569
        }
570
571
        if (!$error) {
572
            // Hook of thirdparty module
573
            if (is_object($hookmanager)) {
574
                $parameters = array('objFrom' => $objFrom);
575
                $action = '';
576
                $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
577
                if ($reshook < 0) {
578
                    $this->setErrorsFromObject($hookmanager);
579
                    $error++;
580
                }
581
            }
582
        }
583
584
        unset($this->context['createfromclone']);
585
586
        // End
587
        if (!$error) {
588
            $this->db->commit();
589
            return $this->id;
590
        } else {
591
            $this->db->rollback();
592
            return -1;
593
        }
594
    }
595
596
597
    /**
598
     * update
599
     *
600
     * @param   User    $user                   User making change
601
     * @param   int     $notrigger              Disable triggers
602
     * @param   User    $userofexpensereport    New user we want to have the expense report on.
603
     * @return  int                             Return integer <0 if KO, >0 if OK
604
     */
605
    public function update($user, $notrigger = 0, $userofexpensereport = null)
606
    {
607
        global $langs;
608
609
        $error = 0;
610
        $this->db->begin();
611
612
        $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element . " SET";
613
        $sql .= " total_ht = " . $this->total_ht;
614
        $sql .= " , total_ttc = " . $this->total_ttc;
615
        $sql .= " , total_tva = " . $this->total_tva;
616
        $sql .= " , date_debut = '" . $this->db->idate($this->date_debut) . "'";
617
        $sql .= " , date_fin = '" . $this->db->idate($this->date_fin) . "'";
618
        if ($userofexpensereport && is_object($userofexpensereport)) {
619
            $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.
620
        }
621
        $sql .= " , fk_user_validator = " . ($this->fk_user_validator > 0 ? $this->fk_user_validator : "null");
622
        $sql .= " , fk_user_valid = " . ($this->fk_user_valid > 0 ? $this->fk_user_valid : "null");
623
        $sql .= " , fk_user_approve = " . ($this->fk_user_approve > 0 ? $this->fk_user_approve : "null");
624
        $sql .= " , fk_user_modif = " . $user->id;
625
        $sql .= " , fk_statut = " . ($this->fk_statut >= 0 ? $this->fk_statut : '0');
626
        $sql .= " , fk_c_paiement = " . ($this->fk_c_paiement > 0 ? $this->fk_c_paiement : "null");
627
        $sql .= " , note_public = " . (!empty($this->note_public) ? "'" . $this->db->escape($this->note_public) . "'" : "''");
628
        $sql .= " , note_private = " . (!empty($this->note_private) ? "'" . $this->db->escape($this->note_private) . "'" : "''");
629
        $sql .= " , detail_refuse = " . (!empty($this->detail_refuse) ? "'" . $this->db->escape($this->detail_refuse) . "'" : "''");
630
        $sql .= " WHERE rowid = " . ((int) $this->id);
631
632
        dol_syslog(get_class($this) . "::update", LOG_DEBUG);
633
        $result = $this->db->query($sql);
634
        if ($result) {
635
            if (!$notrigger) {
636
                // Call trigger
637
                $result = $this->call_trigger('EXPENSE_REPORT_MODIFY', $user);
638
639
                if ($result < 0) {
640
                    $error++;
641
                }
642
                // End call triggers
643
            }
644
645
            if (empty($error)) {
646
                $this->db->commit();
647
                return 1;
648
            } else {
649
                $this->db->rollback();
650
                $this->error = $this->db->error();
651
                return -2;
652
            }
653
        } else {
654
            $this->db->rollback();
655
            $this->error = $this->db->error();
656
            return -1;
657
        }
658
    }
659
660
    /**
661
     *  Load an object from database
662
     *
663
     *  @param  int     $id     Id                      {@min 1}
664
     *  @param  string  $ref    Ref                     {@name ref}
665
     *  @return int             Return integer <0 if KO, >0 if OK
666
     */
667
    public function fetch($id, $ref = '')
668
    {
669
        $sql = "SELECT d.rowid, d.entity, d.ref, d.note_public, d.note_private,"; // DEFAULT
670
        $sql .= " d.detail_refuse, d.detail_cancel, d.fk_user_refuse, d.fk_user_cancel,"; // ACTIONS
671
        $sql .= " d.date_refuse, d.date_cancel,"; // ACTIONS
672
        $sql .= " d.total_ht, d.total_ttc, d.total_tva,";
673
        $sql .= " d.localtax1 as total_localtax1, d.localtax2 as total_localtax2,";
674
        $sql .= " d.date_debut, d.date_fin, d.date_create, d.tms as date_modif, d.date_valid, d.date_approve,"; // DATES (datetime)
675
        $sql .= " d.fk_user_creat, d.fk_user_author, d.fk_user_modif, d.fk_user_validator,";
676
        $sql .= " d.fk_user_valid, d.fk_user_approve,";
677
        $sql .= " d.fk_statut as status, d.fk_c_paiement, d.paid";
678
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . " as d";
679
        if ($ref) {
680
            $sql .= " WHERE d.ref = '" . $this->db->escape($ref) . "'";
681
        } else {
682
            $sql .= " WHERE d.rowid = " . ((int) $id);
683
        }
684
        //$sql.= $restrict;
685
686
        dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
687
        $resql = $this->db->query($sql);
688
        if ($resql) {
689
            $obj = $this->db->fetch_object($resql);
690
            if ($obj) {
691
                $this->id           = $obj->rowid;
692
                $this->ref          = $obj->ref;
693
694
                $this->entity       = $obj->entity;
695
696
                $this->total_ht     = $obj->total_ht;
697
                $this->total_tva    = $obj->total_tva;
698
                $this->total_ttc    = $obj->total_ttc;
699
                $this->localtax1    = $obj->total_localtax1;        // For backward compatibility
700
                $this->localtax2    = $obj->total_localtax2;        // For backward compatibility
701
                $this->total_localtax1 = $obj->total_localtax1;
702
                $this->total_localtax2 = $obj->total_localtax2;
703
704
                $this->note_public  = $obj->note_public;
705
                $this->note_private = $obj->note_private;
706
                $this->detail_refuse = $obj->detail_refuse;
707
                $this->detail_cancel = $obj->detail_cancel;
708
709
                $this->date_debut       = $this->db->jdate($obj->date_debut);
710
                $this->date_fin         = $this->db->jdate($obj->date_fin);
711
                $this->date_valid       = $this->db->jdate($obj->date_valid);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->db->jdate($obj->date_valid) can also be of type string. However, the property $date_valid is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
712
                $this->date_approve     = $this->db->jdate($obj->date_approve);
713
                $this->date_create      = $this->db->jdate($obj->date_create);
714
                $this->date_modif       = $this->db->jdate($obj->date_modif);
715
                $this->date_refuse      = $this->db->jdate($obj->date_refuse);
716
                $this->date_cancel      = $this->db->jdate($obj->date_cancel);
717
718
                $this->fk_user_creat            = $obj->fk_user_creat;
719
                $this->fk_user_author           = $obj->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
720
                $this->fk_user_modif            = $obj->fk_user_modif;
721
                $this->fk_user_validator        = $obj->fk_user_validator;
722
                $this->fk_user_valid            = $obj->fk_user_valid;
723
                $this->fk_user_refuse           = $obj->fk_user_refuse;
724
                $this->fk_user_cancel           = $obj->fk_user_cancel;
725
                $this->fk_user_approve          = $obj->fk_user_approve;
726
727
                $user_author = new User($this->db);
728
                if ($this->fk_user_author > 0) {
729
                    $user_author->fetch($this->fk_user_author);
730
                }
731
732
                $this->user_author_infos = dolGetFirstLastname($user_author->firstname, $user_author->lastname);
733
734
                $user_approver = new User($this->db);
735
                if ($this->fk_user_approve > 0) {
736
                    $user_approver->fetch($this->fk_user_approve);
737
                } elseif ($this->fk_user_validator > 0) {
738
                    $user_approver->fetch($this->fk_user_validator); // For backward compatibility
739
                }
740
                $this->user_validator_infos = dolGetFirstLastname($user_approver->firstname, $user_approver->lastname);
741
742
                $this->fk_statut                = $obj->status; // deprecated
743
                $this->status                   = $obj->status;
744
                $this->fk_c_paiement            = $obj->fk_c_paiement;
745
                $this->paid                     = $obj->paid;
746
747
                if ($this->status == self::STATUS_APPROVED || $this->status == self::STATUS_CLOSED) {
748
                    $user_valid = new User($this->db);
749
                    if ($this->fk_user_valid > 0) {
750
                        $user_valid->fetch($this->fk_user_valid);
751
                    }
752
                    $this->user_valid_infos = dolGetFirstLastname($user_valid->firstname, $user_valid->lastname);
753
                }
754
755
                $this->fetch_optionals();
756
757
                $result = $this->fetch_lines();
758
759
                return $result;
760
            } else {
761
                return 0;
762
            }
763
        } else {
764
            $this->error = $this->db->lasterror();
765
            return -1;
766
        }
767
    }
768
769
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
770
    /**
771
     *  Classify the expense report as paid
772
     *
773
     *  @deprecated
774
     *  @see setPaid()
775
     *  @param    int     $id                 Id of expense report
776
     *  @param    user    $fuser              User making change
777
     *  @param    int     $notrigger          Disable triggers
778
     *  @return   int                         Return integer <0 if KO, >0 if OK
779
     */
780
    public function set_paid($id, $fuser, $notrigger = 0)
781
    {
782
		// phpcs:enable
783
        dol_syslog(get_class($this) . "::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
784
        return $this->setPaid($id, $fuser, $notrigger);
785
    }
786
787
    /**
788
     *    Classify the expense report as paid
789
     *
790
     *    @param    int     $id                 Id of expense report
791
     *    @param    user    $fuser              User making change
792
     *    @param    int     $notrigger          Disable triggers
793
     *    @return   int                         Return integer <0 if KO, >0 if OK
794
     */
795
    public function setPaid($id, $fuser, $notrigger = 0)
796
    {
797
        $error = 0;
798
        $this->db->begin();
799
800
        $sql = "UPDATE " . MAIN_DB_PREFIX . "expensereport";
801
        $sql .= " SET fk_statut = " . self::STATUS_CLOSED . ", paid=1";
802
        $sql .= " WHERE rowid = " . ((int) $id) . " AND fk_statut = " . self::STATUS_APPROVED;
803
804
        dol_syslog(get_class($this) . "::setPaid", LOG_DEBUG);
805
        $resql = $this->db->query($sql);
806
        if ($resql) {
807
            if ($this->db->affected_rows($resql)) {
808
                if (!$notrigger) {
809
                    // Call trigger
810
                    $result = $this->call_trigger('EXPENSE_REPORT_PAID', $fuser);
811
812
                    if ($result < 0) {
813
                        $error++;
814
                    }
815
                    // End call triggers
816
                }
817
818
                if (empty($error)) {
819
                    $this->db->commit();
820
                    return 1;
821
                } else {
822
                    $this->db->rollback();
823
                    $this->error = $this->db->error();
824
                    return -2;
825
                }
826
            } else {
827
                $this->db->commit();
828
                return 0;
829
            }
830
        } else {
831
            $this->db->rollback();
832
            dol_print_error($this->db);
833
            return -1;
834
        }
835
    }
836
837
    /**
838
     *  Returns the label status
839
     *
840
     *  @param      int     $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
841
     *  @return     string              Label
842
     */
843
    public function getLibStatut($mode = 0)
844
    {
845
        return $this->LibStatut($this->status, $mode);
846
    }
847
848
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
849
    /**
850
     *  Returns the label of a status
851
     *
852
     *  @param      int     $status     ID status
853
     *  @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
854
     *  @return     string              Label
855
     */
856
    public function LibStatut($status, $mode = 0)
857
    {
858
		// phpcs:enable
859
        global $langs;
860
861
        $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
862
        $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
863
864
        $statuslogo = array(0 => 'status0', 2 => 'status1', 4 => 'status6', 5 => 'status4', 6 => 'status6', 99 => 'status5');
865
866
        $statusType = $statuslogo[$status];
867
868
        return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
869
    }
870
871
872
    /**
873
     *  Load information on object
874
     *
875
     *  @param  int     $id      Id of object
876
     *  @return void
877
     */
878
    public function info($id)
879
    {
880
        global $conf;
881
882
        $sql = "SELECT f.rowid,";
883
        $sql .= " f.date_create as datec,";
884
        $sql .= " f.tms as date_modification,";
885
        $sql .= " f.date_valid as datev,";
886
        $sql .= " f.date_approve as datea,";
887
        $sql .= " f.fk_user_creat as fk_user_creation,";
888
        $sql .= " f.fk_user_author as fk_user_author,";
889
        $sql .= " f.fk_user_modif as fk_user_modification,";
890
        $sql .= " f.fk_user_valid,";
891
        $sql .= " f.fk_user_approve";
892
        $sql .= " FROM " . MAIN_DB_PREFIX . "expensereport as f";
893
        $sql .= " WHERE f.rowid = " . ((int) $id);
894
        $sql .= " AND f.entity = " . $conf->entity;
895
896
897
898
        $resql = $this->db->query($sql);
899
        if ($resql) {
900
            if ($this->db->num_rows($resql)) {
901
                $obj = $this->db->fetch_object($resql);
902
903
                $this->id = $obj->rowid;
904
905
                $this->date_creation = $this->db->jdate($obj->datec);
906
                $this->date_modification = $this->db->jdate($obj->date_modification);
907
                $this->date_validation = $this->db->jdate($obj->datev);
908
                $this->date_approbation = $this->db->jdate($obj->datea);
909
910
                $this->user_creation_id = $obj->fk_user_author;
911
                $this->user_creation_id = $obj->fk_user_creation;
912
                $this->user_validation_id = $obj->fk_user_valid;
913
                $this->user_modification_id = $obj->fk_user_modification;
914
                $this->user_approve_id = $obj->fk_user_approve;
915
            }
916
            $this->db->free($resql);
917
        } else {
918
            dol_print_error($this->db);
919
        }
920
    }
921
922
923
924
    /**
925
     *  Initialise an instance with random values.
926
     *  Used to build previews or test instances.
927
     *  id must be 0 if object instance is a specimen.
928
     *
929
     *  @return int
930
     */
931
    public function initAsSpecimen()
932
    {
933
        global $user, $langs;
934
935
        $now = dol_now();
936
937
        // Initialise parameters
938
        $this->id = 0;
939
        $this->ref = 'SPECIMEN';
940
        $this->specimen = 1;
941
        $this->entity = 1;
942
        $this->date_create = $now;
943
        $this->date_debut = $now;
944
        $this->date_fin = $now;
945
        $this->date_valid = $now;
946
        $this->date_approve = $now;
947
948
        $type_fees_id = 2; // TF_TRIP
949
950
        $this->status = 5;
951
952
        $this->fk_user_author = $user->id;
953
        $this->fk_user_validator = $user->id;
954
        $this->fk_user_valid = $user->id;
955
        $this->fk_user_approve = $user->id;
956
957
        $this->note_private = 'Private note';
958
        $this->note_public = 'SPECIMEN';
959
        $nbp = 5;
960
        $xnbp = 0;
961
        while ($xnbp < $nbp) {
962
            $line = new ExpenseReportLine($this->db);
963
            $line->comments = $langs->trans("Comment") . " " . $xnbp;
964
            $line->date = ($now - 3600 * (1 + $xnbp));
965
            $line->total_ht = 100;
966
            $line->total_tva = 20;
967
            $line->total_ttc = 120;
968
            $line->qty = 1;
969
            $line->vatrate = 20;
970
            $line->value_unit = 120;
971
            $line->fk_expensereport = 0;
972
            $line->type_fees_code = 'TRA';
973
            $line->fk_c_type_fees = $type_fees_id;
974
975
            $line->projet_ref = 'ABC';
976
977
            $this->lines[$xnbp] = $line;
978
            $xnbp++;
979
980
            $this->total_ht += $line->total_ht;
981
            $this->total_tva += $line->total_tva;
982
            $this->total_ttc += $line->total_ttc;
983
        }
984
985
        return 1;
986
    }
987
988
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
989
    /**
990
     * fetch_line_by_project
991
     *
992
     * @param   int     $projectid      Project id
993
     * @param   User    $user           User
994
     * @return  int                     Return integer <0 if KO, >0 if OK
995
     */
996
    public function fetch_line_by_project($projectid, $user)
997
    {
998
		// phpcs:enable
999
        global $langs;
1000
1001
        $langs->load('trips');
1002
1003
        if ($user->hasRight('expensereport', 'lire')) {
1004
            $sql = "SELECT de.fk_expensereport, de.date, de.comments, de.total_ht, de.total_ttc";
1005
            $sql .= " FROM " . MAIN_DB_PREFIX . "expensereport_det as de";
1006
            $sql .= " WHERE de.fk_projet = " . ((int) $projectid);
1007
1008
            dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
1009
            $result = $this->db->query($sql);
1010
            if ($result) {
1011
                $num = $this->db->num_rows($result);
1012
                $i = 0;
1013
                $total_HT = 0;
1014
                $total_TTC = 0;
1015
1016
                while ($i < $num) {
1017
                    $objp = $this->db->fetch_object($result);
1018
1019
                    $sql2 = "SELECT d.rowid, d.fk_user_author, d.ref, d.fk_statut as status";
1020
                    $sql2 .= " FROM " . MAIN_DB_PREFIX . "expensereport as d";
1021
                    $sql2 .= " WHERE d.rowid = " . ((int) $objp->fk_expensereport);
1022
1023
                    $result2 = $this->db->query($sql2);
1024
                    $obj = $this->db->fetch_object($result2);
1025
1026
                    $objp->fk_user_author = $obj->fk_user_author;
1027
                    $objp->ref = $obj->ref;
1028
                    $objp->fk_c_expensereport_status = $obj->status;
1029
                    $objp->rowid = $obj->rowid;
1030
1031
                    $total_HT += $objp->total_ht;
1032
                    $total_TTC += $objp->total_ttc;
1033
                    $author = new User($this->db);
1034
                    $author->fetch($objp->fk_user_author);
1035
1036
                    print '<tr>';
1037
                    print '<td><a href="' . constant('BASE_URL') . '/expensereport/card.php?id=' . $objp->rowid . '">' . $objp->ref_num . '</a></td>';
1038
                    print '<td class="center">' . dol_print_date($objp->date, 'day') . '</td>';
1039
                    print '<td>' . $author->getNomUrl(1) . '</td>';
1040
                    print '<td>' . $objp->comments . '</td>';
1041
                    print '<td class="right">' . price($objp->total_ht) . '</td>';
1042
                    print '<td class="right">' . price($objp->total_ttc) . '</td>';
1043
                    print '<td class="right">';
1044
1045
                    switch ($objp->fk_c_expensereport_status) {
1046
                        case 4:
1047
                            print img_picto($langs->trans('StatusOrderCanceled'), 'statut5');
1048
                            break;
1049
                        case 1:
1050
                            print $langs->trans('Draft') . ' ' . img_picto($langs->trans('Draft'), 'statut0');
1051
                            break;
1052
                        case 2:
1053
                            print $langs->trans('TripForValid') . ' ' . img_picto($langs->trans('TripForValid'), 'statut3');
1054
                            break;
1055
                        case 5:
1056
                            print $langs->trans('TripForPaid') . ' ' . img_picto($langs->trans('TripForPaid'), 'statut3');
1057
                            break;
1058
                        case 6:
1059
                            print $langs->trans('TripPaid') . ' ' . img_picto($langs->trans('TripPaid'), 'statut4');
1060
                            break;
1061
                    }
1062
                    /*
1063
                     if ($status==4) return img_picto($langs->trans('StatusOrderCanceled'),'statut5');
1064
                    if ($status==1) return img_picto($langs->trans('StatusOrderDraft'),'statut0');
1065
                    if ($status==2) return img_picto($langs->trans('StatusOrderValidated'),'statut1');
1066
                    if ($status==2) return img_picto($langs->trans('StatusOrderOnProcess'),'statut3');
1067
                    if ($status==5) return img_picto($langs->trans('StatusOrderToBill'),'statut4');
1068
                    if ($status==6) return img_picto($langs->trans('StatusOrderOnProcess'),'statut6');
1069
                    */
1070
                    print '</td>';
1071
                    print '</tr>';
1072
1073
                    $i++;
1074
                }
1075
1076
                print '<tr class="liste_total"><td colspan="4">' . $langs->trans("Number") . ': ' . $i . '</td>';
1077
                print '<td class="right" width="100">' . $langs->trans("TotalHT") . ' : ' . price($total_HT) . '</td>';
1078
                print '<td class="right" width="100">' . $langs->trans("TotalTTC") . ' : ' . price($total_TTC) . '</td>';
1079
                print '<td>&nbsp;</td>';
1080
                print '</tr>';
1081
            } else {
1082
                $this->error = $this->db->lasterror();
1083
                return -1;
1084
            }
1085
        }
1086
1087
        return 0;
1088
    }
1089
1090
	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1091
    /**
1092
     * fetch_lines
1093
     *
1094
     * @return  int     Return integer <0 if OK, >0 if KO
1095
     */
1096
    public function fetch_lines()
1097
    {
1098
		// phpcs:enable
1099
        $this->lines = array();
1100
1101
        $sql = ' SELECT de.rowid, de.comments, de.qty, de.value_unit, de.date, de.rang,';
1102
        $sql .= " de." . $this->fk_element . ", de.fk_c_type_fees, de.fk_c_exp_tax_cat, de.fk_projet as fk_project,";
1103
        $sql .= ' de.tva_tx, de.vat_src_code,';
1104
        $sql .= ' de.localtax1_tx, de.localtax2_tx, de.localtax1_type, de.localtax2_type,';
1105
        $sql .= ' de.fk_ecm_files,';
1106
        $sql .= ' de.total_ht, de.total_tva, de.total_ttc,';
1107
        $sql .= ' de.total_localtax1, de.total_localtax2, de.rule_warning_message,';
1108
        $sql .= ' ctf.code as code_type_fees, ctf.label as label_type_fees, ctf.accountancy_code as accountancy_code_type_fees,';
1109
        $sql .= ' p.ref as ref_projet, p.title as title_projet';
1110
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element_line . ' as de';
1111
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_type_fees as ctf ON de.fk_c_type_fees = ctf.id';
1112
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'projet as p ON de.fk_projet = p.rowid';
1113
        $sql .= " WHERE de." . $this->fk_element . " = " . ((int) $this->id);
1114
        if (getDolGlobalString('EXPENSEREPORT_LINES_SORTED_BY_ROWID')) {
1115
            $sql .= ' ORDER BY de.rang ASC, de.rowid ASC';
1116
        } else {
1117
            $sql .= ' ORDER BY de.rang ASC, de.date ASC';
1118
        }
1119
1120
        $resql = $this->db->query($sql);
1121
        if ($resql) {
1122
            $num = $this->db->num_rows($resql);
1123
            $i = 0;
1124
            while ($i < $num) {
1125
                $objp = $this->db->fetch_object($resql);
1126
1127
                $deplig = new ExpenseReportLine($this->db);
1128
1129
                $deplig->rowid          = $objp->rowid;
1130
                $deplig->id             = $objp->rowid;
1131
                $deplig->comments       = $objp->comments;
1132
                $deplig->qty            = $objp->qty;
1133
                $deplig->value_unit     = $objp->value_unit;
1134
                $deplig->date           = $objp->date;
1135
                $deplig->dates          = $this->db->jdate($objp->date);
1136
1137
                $deplig->fk_expensereport = $objp->fk_expensereport;
1138
                $deplig->fk_c_type_fees   = $objp->fk_c_type_fees;
1139
                $deplig->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
1140
                $deplig->fk_projet        = $objp->fk_project; // deprecated
1141
                $deplig->fk_project       = $objp->fk_project;
1142
                $deplig->fk_ecm_files     = $objp->fk_ecm_files;
1143
1144
                $deplig->total_ht         = $objp->total_ht;
1145
                $deplig->total_tva        = $objp->total_tva;
1146
                $deplig->total_ttc        = $objp->total_ttc;
1147
                $deplig->total_localtax1  = $objp->total_localtax1;
1148
                $deplig->total_localtax2  = $objp->total_localtax2;
1149
1150
                $deplig->type_fees_code     = empty($objp->code_type_fees) ? 'TF_OTHER' : $objp->code_type_fees;
1151
                $deplig->type_fees_libelle  = $objp->label_type_fees;
1152
                $deplig->type_fees_accountancy_code = $objp->accountancy_code_type_fees;
1153
1154
                $deplig->tva_tx             = $objp->tva_tx;
1155
                $deplig->vatrate            = $objp->tva_tx;
1156
                $deplig->vat_src_code       = $objp->vat_src_code;
1157
                $deplig->localtax1_tx       = $objp->localtax1_tx;
1158
                $deplig->localtax2_tx       = $objp->localtax2_tx;
1159
                $deplig->localtax1_type     = $objp->localtax1_type;
1160
                $deplig->localtax2_type     = $objp->localtax2_type;
1161
1162
                $deplig->projet_ref         = $objp->ref_projet;
1163
                $deplig->projet_title       = $objp->title_projet;
1164
1165
                $deplig->rule_warning_message = $objp->rule_warning_message;
1166
1167
                $deplig->rang               = $objp->rang;
1168
1169
                $this->lines[$i] = $deplig;
1170
1171
                $i++;
1172
            }
1173
            $this->db->free($resql);
1174
            return 1;
1175
        } else {
1176
            $this->error = $this->db->lasterror();
1177
            dol_syslog(get_class($this) . "::fetch_lines: Error " . $this->error, LOG_ERR);
1178
            return -3;
1179
        }
1180
    }
1181
1182
1183
    /**
1184
     * Delete object in database
1185
     *
1186
     * @param   User|null   $user       User that delete
1187
     * @param   int         $notrigger  0=launch triggers after, 1=disable triggers
1188
     * @return  int                     Return integer <0 if KO, >0 if OK
1189
     */
1190
    public function delete(User $user = null, $notrigger = 0)
1191
    {
1192
        global $conf;
1193
        require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
1194
1195
        $error = 0;
1196
1197
        $this->db->begin();
1198
1199
        if (!$notrigger) {
1200
            // Call trigger
1201
            $result = $this->call_trigger('EXPENSE_REPORT_DELETE', $user);
1202
            if ($result < 0) {
1203
                $error++;
1204
            }
1205
            // End call triggers
1206
        }
1207
1208
        // Delete extrafields of lines and lines
1209
        if (!$error && !empty($this->table_element_line)) {
1210
            $tabletodelete = $this->table_element_line;
1211
            //$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).")";
1212
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . $tabletodelete . " WHERE " . $this->fk_element . " = " . ((int) $this->id);
1213
            if (!$this->db->query($sql)) {
1214
                $error++;
1215
                $this->error = $this->db->lasterror();
1216
                $this->errors[] = $this->error;
1217
                dol_syslog(get_class($this) . "::delete error " . $this->error, LOG_ERR);
1218
            }
1219
        }
1220
1221
        if (!$error) {
1222
            // Delete linked object
1223
            $res = $this->deleteObjectLinked();
1224
            if ($res < 0) {
1225
                $error++;
1226
            }
1227
        }
1228
1229
        if (!$error) {
1230
            // Delete linked contacts
1231
            $res = $this->delete_linked_contact();
1232
            if ($res < 0) {
1233
                $error++;
1234
            }
1235
        }
1236
1237
        // Removed extrafields of object
1238
        if (!$error) {
1239
            $result = $this->deleteExtraFields();
1240
            if ($result < 0) {
1241
                $error++;
1242
                dol_syslog(get_class($this) . "::delete error " . $this->error, LOG_ERR);
1243
            }
1244
        }
1245
1246
        // Delete main record
1247
        if (!$error) {
1248
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element . " WHERE rowid = " . ((int) $this->id);
1249
            $res = $this->db->query($sql);
1250
            if (!$res) {
1251
                $error++;
1252
                $this->error = $this->db->lasterror();
1253
                $this->errors[] = $this->error;
1254
                dol_syslog(get_class($this) . "::delete error " . $this->error, LOG_ERR);
1255
            }
1256
        }
1257
1258
        // Delete record into ECM index and physically
1259
        if (!$error) {
1260
            $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1261
            $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1262
            if (!$res) {
1263
                $error++;
1264
            }
1265
        }
1266
1267
        if (!$error) {
1268
            // We remove directory
1269
            $ref = dol_sanitizeFileName($this->ref);
1270
            if ($conf->expensereport->multidir_output[$this->entity] && !empty($this->ref)) {
1271
                $dir = $conf->expensereport->multidir_output[$this->entity] . "/" . $ref;
1272
                $file = $dir . "/" . $ref . ".pdf";
1273
                if (file_exists($file)) {
1274
                    dol_delete_preview($this);
1275
1276
                    if (!dol_delete_file($file, 0, 0, 0, $this)) {
1277
                        $this->error = 'ErrorFailToDeleteFile';
1278
                        $this->errors[] = $this->error;
1279
                        $this->db->rollback();
1280
                        return 0;
1281
                    }
1282
                }
1283
                if (file_exists($dir)) {
1284
                    $res = @dol_delete_dir_recursive($dir);
1285
                    if (!$res) {
1286
                        $this->error = 'ErrorFailToDeleteDir';
1287
                        $this->errors[] = $this->error;
1288
                        $this->db->rollback();
1289
                        return 0;
1290
                    }
1291
                }
1292
            }
1293
        }
1294
1295
        if (!$error) {
1296
            dol_syslog(get_class($this) . "::delete " . $this->id . " by " . $user->id, LOG_DEBUG);
1297
            $this->db->commit();
1298
            return 1;
1299
        } else {
1300
            $this->db->rollback();
1301
            return -1;
1302
        }
1303
    }
1304
1305
    /**
1306
     * Set to status validate
1307
     *
1308
     * @param   User    $fuser      User
1309
     * @param   int     $notrigger  Disable triggers
1310
     * @return  int                 Return integer <0 if KO, 0 if nothing done, >0 if OK
1311
     */
1312
    public function setValidate($fuser, $notrigger = 0)
1313
    {
1314
        global $conf, $langs, $user;
1315
1316
        $error = 0;
1317
        $now = dol_now();
1318
1319
        // Protection
1320
        if ($this->status == self::STATUS_VALIDATED) {
1321
            dol_syslog(get_class($this) . "::valid action abandoned: already validated", LOG_WARNING);
1322
            return 0;
1323
        }
1324
1325
        $this->date_valid = $now; // Required for the getNextNum later.
1326
1327
        // Define new ref
1328
        if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
1329
            $num = $this->getNextNumRef();
1330
        } else {
1331
            $num = $this->ref;
1332
        }
1333
        if (empty($num) || $num < 0) {
1334
            return -1;
1335
        }
1336
1337
        $this->newref = dol_sanitizeFileName($num);
1338
1339
        $this->db->begin();
1340
1341
        // Validate
1342
        $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element;
1343
        $sql .= " SET ref = '" . $this->db->escape($num) . "',";
1344
        $sql .= " fk_statut = " . self::STATUS_VALIDATED . ",";
1345
        $sql .= " date_valid = '" . $this->db->idate($this->date_valid) . "',";
1346
        $sql .= " fk_user_valid = " . ((int) $user->id);
1347
        $sql .= " WHERE rowid = " . ((int) $this->id);
1348
1349
        $resql = $this->db->query($sql);
1350
        if ($resql) {
1351
            if (!$error && !$notrigger) {
1352
                // Call trigger
1353
                $result = $this->call_trigger('EXPENSE_REPORT_VALIDATE', $fuser);
1354
                if ($result < 0) {
1355
                    $error++;
1356
                }
1357
                // End call triggers
1358
            }
1359
1360
            if (!$error) {
1361
                $this->oldref = $this->ref;
1362
1363
                // Rename directory if dir was a temporary ref
1364
                if (preg_match('/^[\(]?PROV/i', $this->ref)) {
1365
                    require_once constant('DOL_DOCUMENT_ROOT') . '/core/lib/files.lib.php';
1366
1367
                    // Now we rename also files into index
1368
                    $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) . "'";
1369
                    $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'expensereport/" . $this->db->escape($this->ref) . "' AND entity = " . ((int) $this->entity);
1370
                    $resql = $this->db->query($sql);
1371
                    if (!$resql) {
1372
                        $error++;
1373
                        $this->error = $this->db->lasterror();
1374
                    }
1375
                    $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'expensereport/" . $this->db->escape($this->newref) . "'";
1376
                    $sql .= " WHERE filepath = 'expensereport/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
1377
                    $resql = $this->db->query($sql);
1378
                    if (!$resql) {
1379
                        $error++;
1380
                        $this->error = $this->db->lasterror();
1381
                    }
1382
1383
                    // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1384
                    $oldref = dol_sanitizeFileName($this->ref);
1385
                    $newref = dol_sanitizeFileName($num);
1386
                    $dirsource = $conf->expensereport->multidir_output[$this->entity] . '/' . $oldref;
1387
                    $dirdest = $conf->expensereport->multidir_output[$this->entity] . '/' . $newref;
1388
                    if (!$error && file_exists($dirsource)) {
1389
                        dol_syslog(get_class($this) . "::setValidate() rename dir " . $dirsource . " into " . $dirdest);
1390
1391
                        if (@rename($dirsource, $dirdest)) {
1392
                            dol_syslog("Rename ok");
1393
                            // Rename docs starting with $oldref with $newref
1394
                            $listoffiles = dol_dir_list($dirdest, 'files', 1, '^' . preg_quote($oldref, '/'));
1395
                            foreach ($listoffiles as $fileentry) {
1396
                                $dirsource = $fileentry['name'];
1397
                                $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
1398
                                $dirsource = $fileentry['path'] . '/' . $dirsource;
1399
                                $dirdest = $fileentry['path'] . '/' . $dirdest;
1400
                                @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

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