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

ExpenseReport::getNomUrl()   F

Complexity

Conditions 23
Paths 2161

Size

Total Lines 78
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 23
eloc 53
nc 2161
nop 7
dl 0
loc 78
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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

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

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

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

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