Passed
Push — main ( f1540e...02d90d )
by Rafael
45:15
created

ExpenseReport::initAsSpecimen()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 55
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 41
nc 2
nop 0
dl 0
loc 55
rs 9.264
c 0
b 0
f 0

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
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 DoliModules\ExpenseReport\Model;
28
29
/**
30
 *       \file       htdocs/expensereport/class/expensereport.class.php
31
 *       \ingroup    expensereport
32
 *       \brief      File to manage Expense Reports
33
 */
34
35
use DoliCore\Base\GenericDocument;
36
use DoliCore\Model\WorkboardResponse;
37
38
/**
39
 * Class to manage Trips and Expenses
40
 */
41
class ExpenseReport extends GenericDocument
42
{
43
    /**
44
     * Draft status
45
     */
46
    const STATUS_DRAFT = 0;
47
    /**
48
     * Validated (need to be paid)
49
     */
50
    const STATUS_VALIDATED = 2;
51
    /**
52
     * Classified canceled
53
     */
54
    const STATUS_CANCELED = 4;
55
    /**
56
     * Classified approved
57
     */
58
    const STATUS_APPROVED = 5;
59
    /**
60
     * Classified paid.
61
     */
62
    const STATUS_CLOSED = 6;
63
    /**
64
     * Classified refused
65
     */
66
    const STATUS_REFUSED = 99;
67
    /**
68
     * @var string ID to identify managed object
69
     */
70
    public $element = 'expensereport';
71
    /**
72
     * @var string Name of table without prefix where object is stored
73
     */
74
    public $table_element = 'expensereport';
75
    /**
76
     * @var string table element line name
77
     */
78
    public $table_element_line = 'expensereport_det';
79
    /**
80
     * @var string Fieldname with ID of parent key if this field has a parent
81
     */
82
    public $fk_element = 'fk_expensereport';
83
    /**
84
     * @var string String with name of icon for myobject. Must be the part after the 'object_' into object_myobject.png
85
     */
86
    public $picto = 'trip';
87
    /**
88
     * @var ExpenseReportLine[] array of expensereport lines
89
     */
90
    public $lines = [];
91
    /**
92
     * @var ExpenseReportLine expensereport lines
93
     */
94
    public $line;
95
    /**
96
     * @var int|string
97
     */
98
    public $date_debut;
99
    /**
100
     * @var int|string
101
     */
102
    public $date_fin;
103
    /**
104
     * @var int|string
105
     */
106
    public $date_approbation;
107
    /**
108
     * @var int ID
109
     */
110
    public $fk_user;
111
    // Paiement
112
    /**
113
     * @var int ID
114
     */
115
    public $user_approve_id;
116
    /**
117
     * 0=draft, 2=validated (attente approb), 4=canceled, 5=approved, 6=paid, 99=denied
118
     *
119
     * @var int     Status
120
     */
121
    public $status;
122
    /**
123
     * 0=draft, 2=validated (attente approb), 4=canceled, 5=approved, 6=paid, 99=denied
124
     *
125
     * @var int     Status
126
     * @deprecated
127
     */
128
    public $fk_statut;
129
    public $fk_c_paiement;
130
131
    // ACTIONS
132
133
    // Create
134
    public $modepaymentid;
135
    public $paid;
136
    public $user_paid_infos; // Note fk_user_author is not the 'author' but the guy the expense report is for.
137
138
    // Update
139
    public $user_author_infos;
140
    public $user_validator_infos;
141
142
    // Refus
143
    public $rule_warning_message;
144
    /**
145
     * @var int|string
146
     */
147
    public $date_create;
148
    /**
149
     * @var int ID of user creator
150
     */
151
    public $fk_user_creat;
152
153
    // Annulation
154
/**
155
     * @var int ID of user who reclaim expense report
156
     */
157
    public $fk_user_author;
158
    /**
159
     * @var int|string
160
     */
161
    public $date_modif;
162
    /**
163
     * @var int ID
164
     */
165
    public $fk_user_modif;
166
    /**
167
     * @var int|string
168
     */
169
    public $date_refuse;
170
    /**
171
     * @var string
172
     */
173
    public $detail_refuse;
174
    /**
175
     * @var int ID
176
     */
177
    public $fk_user_refuse;
178
    /**
179
     * @var int|string
180
     */
181
    public $date_cancel;
182
    /**
183
     * @var string
184
     */
185
    public $detail_cancel;
186
187
    // Approve
188
    /**
189
     * @var int ID of user who cancel expense report
190
     */
191
    public $fk_user_cancel;
192
    /**
193
     * @var int User that is defined to approve
194
     */
195
    public $fk_user_validator;
196
    /**
197
     * Validation date
198
     * @var int
199
     * @deprecated
200
     * @see $date_valid
201
     */
202
    public $datevalid;  // for backward compatibility (real field should be total_localtax1 defined into CommonObject)
203
        /**
204
     * Validation date
205
     * @var int
206
     */
207
    public $date_valid;  // for backward compatibility (real field should be total_localtax2 defined into CommonObject)
208
    /**
209
     * @var int ID of User making validation
210
     */
211
    public $fk_user_valid;
212
    /**
213
     * @var int ID
214
     */
215
    public $user_valid_infos;
216
    /**
217
     * @var int|string
218
     */
219
    public $date_approve;
220
    /**
221
     * @var int ID User that has approved
222
     */
223
    public $fk_user_approve;
224
public $localtax1;
225
public $localtax2;
226
    /**
227
     * @var array
228
     */
229
    public $labelStatus = [];
230
    /**
231
     * @var array
232
     */
233
    public $labelStatusShort = [];
234
    public $fields = [
235
        'rowid' => ['type' => 'integer', 'label' => 'ID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10],
236
        'ref' => ['type' => 'varchar(50)', 'label' => 'Ref', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'showoncombobox' => 1, 'position' => 15],
237
        'entity' => ['type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 20],
238
        'ref_number_int' => ['type' => 'integer', 'label' => 'Ref number int', 'enabled' => 1, 'visible' => -1, 'position' => 25],
239
        'ref_ext' => ['type' => 'integer', 'label' => 'Ref ext', 'enabled' => 1, 'visible' => -1, 'position' => 30],
240
        'total_ht' => ['type' => 'double(24,8)', 'label' => 'Total ht', 'enabled' => 1, 'visible' => -1, 'position' => 35],
241
        'total_tva' => ['type' => 'double(24,8)', 'label' => 'Total tva', 'enabled' => 1, 'visible' => -1, 'position' => 40],
242
        'localtax1' => ['type' => 'double(24,8)', 'label' => 'Localtax1', 'enabled' => 1, 'visible' => -1, 'position' => 45],
243
        'localtax2' => ['type' => 'double(24,8)', 'label' => 'Localtax2', 'enabled' => 1, 'visible' => -1, 'position' => 50],
244
        'total_ttc' => ['type' => 'double(24,8)', 'label' => 'Total ttc', 'enabled' => 1, 'visible' => -1, 'position' => 55],
245
        'date_debut' => ['type' => 'date', 'label' => 'Date debut', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 60],
246
        'date_fin' => ['type' => 'date', 'label' => 'Date fin', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 65],
247
        'date_valid' => ['type' => 'datetime', 'label' => 'Date valid', 'enabled' => 1, 'visible' => -1, 'position' => 75],
248
        'date_approve' => ['type' => 'datetime', 'label' => 'Date approve', 'enabled' => 1, 'visible' => -1, 'position' => 80],
249
        'date_refuse' => ['type' => 'datetime', 'label' => 'Date refuse', 'enabled' => 1, 'visible' => -1, 'position' => 85],
250
        'date_cancel' => ['type' => 'datetime', 'label' => 'Date cancel', 'enabled' => 1, 'visible' => -1, 'position' => 90],
251
        'fk_user_author' => ['type' => 'integer', 'label' => 'Fk user author', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 100],
252
        'fk_user_modif' => ['type' => 'integer', 'label' => 'Fk user modif', 'enabled' => 1, 'visible' => -1, 'position' => 105],
253
        'fk_user_valid' => ['type' => 'integer', 'label' => 'Fk user valid', 'enabled' => 1, 'visible' => -1, 'position' => 110],
254
        'fk_user_validator' => ['type' => 'integer', 'label' => 'Fk user validator', 'enabled' => 1, 'visible' => -1, 'position' => 115],
255
        'fk_user_approve' => ['type' => 'integer', 'label' => 'Fk user approve', 'enabled' => 1, 'visible' => -1, 'position' => 120],
256
        'fk_user_refuse' => ['type' => 'integer', 'label' => 'Fk user refuse', 'enabled' => 1, 'visible' => -1, 'position' => 125],
257
        'fk_user_cancel' => ['type' => 'integer', 'label' => 'Fk user cancel', 'enabled' => 1, 'visible' => -1, 'position' => 130],
258
        'fk_c_paiement' => ['type' => 'integer', 'label' => 'Fk c paiement', 'enabled' => 1, 'visible' => -1, 'position' => 140],
259
        'paid' => ['type' => 'integer', 'label' => 'Paid', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 145],
260
        'note_public' => ['type' => 'html', 'label' => 'Note public', 'enabled' => 1, 'visible' => 0, 'position' => 150],
261
        'note_private' => ['type' => 'html', 'label' => 'Note private', 'enabled' => 1, 'visible' => 0, 'position' => 155],
262
        'detail_refuse' => ['type' => 'varchar(255)', 'label' => 'Detail refuse', 'enabled' => 1, 'visible' => -1, 'position' => 160],
263
        'detail_cancel' => ['type' => 'varchar(255)', 'label' => 'Detail cancel', 'enabled' => 1, 'visible' => -1, 'position' => 165],
264
        'integration_compta' => ['type' => 'integer', 'label' => 'Integration compta', 'enabled' => 1, 'visible' => -1, 'position' => 170],
265
        'fk_bank_account' => ['type' => 'integer', 'label' => 'Fk bank account', 'enabled' => 1, 'visible' => -1, 'position' => 175],
266
        'fk_multicurrency' => ['type' => 'integer', 'label' => 'Fk multicurrency', 'enabled' => 1, 'visible' => -1, 'position' => 185],
267
        'multicurrency_code' => ['type' => 'varchar(255)', 'label' => 'Multicurrency code', 'enabled' => 1, 'visible' => -1, 'position' => 190],
268
        'multicurrency_tx' => ['type' => 'double(24,8)', 'label' => 'Multicurrency tx', 'enabled' => 1, 'visible' => -1, 'position' => 195],
269
        'multicurrency_total_ht' => ['type' => 'double(24,8)', 'label' => 'Multicurrency total ht', 'enabled' => 1, 'visible' => -1, 'position' => 200],
270
        'multicurrency_total_tva' => ['type' => 'double(24,8)', 'label' => 'Multicurrency total tva', 'enabled' => 1, 'visible' => -1, 'position' => 205],
271
        'multicurrency_total_ttc' => ['type' => 'double(24,8)', 'label' => 'Multicurrency total ttc', 'enabled' => 1, 'visible' => -1, 'position' => 210],
272
        'extraparams' => ['type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 220],
273
        'date_create' => ['type' => 'datetime', 'label' => 'Date create', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 300],
274
        'tms' => ['type' => 'timestamp', 'label' => 'Tms', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 305],
275
        'import_key' => ['type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -1, 'position' => 1000],
276
        'model_pdf' => ['type' => 'varchar(255)', 'label' => 'Model pdf', 'enabled' => 1, 'visible' => 0, 'position' => 1010],
277
        'fk_statut' => ['type' => 'integer', 'label' => 'Fk statut', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 500],
278
    ];
279
280
    /**
281
     *  Constructor
282
     *
283
     * @param DoliDB $db Handler access base de donnees
0 ignored issues
show
Bug introduced by
The type DoliModules\ExpenseReport\Model\DoliDB was not found. Did you mean DoliDB? If so, make sure to prefix the type with \.
Loading history...
284
     */
285
    public function __construct($db)
286
    {
287
        $this->db = $db;
288
        $this->total_ht = 0;
289
        $this->total_ttc = 0;
290
        $this->total_tva = 0;
291
        $this->total_localtax1 = 0;
292
        $this->total_localtax2 = 0;
293
        $this->localtax1 = 0;   // For backward compatibility
294
        $this->localtax2 = 0;   // For backward compatibility
295
        $this->modepaymentid = 0;
296
297
        // List of language codes for status
298
        $this->labelStatusShort = [0 => 'Draft', 2 => 'Validated', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused'];
299
        $this->labelStatus = [0 => 'Draft', 2 => 'ValidatedWaitingApproval', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused'];
300
    }
301
302
    /**
303
     *  Load an object from its id and create a new one in database
304
     *
305
     * @param User $user           User making the clone
306
     * @param int  $fk_user_author Id of new user
307
     *
308
     * @return     int                         New id of clone
309
     */
310
    public function createFromClone(User $user, $fk_user_author)
311
    {
312
        global $hookmanager;
313
314
        $error = 0;
315
316
        if (empty($fk_user_author)) {
317
            $fk_user_author = $user->id;
318
        }
319
320
        $this->db->begin();
321
322
        // get extrafields so they will be clone
323
        //foreach($this->lines as $line)
324
        //$line->fetch_optionals();
325
326
        // Load source object
327
        $objFrom = clone $this;
328
329
        $this->id = 0;
330
        $this->ref = '';
331
        $this->status = 0;
332
        $this->fk_statut = 0; // deprecated
333
334
        // Clear fields
335
        $this->fk_user_creat = $user->id;
336
        $this->fk_user_author = $fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
337
        $this->fk_user_valid = 0;
338
        $this->date_create = '';
339
        $this->date_creation = '';
340
        $this->date_validation = '';
341
342
        // Remove link on lines to a joined file
343
        if (is_array($this->lines) && count($this->lines) > 0) {
344
            foreach ($this->lines as $key => $line) {
345
                $this->lines[$key]->fk_ecm_files = 0;
346
            }
347
        }
348
349
        // Create clone
350
        $this->context['createfromclone'] = 'createfromclone';
351
        $result = $this->create($user);
352
        if ($result < 0) {
353
            $error++;
354
        }
355
356
        if (!$error) {
357
            // Hook of thirdparty module
358
            if (is_object($hookmanager)) {
359
                $parameters = ['objFrom' => $objFrom];
360
                $action = '';
361
                $reshook = $hookmanager->executeHooks('createFrom', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
362
                if ($reshook < 0) {
363
                    $this->setErrorsFromObject($hookmanager);
364
                    $error++;
365
                }
366
            }
367
        }
368
369
        unset($this->context['createfromclone']);
370
371
        // End
372
        if (!$error) {
373
            $this->db->commit();
374
            return $this->id;
375
        } else {
376
            $this->db->rollback();
377
            return -1;
378
        }
379
    }
380
381
    /**
382
     * Create object in database
383
     *
384
     * @param User $user      User that create
385
     * @param int  $notrigger Disable triggers
386
     *
387
     * @return  int             Return integer <0 if KO, >0 if OK
388
     */
389
    public function create($user, $notrigger = 0)
390
    {
391
        global $conf, $langs;
392
393
        $now = dol_now();
394
395
        $error = 0;
396
397
        // Check parameters
398
        if (empty($this->date_debut) || empty($this->date_fin)) {
399
            $this->error = $langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Date'));
400
            return -1;
401
        }
402
403
        $fuserid = $this->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
404
        if (empty($fuserid)) {
405
            $fuserid = $user->id;
406
        }
407
408
        $this->db->begin();
409
410
        $sql = "INSERT INTO " . MAIN_DB_PREFIX . $this->table_element . " (";
411
        $sql .= "ref";
412
        $sql .= ",total_ht";
413
        $sql .= ",total_ttc";
414
        $sql .= ",total_tva";
415
        $sql .= ",date_debut";
416
        $sql .= ",date_fin";
417
        $sql .= ",date_create";
418
        $sql .= ",fk_user_creat";
419
        $sql .= ",fk_user_author";
420
        $sql .= ",fk_user_validator";
421
        $sql .= ",fk_user_approve";
422
        $sql .= ",fk_user_modif";
423
        $sql .= ",fk_statut";
424
        $sql .= ",fk_c_paiement";
425
        $sql .= ",paid";
426
        $sql .= ",note_public";
427
        $sql .= ",note_private";
428
        $sql .= ",entity";
429
        $sql .= ") VALUES(";
430
        $sql .= "'(PROV)'";
431
        $sql .= ", " . price2num($this->total_ht, 'MT');
432
        $sql .= ", " . price2num($this->total_ttc, 'MT');
433
        $sql .= ", " . price2num($this->total_tva, 'MT');
434
        $sql .= ", '" . $this->db->idate($this->date_debut) . "'";
435
        $sql .= ", '" . $this->db->idate($this->date_fin) . "'";
436
        $sql .= ", '" . $this->db->idate($now) . "'";
437
        $sql .= ", " . ((int) $user->id);
438
        $sql .= ", " . ((int) $fuserid);
439
        $sql .= ", " . ($this->fk_user_validator > 0 ? ((int) $this->fk_user_validator) : "null");
440
        $sql .= ", " . ($this->fk_user_approve > 0 ? ((int) $this->fk_user_approve) : "null");
441
        $sql .= ", " . ($this->fk_user_modif > 0 ? ((int) $this->fk_user_modif) : "null");
442
        $sql .= ", " . ($this->fk_statut > 1 ? ((int) $this->fk_statut) : 0);
443
        $sql .= ", " . ($this->modepaymentid ? ((int) $this->modepaymentid) : "null");
444
        $sql .= ", 0";
445
        $sql .= ", " . ($this->note_public ? "'" . $this->db->escape($this->note_public) . "'" : "null");
446
        $sql .= ", " . ($this->note_private ? "'" . $this->db->escape($this->note_private) . "'" : "null");
447
        $sql .= ", " . ((int) $conf->entity);
448
        $sql .= ")";
449
450
        $result = $this->db->query($sql);
451
        if ($result) {
452
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
453
            $this->ref = '(PROV' . $this->id . ')';
454
455
            $sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . " SET ref='" . $this->db->escape($this->ref) . "' WHERE rowid=" . ((int) $this->id);
456
            $resql = $this->db->query($sql);
457
            if (!$resql) {
458
                $this->error = $this->db->lasterror();
459
                $error++;
460
            }
461
462
            if (!$error) {
463
                if (is_array($this->lines) && count($this->lines) > 0) {
464
                    foreach ($this->lines as $line) {
465
                        // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
466
                        //if (! is_object($line)) $line=json_decode(json_encode($line), false);  // convert recursively array into object.
467
                        if (!is_object($line)) {
468
                            $line = (object) $line;
469
                            $newndfline = new ExpenseReportLine($this->db);
470
                            $newndfline->fk_expensereport = $line->fk_expensereport;
471
                            $newndfline->fk_c_type_fees = $line->fk_c_type_fees;
472
                            $newndfline->fk_project = $line->fk_project;
473
                            $newndfline->vatrate = $line->vatrate;
474
                            $newndfline->vat_src_code = $line->vat_src_code;
475
                            $newndfline->localtax1_tx = $line->localtax1_tx;
476
                            $newndfline->localtax2_tx = $line->localtax2_tx;
477
                            $newndfline->localtax1_type = $line->localtax1_type;
478
                            $newndfline->localtax2_type = $line->localtax2_type;
479
                            $newndfline->comments = $line->comments;
480
                            $newndfline->qty = $line->qty;
481
                            $newndfline->value_unit = $line->value_unit;
482
                            $newndfline->total_ht = $line->total_ht;
483
                            $newndfline->total_ttc = $line->total_ttc;
484
                            $newndfline->total_tva = $line->total_tva;
485
                            $newndfline->total_localtax1 = $line->total_localtax1;
486
                            $newndfline->total_localtax2 = $line->total_localtax2;
487
                            $newndfline->date = $line->date;
488
                            $newndfline->rule_warning_message = $line->rule_warning_message;
489
                            $newndfline->fk_c_exp_tax_cat = $line->fk_c_exp_tax_cat;
490
                            $newndfline->fk_ecm_files = $line->fk_ecm_files;
491
                        } else {
492
                            $newndfline = $line;
493
                        }
494
                        //$newndfline=new ExpenseReportLine($this->db);
495
                        $newndfline->fk_expensereport = $this->id;
496
                        $result = $newndfline->insert();
497
                        if ($result < 0) {
498
                            $this->error = $newndfline->error;
499
                            $this->errors = $newndfline->errors;
500
                            $error++;
501
                            break;
502
                        }
503
                    }
504
                }
505
            }
506
507
            if (!$error) {
508
                $result = $this->insertExtraFields();
509
                if ($result < 0) {
510
                    $error++;
511
                }
512
            }
513
514
            if (!$error) {
515
                $result = $this->update_price(1);
516
                if ($result > 0) {
517
                    if (!$notrigger) {
518
                        // Call trigger
519
                        $result = $this->call_trigger('EXPENSE_REPORT_CREATE', $user);
520
521
                        if ($result < 0) {
522
                            $error++;
523
                        }
524
                        // End call triggers
525
                    }
526
527
                    if (empty($error)) {
528
                        $this->db->commit();
529
                        return $this->id;
530
                    } else {
531
                        $this->db->rollback();
532
                        return -4;
533
                    }
534
                } else {
535
                    $this->db->rollback();
536
                    return -3;
537
                }
538
            } else {
539
                dol_syslog(get_class($this) . "::create error " . $this->error, LOG_ERR);
540
                $this->db->rollback();
541
                return -2;
542
            }
543
        } else {
544
            $this->error = $this->db->lasterror() . " sql=" . $sql;
545
            $this->db->rollback();
546
            return -1;
547
        }
548
    }
549
550
    /**
551
     *  Classify the expense report as paid
552
     *
553
     * @param int  $id        Id of expense report
554
     * @param user $fuser     User making change
555
     * @param int  $notrigger Disable triggers
556
     *
557
     * @return   int                         Return integer <0 if KO, >0 if OK
558
     * @deprecated
559
     * @see setPaid()
560
     */
561
    public function set_paid($id, $fuser, $notrigger = 0)
562
    {
563
        // phpcs:enable
564
        dol_syslog(get_class($this) . "::set_paid is deprecated, use setPaid instead", LOG_NOTICE);
565
        return $this->setPaid($id, $fuser, $notrigger);
566
    }
567
568
    /**
569
     *    Classify the expense report as paid
570
     *
571
     * @param int  $id        Id of expense report
572
     * @param user $fuser     User making change
573
     * @param int  $notrigger Disable triggers
574
     *
575
     * @return   int                         Return integer <0 if KO, >0 if OK
576
     */
577
    public function setPaid($id, $fuser, $notrigger = 0)
578
    {
579
        $error = 0;
580
        $this->db->begin();
581
582
        $sql = "UPDATE " . MAIN_DB_PREFIX . "expensereport";
583
        $sql .= " SET fk_statut = " . self::STATUS_CLOSED . ", paid=1";
584
        $sql .= " WHERE rowid = " . ((int) $id) . " AND fk_statut = " . self::STATUS_APPROVED;
585
586
        dol_syslog(get_class($this) . "::setPaid", LOG_DEBUG);
587
        $resql = $this->db->query($sql);
588
        if ($resql) {
589
            if ($this->db->affected_rows($resql)) {
590
                if (!$notrigger) {
591
                    // Call trigger
592
                    $result = $this->call_trigger('EXPENSE_REPORT_PAID', $fuser);
593
594
                    if ($result < 0) {
595
                        $error++;
596
                    }
597
                    // End call triggers
598
                }
599
600
                if (empty($error)) {
601
                    $this->db->commit();
602
                    return 1;
603
                } else {
604
                    $this->db->rollback();
605
                    $this->error = $this->db->error();
606
                    return -2;
607
                }
608
            } else {
609
                $this->db->commit();
610
                return 0;
611
            }
612
        } else {
613
            $this->db->rollback();
614
            dol_print_error($this->db);
615
            return -1;
616
        }
617
    }
618
619
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
620
621
    /**
622
     *  Load information on object
623
     *
624
     * @param int $id Id of object
625
     *
626
     * @return void
627
     */
628
    public function info($id)
629
    {
630
        global $conf;
631
632
        $sql = "SELECT f.rowid,";
633
        $sql .= " f.date_create as datec,";
634
        $sql .= " f.tms as date_modification,";
635
        $sql .= " f.date_valid as datev,";
636
        $sql .= " f.date_approve as datea,";
637
        $sql .= " f.fk_user_creat as fk_user_creation,";
638
        $sql .= " f.fk_user_author as fk_user_author,";
639
        $sql .= " f.fk_user_modif as fk_user_modification,";
640
        $sql .= " f.fk_user_valid,";
641
        $sql .= " f.fk_user_approve";
642
        $sql .= " FROM " . MAIN_DB_PREFIX . "expensereport as f";
643
        $sql .= " WHERE f.rowid = " . ((int) $id);
644
        $sql .= " AND f.entity = " . $conf->entity;
645
646
647
        $resql = $this->db->query($sql);
648
        if ($resql) {
649
            if ($this->db->num_rows($resql)) {
650
                $obj = $this->db->fetch_object($resql);
651
652
                $this->id = $obj->rowid;
653
654
                $this->date_creation = $this->db->jdate($obj->datec);
655
                $this->date_modification = $this->db->jdate($obj->date_modification);
656
                $this->date_validation = $this->db->jdate($obj->datev);
657
                $this->date_approbation = $this->db->jdate($obj->datea);
658
659
                $this->user_creation_id = $obj->fk_user_author;
660
                $this->user_creation_id = $obj->fk_user_creation;
661
                $this->user_validation_id = $obj->fk_user_valid;
662
                $this->user_modification_id = $obj->fk_user_modification;
663
                $this->user_approve_id = $obj->fk_user_approve;
664
            }
665
            $this->db->free($resql);
666
        } else {
667
            dol_print_error($this->db);
668
        }
669
    }
670
671
    /**
672
     *  Initialise an instance with random values.
673
     *  Used to build previews or test instances.
674
     *  id must be 0 if object instance is a specimen.
675
     *
676
     * @return int
677
     */
678
    public function initAsSpecimen()
679
    {
680
        global $user, $langs;
681
682
        $now = dol_now();
683
684
        // Initialise parameters
685
        $this->id = 0;
686
        $this->ref = 'SPECIMEN';
687
        $this->specimen = 1;
688
        $this->entity = 1;
689
        $this->date_create = $now;
690
        $this->date_debut = $now;
691
        $this->date_fin = $now;
692
        $this->date_valid = $now;
693
        $this->date_approve = $now;
694
695
        $type_fees_id = 2; // TF_TRIP
696
697
        $this->status = 5;
698
699
        $this->fk_user_author = $user->id;
700
        $this->fk_user_validator = $user->id;
701
        $this->fk_user_valid = $user->id;
702
        $this->fk_user_approve = $user->id;
703
704
        $this->note_private = 'Private note';
705
        $this->note_public = 'SPECIMEN';
706
        $nbp = 5;
707
        $xnbp = 0;
708
        while ($xnbp < $nbp) {
709
            $line = new ExpenseReportLine($this->db);
710
            $line->comments = $langs->trans("Comment") . " " . $xnbp;
711
            $line->date = ($now - 3600 * (1 + $xnbp));
712
            $line->total_ht = 100;
713
            $line->total_tva = 20;
714
            $line->total_ttc = 120;
715
            $line->qty = 1;
716
            $line->vatrate = 20;
717
            $line->value_unit = 120;
718
            $line->fk_expensereport = 0;
719
            $line->type_fees_code = 'TRA';
720
            $line->fk_c_type_fees = $type_fees_id;
721
722
            $line->projet_ref = 'ABC';
723
724
            $this->lines[$xnbp] = $line;
725
            $xnbp++;
726
727
            $this->total_ht += $line->total_ht;
728
            $this->total_tva += $line->total_tva;
729
            $this->total_ttc += $line->total_ttc;
730
        }
731
732
        return 1;
733
    }
734
735
    /**
736
     * fetch_line_by_project
737
     *
738
     * @param int  $projectid Project id
739
     * @param User $user      User
740
     *
741
     * @return  int                     Return integer <0 if KO, >0 if OK
742
     */
743
    public function fetch_line_by_project($projectid, $user)
744
    {
745
        // phpcs:enable
746
        global $langs;
747
748
        $langs->load('trips');
749
750
        if ($user->hasRight('expensereport', 'lire')) {
751
            $sql = "SELECT de.fk_expensereport, de.date, de.comments, de.total_ht, de.total_ttc";
752
            $sql .= " FROM " . MAIN_DB_PREFIX . "expensereport_det as de";
753
            $sql .= " WHERE de.fk_projet = " . ((int) $projectid);
754
755
            dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
756
            $result = $this->db->query($sql);
757
            if ($result) {
758
                $num = $this->db->num_rows($result);
759
                $i = 0;
760
                $total_HT = 0;
761
                $total_TTC = 0;
762
763
                while ($i < $num) {
764
                    $objp = $this->db->fetch_object($result);
765
766
                    $sql2 = "SELECT d.rowid, d.fk_user_author, d.ref, d.fk_statut as status";
767
                    $sql2 .= " FROM " . MAIN_DB_PREFIX . "expensereport as d";
768
                    $sql2 .= " WHERE d.rowid = " . ((int) $objp->fk_expensereport);
769
770
                    $result2 = $this->db->query($sql2);
771
                    $obj = $this->db->fetch_object($result2);
772
773
                    $objp->fk_user_author = $obj->fk_user_author;
774
                    $objp->ref = $obj->ref;
775
                    $objp->fk_c_expensereport_status = $obj->status;
776
                    $objp->rowid = $obj->rowid;
777
778
                    $total_HT = $total_HT + $objp->total_ht;
779
                    $total_TTC = $total_TTC + $objp->total_ttc;
780
                    $author = new User($this->db);
781
                    $author->fetch($objp->fk_user_author);
782
783
                    print '<tr>';
784
                    print '<td><a href="' . DOL_URL_ROOT . '/expensereport/card.php?id=' . $objp->rowid . '">' . $objp->ref_num . '</a></td>';
785
                    print '<td class="center">' . dol_print_date($objp->date, 'day') . '</td>';
786
                    print '<td>' . $author->getNomUrl(1) . '</td>';
787
                    print '<td>' . $objp->comments . '</td>';
788
                    print '<td class="right">' . price($objp->total_ht) . '</td>';
789
                    print '<td class="right">' . price($objp->total_ttc) . '</td>';
790
                    print '<td class="right">';
791
792
                    switch ($objp->fk_c_expensereport_status) {
793
                        case 4:
794
                            print img_picto($langs->trans('StatusOrderCanceled'), 'statut5');
795
                            break;
796
                        case 1:
797
                            print $langs->trans('Draft') . ' ' . img_picto($langs->trans('Draft'), 'statut0');
798
                            break;
799
                        case 2:
800
                            print $langs->trans('TripForValid') . ' ' . img_picto($langs->trans('TripForValid'), 'statut3');
801
                            break;
802
                        case 5:
803
                            print $langs->trans('TripForPaid') . ' ' . img_picto($langs->trans('TripForPaid'), 'statut3');
804
                            break;
805
                        case 6:
806
                            print $langs->trans('TripPaid') . ' ' . img_picto($langs->trans('TripPaid'), 'statut4');
807
                            break;
808
                    }
809
                    /*
810
                     if ($status==4) return img_picto($langs->trans('StatusOrderCanceled'),'statut5');
811
                    if ($status==1) return img_picto($langs->trans('StatusOrderDraft'),'statut0');
812
                    if ($status==2) return img_picto($langs->trans('StatusOrderValidated'),'statut1');
813
                    if ($status==2) return img_picto($langs->trans('StatusOrderOnProcess'),'statut3');
814
                    if ($status==5) return img_picto($langs->trans('StatusOrderToBill'),'statut4');
815
                    if ($status==6) return img_picto($langs->trans('StatusOrderOnProcess'),'statut6');
816
                    */
817
                    print '</td>';
818
                    print '</tr>';
819
820
                    $i++;
821
                }
822
823
                print '<tr class="liste_total"><td colspan="4">' . $langs->trans("Number") . ': ' . $i . '</td>';
824
                print '<td class="right" width="100">' . $langs->trans("TotalHT") . ' : ' . price($total_HT) . '</td>';
825
                print '<td class="right" width="100">' . $langs->trans("TotalTTC") . ' : ' . price($total_TTC) . '</td>';
826
                print '<td>&nbsp;</td>';
827
                print '</tr>';
828
            } else {
829
                $this->error = $this->db->lasterror();
830
                return -1;
831
            }
832
        }
833
834
        return 0;
835
    }
836
837
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
838
839
    /**
840
     *  Load an object from database
841
     *
842
     * @param int    $id  Id                      {@min 1}
843
     * @param string $ref Ref                     {@name ref}
844
     *
845
     * @return int             Return integer <0 if KO, >0 if OK
846
     */
847
    public function fetch($id, $ref = '')
848
    {
849
        $sql = "SELECT d.rowid, d.entity, d.ref, d.note_public, d.note_private,"; // DEFAULT
850
        $sql .= " d.detail_refuse, d.detail_cancel, d.fk_user_refuse, d.fk_user_cancel,"; // ACTIONS
851
        $sql .= " d.date_refuse, d.date_cancel,"; // ACTIONS
852
        $sql .= " d.total_ht, d.total_ttc, d.total_tva,";
853
        $sql .= " d.localtax1 as total_localtax1, d.localtax2 as total_localtax2,";
854
        $sql .= " d.date_debut, d.date_fin, d.date_create, d.tms as date_modif, d.date_valid, d.date_approve,"; // DATES (datetime)
855
        $sql .= " d.fk_user_creat, d.fk_user_author, d.fk_user_modif, d.fk_user_validator,";
856
        $sql .= " d.fk_user_valid, d.fk_user_approve,";
857
        $sql .= " d.fk_statut as status, d.fk_c_paiement, d.paid";
858
        $sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element . " as d";
859
        if ($ref) {
860
            $sql .= " WHERE d.ref = '" . $this->db->escape($ref) . "'";
861
        } else {
862
            $sql .= " WHERE d.rowid = " . ((int) $id);
863
        }
864
        //$sql.= $restrict;
865
866
        dol_syslog(get_class($this) . "::fetch", LOG_DEBUG);
867
        $resql = $this->db->query($sql);
868
        if ($resql) {
869
            $obj = $this->db->fetch_object($resql);
870
            if ($obj) {
871
                $this->id = $obj->rowid;
872
                $this->ref = $obj->ref;
873
874
                $this->entity = $obj->entity;
875
876
                $this->total_ht = $obj->total_ht;
877
                $this->total_tva = $obj->total_tva;
878
                $this->total_ttc = $obj->total_ttc;
879
                $this->localtax1 = $obj->total_localtax1;        // For backward compatibility
880
                $this->localtax2 = $obj->total_localtax2;        // For backward compatibility
881
                $this->total_localtax1 = $obj->total_localtax1;
882
                $this->total_localtax2 = $obj->total_localtax2;
883
884
                $this->note_public = $obj->note_public;
885
                $this->note_private = $obj->note_private;
886
                $this->detail_refuse = $obj->detail_refuse;
887
                $this->detail_cancel = $obj->detail_cancel;
888
889
                $this->date_debut = $this->db->jdate($obj->date_debut);
890
                $this->date_fin = $this->db->jdate($obj->date_fin);
891
                $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...
892
                $this->date_approve = $this->db->jdate($obj->date_approve);
893
                $this->date_create = $this->db->jdate($obj->date_create);
894
                $this->date_modif = $this->db->jdate($obj->date_modif);
895
                $this->date_refuse = $this->db->jdate($obj->date_refuse);
896
                $this->date_cancel = $this->db->jdate($obj->date_cancel);
897
898
                $this->fk_user_creat = $obj->fk_user_creat;
899
                $this->fk_user_author = $obj->fk_user_author; // Note fk_user_author is not the 'author' but the guy the expense report is for.
900
                $this->fk_user_modif = $obj->fk_user_modif;
901
                $this->fk_user_validator = $obj->fk_user_validator;
902
                $this->fk_user_valid = $obj->fk_user_valid;
903
                $this->fk_user_refuse = $obj->fk_user_refuse;
904
                $this->fk_user_cancel = $obj->fk_user_cancel;
905
                $this->fk_user_approve = $obj->fk_user_approve;
906
907
                $user_author = new User($this->db);
908
                if ($this->fk_user_author > 0) {
909
                    $user_author->fetch($this->fk_user_author);
910
                }
911
912
                $this->user_author_infos = dolGetFirstLastname($user_author->firstname, $user_author->lastname);
913
914
                $user_approver = new User($this->db);
915
                if ($this->fk_user_approve > 0) {
916
                    $user_approver->fetch($this->fk_user_approve);
917
                } elseif ($this->fk_user_validator > 0) {
918
                    $user_approver->fetch($this->fk_user_validator); // For backward compatibility
919
                }
920
                $this->user_validator_infos = dolGetFirstLastname($user_approver->firstname, $user_approver->lastname);
921
922
                $this->fk_statut = $obj->status; // deprecated
923
                $this->status = $obj->status;
924
                $this->fk_c_paiement = $obj->fk_c_paiement;
925
                $this->paid = $obj->paid;
926
927
                if ($this->status == self::STATUS_APPROVED || $this->status == self::STATUS_CLOSED) {
928
                    $user_valid = new User($this->db);
929
                    if ($this->fk_user_valid > 0) {
930
                        $user_valid->fetch($this->fk_user_valid);
931
                    }
932
                    $this->user_valid_infos = dolGetFirstLastname($user_valid->firstname, $user_valid->lastname);
0 ignored issues
show
Documentation Bug introduced by
The property $user_valid_infos was declared of type integer, but dolGetFirstLastname($use... $user_valid->lastname) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
933
                }
934
935
                $this->fetch_optionals();
936
937
                $result = $this->fetch_lines();
938
939
                return $result;
940
            } else {
941
                return 0;
942
            }
943
        } else {
944
            $this->error = $this->db->lasterror();
945
            return -1;
946
        }
947
    }
948
949
    /**
950
     * fetch_lines
951
     *
952
     * @return  int     Return integer <0 if OK, >0 if KO
953
     */
954
    public function fetch_lines()
955
    {
956
        // phpcs:enable
957
        $this->lines = [];
958
959
        $sql = ' SELECT de.rowid, de.comments, de.qty, de.value_unit, de.date, de.rang,';
960
        $sql .= " de." . $this->fk_element . ", de.fk_c_type_fees, de.fk_c_exp_tax_cat, de.fk_projet as fk_project,";
961
        $sql .= ' de.tva_tx, de.vat_src_code,';
962
        $sql .= ' de.localtax1_tx, de.localtax2_tx, de.localtax1_type, de.localtax2_type,';
963
        $sql .= ' de.fk_ecm_files,';
964
        $sql .= ' de.total_ht, de.total_tva, de.total_ttc,';
965
        $sql .= ' de.total_localtax1, de.total_localtax2, de.rule_warning_message,';
966
        $sql .= ' ctf.code as code_type_fees, ctf.label as label_type_fees, ctf.accountancy_code as accountancy_code_type_fees,';
967
        $sql .= ' p.ref as ref_projet, p.title as title_projet';
968
        $sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element_line . ' as de';
969
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'c_type_fees as ctf ON de.fk_c_type_fees = ctf.id';
970
        $sql .= ' LEFT JOIN ' . MAIN_DB_PREFIX . 'projet as p ON de.fk_projet = p.rowid';
971
        $sql .= " WHERE de." . $this->fk_element . " = " . ((int) $this->id);
972
        if (getDolGlobalString('EXPENSEREPORT_LINES_SORTED_BY_ROWID')) {
973
            $sql .= ' ORDER BY de.rang ASC, de.rowid ASC';
974
        } else {
975
            $sql .= ' ORDER BY de.rang ASC, de.date ASC';
976
        }
977
978
        $resql = $this->db->query($sql);
979
        if ($resql) {
980
            $num = $this->db->num_rows($resql);
981
            $i = 0;
982
            while ($i < $num) {
983
                $objp = $this->db->fetch_object($resql);
984
985
                $deplig = new ExpenseReportLine($this->db);
986
987
                $deplig->rowid = $objp->rowid;
988
                $deplig->id = $objp->rowid;
989
                $deplig->comments = $objp->comments;
990
                $deplig->qty = $objp->qty;
991
                $deplig->value_unit = $objp->value_unit;
992
                $deplig->date = $objp->date;
993
                $deplig->dates = $this->db->jdate($objp->date);
994
995
                $deplig->fk_expensereport = $objp->fk_expensereport;
996
                $deplig->fk_c_type_fees = $objp->fk_c_type_fees;
997
                $deplig->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
998
                $deplig->fk_projet = $objp->fk_project; // deprecated
999
                $deplig->fk_project = $objp->fk_project;
1000
                $deplig->fk_ecm_files = $objp->fk_ecm_files;
1001
1002
                $deplig->total_ht = $objp->total_ht;
1003
                $deplig->total_tva = $objp->total_tva;
1004
                $deplig->total_ttc = $objp->total_ttc;
1005
                $deplig->total_localtax1 = $objp->total_localtax1;
1006
                $deplig->total_localtax2 = $objp->total_localtax2;
1007
1008
                $deplig->type_fees_code = empty($objp->code_type_fees) ? 'TF_OTHER' : $objp->code_type_fees;
1009
                $deplig->type_fees_libelle = $objp->label_type_fees;
1010
                $deplig->type_fees_accountancy_code = $objp->accountancy_code_type_fees;
1011
1012
                $deplig->tva_tx = $objp->tva_tx;
1013
                $deplig->vatrate = $objp->tva_tx;
1014
                $deplig->vat_src_code = $objp->vat_src_code;
1015
                $deplig->localtax1_tx = $objp->localtax1_tx;
1016
                $deplig->localtax2_tx = $objp->localtax2_tx;
1017
                $deplig->localtax1_type = $objp->localtax1_type;
1018
                $deplig->localtax2_type = $objp->localtax2_type;
1019
1020
                $deplig->projet_ref = $objp->ref_projet;
1021
                $deplig->projet_title = $objp->title_projet;
1022
1023
                $deplig->rule_warning_message = $objp->rule_warning_message;
1024
1025
                $deplig->rang = $objp->rang;
1026
1027
                $this->lines[$i] = $deplig;
1028
1029
                $i++;
1030
            }
1031
            $this->db->free($resql);
1032
            return 1;
1033
        } else {
1034
            $this->error = $this->db->lasterror();
1035
            dol_syslog(get_class($this) . "::fetch_lines: Error " . $this->error, LOG_ERR);
1036
            return -3;
1037
        }
1038
    }
1039
1040
    /**
1041
     *  Return clicable name (with picto eventually)
1042
     *
1043
     * @param int    $withpicto             0=No picto, 1=Include picto into link, 2=Only picto
1044
     * @param string $option                Where point the link ('', 'document', ..)
1045
     * @param int    $max                   Max length of shown ref
1046
     * @param int    $short                 1=Return just URL
1047
     * @param string $moretitle             Add more text to title tooltip
1048
     * @param int    $notooltip             1=Disable tooltip
1049
     * @param int    $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save
1050
     *                                      lastsearch_values whenclicking
1051
     *
1052
     * @return     string                              String with URL
1053
     */
1054
    public function getNomUrl($withpicto = 0, $option = '', $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1)
1055
    {
1056
        global $langs, $hookmanager;
1057
1058
        $result = '';
1059
1060
        $url = DOL_URL_ROOT . '/expensereport/card.php?id=' . $this->id;
1061
1062
        if ($short) {
1063
            return $url;
1064
        }
1065
1066
        $params = [
1067
            'id' => $this->id,
1068
            'objecttype' => $this->element,
1069
            'option' => $option,
1070
            'moretitle' => $moretitle,
1071
            'nofetch' => 1,
1072
        ];
1073
        $classfortooltip = 'classfortooltip';
1074
        $dataparams = '';
1075
        if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1076
            $classfortooltip = 'classforajaxtooltip';
1077
            $dataparams = ' data-params="' . dol_escape_htmltag(json_encode($params)) . '"';
1078
            $label = '';
1079
        } else {
1080
            $label = implode($this->getTooltipContentArray($params));
1081
        }
1082
1083
        if ($option != 'nolink') {
1084
            // Add param to save lastsearch_values or not
1085
            $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1086
            if ($save_lastsearch_value == -1 && isset($_SERVER['PHP_SELF']) && preg_match('/list\.php/', $_SERVER['PHP_SELF'])) {
1087
                $add_save_lastsearch_values = 1;
1088
            }
1089
            if ($add_save_lastsearch_values) {
1090
                $url .= '&save_lastsearch_values=1';
1091
            }
1092
        }
1093
1094
        $ref = $this->ref;
1095
        if (empty($ref)) {
1096
            $ref = $this->id;
1097
        }
1098
1099
        $linkclose = '';
1100
        if (empty($notooltip)) {
1101
            if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1102
                $label = $langs->trans("ShowExpenseReport");
1103
                $linkclose .= ' alt="' . dol_escape_htmltag($label, 1) . '"';
1104
            }
1105
            $linkclose .= ($label ? ' title="' . dol_escape_htmltag($label, 1) . '"' : ' title="tocomplete"');
1106
            $linkclose .= $dataparams . ' class="' . $classfortooltip . '"';
1107
        }
1108
1109
        $linkstart = '<a href="' . $url . '"';
1110
        $linkstart .= $linkclose . '>';
1111
        $linkend = '</a>';
1112
1113
        $result .= $linkstart;
1114
        if ($withpicto) {
1115
            $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="' . (($withpicto != 2) ? 'paddingright ' : '') . '"'), 0, 0, $notooltip ? 0 : 1);
1116
        }
1117
        if ($withpicto != 2) {
1118
            $result .= ($max ? dol_trunc($ref, $max) : $ref);
1119
        }
1120
        $result .= $linkend;
1121
1122
        global $action;
1123
        $hookmanager->initHooks([$this->element . 'dao']);
1124
        $parameters = ['id' => $this->id, 'getnomurl' => &$result];
1125
        $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1126
        if ($reshook > 0) {
1127
            $result = $hookmanager->resPrint;
1128
        } else {
1129
            $result .= $hookmanager->resPrint;
1130
        }
1131
        return $result;
1132
    }
1133
1134
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1135
1136
    /**
1137
     * getTooltipContentArray
1138
     *
1139
     * @param array $params ex option, infologin
1140
     *
1141
     * @return array
1142
     * @since v18
1143
     */
1144
    public function getTooltipContentArray($params)
1145
    {
1146
        global $conf, $langs;
1147
1148
        $langs->load('expensereport');
1149
1150
        $nofetch = !empty($params['nofetch']);
1151
        $moretitle = $params['moretitle'] ?? '';
1152
1153
        $datas = [];
1154
        $datas['picto'] = img_picto('', $this->picto) . ' <u class="paddingrightonly">' . $langs->trans("ExpenseReport") . '</u>';
1155
        if (isset($this->status)) {
1156
            $datas['picto'] .= ' ' . $this->getLibStatut(5);
1157
        }
1158
        if ($moretitle) {
1159
            $datas['picto'] .= ' - ' . $moretitle;
1160
        }
1161
        if (!empty($this->ref)) {
1162
            $datas['ref'] = '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
1163
        }
1164
        if (!empty($this->total_ht)) {
1165
            $datas['total_ht'] = '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1166
        }
1167
        if (!empty($this->total_tva)) {
1168
            $datas['total_tva'] = '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1169
        }
1170
        if (!empty($this->total_ttc)) {
1171
            $datas['total_ttc'] = '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1172
        }
1173
1174
        return $datas;
1175
    }
1176
1177
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1178
1179
    /**
1180
     *  Returns the label status
1181
     *
1182
     * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short
1183
     *                  label + Picto
1184
     *
1185
     * @return     string              Label
1186
     */
1187
    public function getLibStatut($mode = 0)
1188
    {
1189
        return $this->LibStatut($this->status, $mode);
1190
    }
1191
1192
    /**
1193
     *  Returns the label of a status
1194
     *
1195
     * @param int $status ID status
1196
     * @param int $mode   0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short
1197
     *                    label + Picto, 6=Long label + Picto
1198
     *
1199
     * @return     string              Label
1200
     */
1201
    public function LibStatut($status, $mode = 0)
1202
    {
1203
        // phpcs:enable
1204
        global $langs;
1205
1206
        $labelStatus = $langs->transnoentitiesnoconv($this->labelStatus[$status]);
1207
        $labelStatusShort = $langs->transnoentitiesnoconv($this->labelStatusShort[$status]);
1208
1209
        $statuslogo = [0 => 'status0', 2 => 'status1', 4 => 'status6', 5 => 'status4', 6 => 'status6', 99 => 'status5'];
1210
1211
        $statusType = $statuslogo[$status];
1212
1213
        return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
1214
    }
1215
1216
    /**
1217
     * Delete object in database
1218
     *
1219
     * @param User|null $user      User that delete
1220
     * @param int       $notrigger 0=launch triggers after, 1=disable triggers
1221
     *
1222
     * @return  int                     Return integer <0 if KO, >0 if OK
1223
     */
1224
    public function delete(User $user = null, $notrigger = 0)
1225
    {
1226
        global $conf;
1227
        require_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
1228
1229
        $error = 0;
1230
1231
        $this->db->begin();
1232
1233
        if (!$notrigger) {
1234
            // Call trigger
1235
            $result = $this->call_trigger('EXPENSE_REPORT_DELETE', $user);
1236
            if ($result < 0) {
1237
                $error++;
1238
            }
1239
            // End call triggers
1240
        }
1241
1242
        // Delete extrafields of lines and lines
1243
        if (!$error && !empty($this->table_element_line)) {
1244
            $tabletodelete = $this->table_element_line;
1245
            //$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).")";
1246
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . $tabletodelete . " WHERE " . $this->fk_element . " = " . ((int) $this->id);
1247
            if (!$this->db->query($sql)) {
1248
                $error++;
1249
                $this->error = $this->db->lasterror();
1250
                $this->errors[] = $this->error;
1251
                dol_syslog(get_class($this) . "::delete error " . $this->error, LOG_ERR);
1252
            }
1253
        }
1254
1255
        if (!$error) {
1256
            // Delete linked object
1257
            $res = $this->deleteObjectLinked();
1258
            if ($res < 0) {
1259
                $error++;
1260
            }
1261
        }
1262
1263
        if (!$error) {
1264
            // Delete linked contacts
1265
            $res = $this->delete_linked_contact();
1266
            if ($res < 0) {
1267
                $error++;
1268
            }
1269
        }
1270
1271
        // Removed extrafields of object
1272
        if (!$error) {
1273
            $result = $this->deleteExtraFields();
1274
            if ($result < 0) {
1275
                $error++;
1276
                dol_syslog(get_class($this) . "::delete error " . $this->error, LOG_ERR);
1277
            }
1278
        }
1279
1280
        // Delete main record
1281
        if (!$error) {
1282
            $sql = "DELETE FROM " . MAIN_DB_PREFIX . $this->table_element . " WHERE rowid = " . ((int) $this->id);
1283
            $res = $this->db->query($sql);
1284
            if (!$res) {
1285
                $error++;
1286
                $this->error = $this->db->lasterror();
1287
                $this->errors[] = $this->error;
1288
                dol_syslog(get_class($this) . "::delete error " . $this->error, LOG_ERR);
1289
            }
1290
        }
1291
1292
        // Delete record into ECM index and physically
1293
        if (!$error) {
1294
            $res = $this->deleteEcmFiles(0); // Deleting files physically is done later with the dol_delete_dir_recursive
1295
            $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
1296
            if (!$res) {
1297
                $error++;
1298
            }
1299
        }
1300
1301
        if (!$error) {
1302
            // We remove directory
1303
            $ref = dol_sanitizeFileName($this->ref);
1304
            if ($conf->expensereport->multidir_output[$this->entity] && !empty($this->ref)) {
1305
                $dir = $conf->expensereport->multidir_output[$this->entity] . "/" . $ref;
1306
                $file = $dir . "/" . $ref . ".pdf";
1307
                if (file_exists($file)) {
1308
                    dol_delete_preview($this);
1309
1310
                    if (!dol_delete_file($file, 0, 0, 0, $this)) {
1311
                        $this->error = 'ErrorFailToDeleteFile';
1312
                        $this->errors[] = $this->error;
1313
                        $this->db->rollback();
1314
                        return 0;
1315
                    }
1316
                }
1317
                if (file_exists($dir)) {
1318
                    $res = @dol_delete_dir_recursive($dir);
1319
                    if (!$res) {
1320
                        $this->error = 'ErrorFailToDeleteDir';
1321
                        $this->errors[] = $this->error;
1322
                        $this->db->rollback();
1323
                        return 0;
1324
                    }
1325
                }
1326
            }
1327
        }
1328
1329
        if (!$error) {
1330
            dol_syslog(get_class($this) . "::delete " . $this->id . " by " . $user->id, LOG_DEBUG);
1331
            $this->db->commit();
1332
            return 1;
1333
        } else {
1334
            $this->db->rollback();
1335
            return -1;
1336
        }
1337
    }
1338
1339
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1340
1341
    /**
1342
     * Set to status validate
1343
     *
1344
     * @param User $fuser     User
1345
     * @param int  $notrigger Disable triggers
1346
     *
1347
     * @return  int                 Return integer <0 if KO, 0 if nothing done, >0 if OK
1348
     */
1349
    public function setValidate($fuser, $notrigger = 0)
1350
    {
1351
        global $conf, $langs, $user;
1352
1353
        $error = 0;
1354
        $now = dol_now();
1355
1356
        // Protection
1357
        if ($this->status == self::STATUS_VALIDATED) {
1358
            dol_syslog(get_class($this) . "::valid action abandoned: already validated", LOG_WARNING);
1359
            return 0;
1360
        }
1361
1362
        $this->date_valid = $now; // Required for the getNextNum later.
1363
1364
        // Define new ref
1365
        if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
1366
            $num = $this->getNextNumRef();
1367
        } else {
1368
            $num = $this->ref;
1369
        }
1370
        if (empty($num) || $num < 0) {
1371
            return -1;
1372
        }
1373
1374
        $this->newref = dol_sanitizeFileName($num);
1375
1376
        $this->db->begin();
1377
1378
        // Validate
1379
        $sql = "UPDATE " . MAIN_DB_PREFIX . $this->table_element;
1380
        $sql .= " SET ref = '" . $this->db->escape($num) . "',";
1381
        $sql .= " fk_statut = " . self::STATUS_VALIDATED . ",";
1382
        $sql .= " date_valid = '" . $this->db->idate($this->date_valid) . "',";
1383
        $sql .= " fk_user_valid = " . ((int) $user->id);
1384
        $sql .= " WHERE rowid = " . ((int) $this->id);
1385
1386
        $resql = $this->db->query($sql);
1387
        if ($resql) {
1388
            if (!$error && !$notrigger) {
1389
                // Call trigger
1390
                $result = $this->call_trigger('EXPENSE_REPORT_VALIDATE', $fuser);
1391
                if ($result < 0) {
1392
                    $error++;
1393
                }
1394
                // End call triggers
1395
            }
1396
1397
            if (!$error) {
1398
                $this->oldref = $this->ref;
1399
1400
                // Rename directory if dir was a temporary ref
1401
                if (preg_match('/^[\(]?PROV/i', $this->ref)) {
1402
                    require_once BASE_PATH . '/../Dolibarr/Lib/Files.php';
1403
1404
                    // Now we rename also files into index
1405
                    $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) . "'";
1406
                    $sql .= " WHERE filename LIKE '" . $this->db->escape($this->ref) . "%' AND filepath = 'expensereport/" . $this->db->escape($this->ref) . "' AND entity = " . ((int) $this->entity);
1407
                    $resql = $this->db->query($sql);
1408
                    if (!$resql) {
1409
                        $error++;
1410
                        $this->error = $this->db->lasterror();
1411
                    }
1412
                    $sql = 'UPDATE ' . MAIN_DB_PREFIX . "ecm_files set filepath = 'expensereport/" . $this->db->escape($this->newref) . "'";
1413
                    $sql .= " WHERE filepath = 'expensereport/" . $this->db->escape($this->ref) . "' and entity = " . $conf->entity;
1414
                    $resql = $this->db->query($sql);
1415
                    if (!$resql) {
1416
                        $error++;
1417
                        $this->error = $this->db->lasterror();
1418
                    }
1419
1420
                    // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1421
                    $oldref = dol_sanitizeFileName($this->ref);
1422
                    $newref = dol_sanitizeFileName($num);
1423
                    $dirsource = $conf->expensereport->multidir_output[$this->entity] . '/' . $oldref;
1424
                    $dirdest = $conf->expensereport->multidir_output[$this->entity] . '/' . $newref;
1425
                    if (!$error && file_exists($dirsource)) {
1426
                        dol_syslog(get_class($this) . "::setValidate() rename dir " . $dirsource . " into " . $dirdest);
1427
1428
                        if (@rename($dirsource, $dirdest)) {
1429
                            dol_syslog("Rename ok");
1430
                            // Rename docs starting with $oldref with $newref
1431
                            $listoffiles = dol_dir_list($dirdest, 'files', 1, '^' . preg_quote($oldref, '/'));
1432
                            foreach ($listoffiles as $fileentry) {
1433
                                $dirsource = $fileentry['name'];
1434
                                $dirdest = preg_replace('/^' . preg_quote($oldref, '/') . '/', $newref, $dirsource);
1435
                                $dirsource = $fileentry['path'] . '/' . $dirsource;
1436
                                $dirdest = $fileentry['path'] . '/' . $dirdest;
1437
                                @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

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