Passed
Branch develop (a7390e)
by
unknown
25:38
created

ExpenseReport::updateline()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 104
Code Lines 68

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 68
c 0
b 0
f 0
nc 17
nop 11
dl 0
loc 104
rs 7.7648

How to fix   Long Method    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
/* Copyright (C) 2011 		Dimitri Mouillard   	<[email protected]>
3
 * Copyright (C) 2015 		Laurent Destailleur 	<[email protected]>
4
 * Copyright (C) 2015 		Alexandre Spangaro  	<[email protected]>
5
 * Copyright (C) 2018       Nicolas ZABOURI         <[email protected]>
6
 * Copyright (c) 2018       Frédéric France         <[email protected]>
7
 * Copyright (C) 2016-2018 	Ferran Marcet       	<[email protected]>
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
/**
24
 *       \file       htdocs/expensereport/class/expensereport.class.php
25
 *       \ingroup    expensereport
26
 *       \brief      File to manage Expense Reports
27
 */
28
require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php';
29
require_once DOL_DOCUMENT_ROOT .'/expensereport/class/expensereport_ik.class.php';
30
require_once DOL_DOCUMENT_ROOT .'/expensereport/class/expensereport_rule.class.php';
31
32
/**
33
 * Class to manage Trips and Expenses
34
 */
35
class ExpenseReport extends CommonObject
36
{
37
    /**
38
	 * @var string ID to identify managed object
39
	 */
40
	public $element='expensereport';
41
42
    /**
43
	 * @var string Name of table without prefix where object is stored
44
	 */
45
	public $table_element='expensereport';
46
47
    public $table_element_line = 'expensereport_det';
48
    public $fk_element = 'fk_expensereport';
49
    public $picto = 'trip';
50
51
    public $lines=array();
52
53
    public $date_debut;
54
55
    public $date_fin;
56
57
    public $status;
58
    public $fk_statut;     // -- 0=draft, 2=validated (attente approb), 4=canceled, 5=approved, 6=payed, 99=denied
59
    public $fk_c_paiement;
60
    public $paid;
61
62
    public $user_author_infos;
63
    public $user_validator_infos;
64
65
    public $fk_typepayment;
66
	public $num_payment;
67
    public $code_paiement;
68
    public $code_statut;
69
70
    // ACTIONS
71
72
    // Create
73
    public $date_create;
74
    public $fk_user_author;    // Note fk_user_author is not the 'author' but the guy the expense report is for.
75
76
    // Update
77
	public $date_modif;
78
    public $fk_user_modif;
79
80
    // Refus
81
    public $date_refuse;
82
    public $detail_refuse;
83
    public $fk_user_refuse;
84
85
    // Annulation
86
    public $date_cancel;
87
    public $detail_cancel;
88
    public $fk_user_cancel;
89
90
    public $fk_user_validator;	// User that is defined to approve
91
92
    // Validation
93
    public $date_valid;		// User making validation
94
    public $fk_user_valid;
95
    public $user_valid_infos;
96
97
    // Approve
98
    public $date_approve;
99
    public $fk_user_approve;	// User that has approved
100
101
    // Paiement
102
    public $user_paid_infos;
103
104
    /*
105
        END ACTIONS
106
    */
107
108
    /**
109
	 * Draft status
110
	 */
111
	const STATUS_DRAFT = 0;
112
113
	/**
114
	 * Validated (need to be paid)
115
	 */
116
	const STATUS_VALIDATED = 2;
117
118
	/**
119
	 * Classified canceled
120
	 */
121
	const STATUS_CANCELED = 4;
122
123
	/**
124
	 * Classified approved
125
	 */
126
	const STATUS_APPROVED = 5;
127
128
	/**
129
	 * Classified refused
130
	 */
131
	const STATUS_REFUSED = 99;
132
133
	/**
134
	 * Classified paid.
135
	 */
136
	const STATUS_CLOSED = 6;
137
138
139
140
	/**
141
     *  Constructor
142
     *
143
     *  @param  DoliDB  $db     Handler acces base de donnees
144
     */
145
    public function __construct($db)
146
    {
147
        $this->db = $db;
148
        $this->total_ht = 0;
149
        $this->total_ttc = 0;
150
        $this->total_tva = 0;
151
        $this->modepaymentid = 0;
152
153
        // List of language codes for status
154
        $this->statuts_short = array(0 => 'Draft', 2 => 'Validated', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
155
        $this->statuts = array(0 => 'Draft', 2 => 'ValidatedWaitingApproval', 4 => 'Canceled', 5 => 'Approved', 6 => 'Paid', 99 => 'Refused');
156
        $this->statuts_logo = array(0 => 'statut0', 2 => 'statut1', 4 => 'statut5', 5 => 'statut3', 6 => 'statut6', 99 => 'statut5');
157
    }
158
159
    /**
160
     * Create object in database
161
     *
162
     * @param   User    $user   User that create
163
     * @param   int     $notrigger   Disable triggers
164
     * @return  int             <0 if KO, >0 if OK
165
     */
166
    public function create($user, $notrigger = 0)
167
    {
168
        global $conf, $langs;
169
170
        $now = dol_now();
171
172
        $error = 0;
173
174
        // Check parameters
175
        if (empty($this->date_debut) || empty($this->date_fin))
176
        {
177
            $this->error=$langs->trans('ErrorFieldRequired', $langs->transnoentitiesnoconv('Date'));
178
            return -1;
179
        }
180
181
        $fuserid = $this->fk_user_author;       // Note fk_user_author is not the 'author' but the guy the expense report is for.
182
        if (empty($fuserid)) $fuserid = $user->id;
183
184
        $this->db->begin();
185
186
        $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (";
187
        $sql.= "ref";
188
        $sql.= ",total_ht";
189
        $sql.= ",total_ttc";
190
        $sql.= ",total_tva";
191
        $sql.= ",date_debut";
192
        $sql.= ",date_fin";
193
        $sql.= ",date_create";
194
        $sql.= ",fk_user_author";
195
        $sql.= ",fk_user_validator";
196
        $sql.= ",fk_user_approve";
197
        $sql.= ",fk_user_modif";
198
        $sql.= ",fk_statut";
199
        $sql.= ",fk_c_paiement";
200
        $sql.= ",paid";
201
        $sql.= ",note_public";
202
        $sql.= ",note_private";
203
        $sql.= ",entity";
204
        $sql.= ") VALUES(";
205
        $sql.= "'(PROV)'";
206
        $sql.= ", ".$this->total_ht;
207
        $sql.= ", ".$this->total_ttc;
208
        $sql.= ", ".$this->total_tva;
209
        $sql.= ", '".$this->db->idate($this->date_debut)."'";
210
        $sql.= ", '".$this->db->idate($this->date_fin)."'";
211
        $sql.= ", '".$this->db->idate($now)."'";
212
        $sql.= ", ".$fuserid;
213
        $sql.= ", ".($this->fk_user_validator > 0 ? $this->fk_user_validator:"null");
214
        $sql.= ", ".($this->fk_user_approve > 0 ? $this->fk_user_approve:"null");
215
        $sql.= ", ".($this->fk_user_modif > 0 ? $this->fk_user_modif:"null");
216
        $sql.= ", ".($this->fk_statut > 1 ? $this->fk_statut:0);
217
        $sql.= ", ".($this->modepaymentid?$this->modepaymentid:"null");
218
        $sql.= ", 0";
219
        $sql.= ", ".($this->note_public?"'".$this->db->escape($this->note_public)."'":"null");
220
        $sql.= ", ".($this->note_private?"'".$this->db->escape($this->note_private)."'":"null");
221
        $sql.= ", ".$conf->entity;
222
        $sql.= ")";
223
224
        $result = $this->db->query($sql);
225
        if ($result)
226
        {
227
            $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
228
            $this->ref='(PROV'.$this->id.')';
229
230
            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET ref='".$this->db->escape($this->ref)."' WHERE rowid=".$this->id;
231
            $resql=$this->db->query($sql);
232
            if (!$resql)
233
            {
234
                $this->error = $this->db->lasterror();
235
                $error++;
236
            }
237
238
            if (! $error)
239
            {
240
                if (is_array($this->lines) && count($this->lines)>0)
241
                {
242
    	            foreach ($this->lines as $i => $val)
243
    	            {
244
    	                //$newndfline=new ExpenseReportLine($this->db);
245
    	                $newndfline=$this->lines[$i];
246
    	                $newndfline->fk_expensereport=$this->id;
247
    	                $result=$newndfline->insert();
248
        	            if ($result < 0)
249
        	            {
250
        	                $this->error = $newndfline->error;
251
        	                $error++;
252
        	                break;
253
        	            }
254
    	            }
255
                }
256
            }
257
258
            if (! $error)
259
            {
260
            	$result=$this->insertExtraFields();
261
           		if ($result < 0) $error++;
262
            }
263
264
            if (! $error)
265
            {
266
                $result=$this->update_price();
267
                if ($result > 0)
268
                {
269
270
					if (!$notrigger)
271
					{
272
						// Call trigger
273
						$result=$this->call_trigger('EXPENSE_REPORT_CREATE', $user);
274
275
						if ($result < 0) {
276
							$error++;
277
						}
278
						// End call triggers
279
					}
280
281
					if (empty($error))
282
					{
283
						$this->db->commit();
284
						return $this->id;
285
					}
286
					else
287
					{
288
						$this->db->rollback();
289
						return -4;
290
					}
291
                }
292
                else
293
                {
294
                    $this->db->rollback();
295
                    return -3;
296
                }
297
            }
298
            else
299
            {
300
                dol_syslog(get_class($this)."::create error ".$this->error, LOG_ERR);
301
                $this->db->rollback();
302
                return -2;
303
            }
304
        }
305
        else
306
        {
307
            $this->error=$this->db->lasterror()." sql=".$sql;
308
            $this->db->rollback();
309
            return -1;
310
        }
311
    }
312
313
314
    /**
315
     *	Load an object from its id and create a new one in database
316
     *
317
	 *  @param	    User	$user		        User making the clone
318
     *	@param		int     $fk_user_author		Id of new user
319
     *	@return		int							New id of clone
320
     */
321
    public function createFromClone(User $user, $fk_user_author)
322
    {
323
        global $hookmanager;
324
325
        $error=0;
326
327
        if (empty($fk_user_author)) $fk_user_author = $user->id;
328
329
        $this->db->begin();
330
331
        // get extrafields so they will be clone
332
        //foreach($this->lines as $line)
333
            //$line->fetch_optionals($line->rowid);
334
335
        // Load source object
336
        $objFrom = clone $this;
337
338
        $this->id=0;
339
        $this->ref = '';
340
        $this->status=0;
341
        $this->fk_statut=0;
342
343
        // Clear fields
344
        $this->fk_user_author     = $fk_user_author;     // Note fk_user_author is not the 'author' but the guy the expense report is for.
345
        $this->fk_user_valid      = '';
346
        $this->date_create  	  = '';
347
        $this->date_creation      = '';
348
        $this->date_validation    = '';
349
350
        // Create clone
351
        $this->context['createfromclone'] = 'createfromclone';
352
        $result=$this->create($user);
353
        if ($result < 0) $error++;
354
355
        if (! $error)
356
        {
357
            // Hook of thirdparty module
358
            if (is_object($hookmanager))
359
            {
360
                $parameters=array('objFrom'=>$objFrom);
361
                $action='';
362
                $reshook=$hookmanager->executeHooks('createFrom', $parameters, $this, $action);    // Note that $action and $object may have been modified by some hooks
363
                if ($reshook < 0) $error++;
364
            }
365
        }
366
367
        unset($this->context['createfromclone']);
368
369
        // End
370
        if (! $error)
371
        {
372
            $this->db->commit();
373
            return $this->id;
374
        }
375
        else
376
        {
377
            $this->db->rollback();
378
            return -1;
379
        }
380
    }
381
382
383
    /**
384
     * update
385
     *
386
     * @param   User    $user                   User making change
387
	 * @param   int     $notrigger              Disable triggers
388
     * @param   User    $userofexpensereport    New user we want to have the expense report on.
389
     * @return  int                             <0 if KO, >0 if OK
390
     */
391
    public function update($user, $notrigger = 0, $userofexpensereport = null)
392
    {
393
        global $langs;
394
395
		$error = 0;
396
		$this->db->begin();
397
398
        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
399
        $sql.= " total_ht = ".$this->total_ht;
400
        $sql.= " , total_ttc = ".$this->total_ttc;
401
        $sql.= " , total_tva = ".$this->total_tva;
402
        $sql.= " , date_debut = '".$this->db->idate($this->date_debut)."'";
403
        $sql.= " , date_fin = '".$this->db->idate($this->date_fin)."'";
404
        if ($userofexpensereport && is_object($userofexpensereport))
405
        {
406
            $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.
407
        }
408
        $sql.= " , fk_user_validator = ".($this->fk_user_validator > 0 ? $this->fk_user_validator:"null");
409
        $sql.= " , fk_user_valid = ".($this->fk_user_valid > 0 ? $this->fk_user_valid:"null");
410
        $sql.= " , fk_user_approve = ".($this->fk_user_approve > 0 ? $this->fk_user_approve:"null");
411
        $sql.= " , fk_user_modif = ".$user->id;
412
        $sql.= " , fk_statut = ".($this->fk_statut >= 0 ? $this->fk_statut:'0');
413
        $sql.= " , fk_c_paiement = ".($this->fk_c_paiement > 0 ? $this->fk_c_paiement:"null");
414
        $sql.= " , note_public = ".(!empty($this->note_public)?"'".$this->db->escape($this->note_public)."'":"''");
415
        $sql.= " , note_private = ".(!empty($this->note_private)?"'".$this->db->escape($this->note_private)."'":"''");
416
        $sql.= " , detail_refuse = ".(!empty($this->detail_refuse)?"'".$this->db->escape($this->detail_refuse)."'":"''");
417
        $sql.= " WHERE rowid = ".$this->id;
418
419
        dol_syslog(get_class($this)."::update sql=".$sql, LOG_DEBUG);
420
        $result = $this->db->query($sql);
421
        if ($result)
422
        {
423
            if (!$notrigger)
424
			{
425
				// Call trigger
426
				$result=$this->call_trigger('EXPENSE_REPORT_UPDATE', $user);
427
428
				if ($result < 0) {
429
					$error++;
430
				}
431
				// End call triggers
432
			}
433
434
			if (empty($error))
435
			{
436
				$this->db->commit();
437
				return 1;
438
			}
439
			else
440
			{
441
				$this->db->rollback();
442
				$this->error=$this->db->error();
443
				return -2;
444
			}
445
        }
446
        else
447
        {
448
			$this->db->rollback();
449
            $this->error=$this->db->error();
450
            return -1;
451
        }
452
    }
453
454
    /**
455
     *  Load an object from database
456
     *
457
     *  @param  int     $id     Id                      {@min 1}
458
     *  @param  string  $ref    Ref                     {@name ref}
459
     *  @return int             <0 if KO, >0 if OK
460
     */
461
    public function fetch($id, $ref = '')
462
    {
463
        global $conf;
464
465
        $sql = "SELECT d.rowid, d.ref, d.note_public, d.note_private,";                                 // DEFAULT
466
        $sql.= " d.detail_refuse, d.detail_cancel, d.fk_user_refuse, d.fk_user_cancel,";                // ACTIONS
467
        $sql.= " d.date_refuse, d.date_cancel,";                                                        // ACTIONS
468
        $sql.= " d.total_ht, d.total_ttc, d.total_tva,";                                                // TOTAUX (int)
469
        $sql.= " d.date_debut, d.date_fin, d.date_create, d.tms as date_modif, d.date_valid, d.date_approve,";	// DATES (datetime)
470
        $sql.= " d.fk_user_author, d.fk_user_modif, d.fk_user_validator,";
471
        $sql.= " d.fk_user_valid, d.fk_user_approve,";
472
        $sql.= " d.fk_statut as status, d.fk_c_paiement, d.paid,";
473
        $sql.= " dp.libelle as libelle_paiement, dp.code as code_paiement";                             // INNER JOIN paiement
474
        $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element." as d";
475
        $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as dp ON d.fk_c_paiement = dp.id";
476
        if ($ref) $sql.= " WHERE d.ref = '".$this->db->escape($ref)."'";
477
        else $sql.= " WHERE d.rowid = ".$id;
478
        //$sql.= $restrict;
479
480
        dol_syslog(get_class($this)."::fetch sql=".$sql, LOG_DEBUG);
481
        $resql = $this->db->query($sql) ;
482
        if ($resql)
483
        {
484
            $obj = $this->db->fetch_object($resql);
485
            if ($obj)
486
            {
487
                $this->id           = $obj->rowid;
488
                $this->ref          = $obj->ref;
489
                $this->total_ht     = $obj->total_ht;
490
                $this->total_tva    = $obj->total_tva;
491
                $this->total_ttc    = $obj->total_ttc;
492
                $this->note_public  = $obj->note_public;
493
                $this->note_private = $obj->note_private;
494
                $this->detail_refuse = $obj->detail_refuse;
495
                $this->detail_cancel = $obj->detail_cancel;
496
497
                $this->date_debut       = $this->db->jdate($obj->date_debut);
498
                $this->date_fin         = $this->db->jdate($obj->date_fin);
499
                $this->date_valid       = $this->db->jdate($obj->date_valid);
500
                $this->date_approve     = $this->db->jdate($obj->date_approve);
501
                $this->date_create      = $this->db->jdate($obj->date_create);
502
                $this->date_modif       = $this->db->jdate($obj->date_modif);
503
                $this->date_refuse      = $this->db->jdate($obj->date_refuse);
504
                $this->date_cancel      = $this->db->jdate($obj->date_cancel);
505
506
                $this->fk_user_author           = $obj->fk_user_author;    // Note fk_user_author is not the 'author' but the guy the expense report is for.
507
                $this->fk_user_modif            = $obj->fk_user_modif;
508
                $this->fk_user_validator        = $obj->fk_user_validator;
509
                $this->fk_user_valid            = $obj->fk_user_valid;
510
                $this->fk_user_refuse           = $obj->fk_user_refuse;
511
                $this->fk_user_cancel           = $obj->fk_user_cancel;
512
                $this->fk_user_approve          = $obj->fk_user_approve;
513
514
                $user_author = new User($this->db);
515
                if ($this->fk_user_author > 0) $user_author->fetch($this->fk_user_author);
516
517
                $this->user_author_infos = dolGetFirstLastname($user_author->firstname, $user_author->lastname);
518
519
                $user_approver = new User($this->db);
520
                if ($this->fk_user_approve > 0) $user_approver->fetch($this->fk_user_approve);
521
                elseif ($this->fk_user_validator > 0) $user_approver->fetch($this->fk_user_validator);		// For backward compatibility
522
                $this->user_validator_infos = dolGetFirstLastname($user_approver->firstname, $user_approver->lastname);
523
524
                $this->fk_statut                = $obj->status;
525
                $this->status                   = $obj->status;
526
                $this->fk_c_paiement            = $obj->fk_c_paiement;
527
                $this->paid                     = $obj->paid;
528
529
                if ($this->fk_statut==5 || $this->fk_statut==6)
530
                {
531
                    $user_valid = new User($this->db);
532
                    if ($this->fk_user_valid > 0) $user_valid->fetch($this->fk_user_valid);
533
                    $this->user_valid_infos = dolGetFirstLastname($user_valid->firstname, $user_valid->lastname);
534
                }
535
536
                $this->libelle_statut   = $obj->libelle_statut;
537
                $this->libelle_paiement = $obj->libelle_paiement;
538
                $this->code_statut      = $obj->code_statut;
539
                $this->code_paiement    = $obj->code_paiement;
540
541
                $this->lines = array();
542
543
                $result=$this->fetch_lines();
544
545
                return $result;
546
            }
547
            else
548
            {
549
                return 0;
550
            }
551
        }
552
        else
553
        {
554
            $this->error=$this->db->lasterror();
555
            return -1;
556
        }
557
    }
558
559
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
560
    /**
561
     *    Classify the expense report as paid
562
     *
563
     *    @param    int     $id                 Id of expense report
564
     *    @param    user    $fuser              User making change
565
	 *    @param    int     $notrigger          Disable triggers
566
     *    @return   int                         <0 if KO, >0 if OK
567
     */
568
    public function set_paid($id, $fuser, $notrigger = 0)
569
    {
570
        // phpcs:enable
571
		$error = 0;
572
		$this->db->begin();
573
574
        $sql = "UPDATE ".MAIN_DB_PREFIX."expensereport";
575
        $sql.= " SET fk_statut = ".self::STATUS_CLOSED.", paid=1";
576
        $sql.= " WHERE rowid = ".$id." AND fk_statut = ".self::STATUS_APPROVED;
577
578
        dol_syslog(get_class($this)."::set_paid sql=".$sql, LOG_DEBUG);
579
        $resql=$this->db->query($sql);
580
        if ($resql)
581
        {
582
            if ($this->db->affected_rows($resql))
583
            {
584
				if (!$notrigger)
585
				{
586
					// Call trigger
587
					$result=$this->call_trigger('EXPENSE_REPORT_PAID', $fuser);
588
589
					if ($result < 0) {
590
						$error++;
591
					}
592
					// End call triggers
593
				}
594
595
				if (empty($error))
596
				{
597
					$this->db->commit();
598
					return 1;
599
				}
600
				else
601
				{
602
					$this->db->rollback();
603
					$this->error=$this->db->error();
604
					return -2;
605
				}
606
            }
607
            else
608
            {
609
				$this->db->commit();
610
                return 0;
611
            }
612
        }
613
        else
614
        {
615
			$this->db->rollback();
616
            dol_print_error($this->db);
617
            return -1;
618
        }
619
    }
620
621
    /**
622
     *  Returns the label status
623
     *
624
     *  @param      int     $mode       0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto
625
     *  @return     string              Label
626
     */
627
    public function getLibStatut($mode = 0)
628
    {
629
        return $this->LibStatut($this->status, $mode);
630
    }
631
632
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
633
    /**
634
     *  Returns the label of a statut
635
     *
636
     *  @param      int     $status     id statut
637
     *  @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
638
     *  @return     string              Label
639
     */
640
    public function LibStatut($status, $mode = 0)
641
    {
642
        // phpcs:enable
643
        global $langs;
644
645
        if ($mode == 0)
646
            return $langs->transnoentities($this->statuts[$status]);
647
648
        elseif ($mode == 1)
649
            return $langs->transnoentities($this->statuts_short[$status]);
650
651
        elseif ($mode == 2)
652
            return img_picto($langs->transnoentities($this->statuts_short[$status]), $this->statuts_logo[$status]).' '.$langs->transnoentities($this->statuts_short[$status]);
653
654
        elseif ($mode == 3)
655
            return img_picto($langs->transnoentities($this->statuts_short[$status]), $this->statuts_logo[$status]);
656
657
        elseif ($mode == 4)
658
            return img_picto($langs->transnoentities($this->statuts_short[$status]), $this->statuts_logo[$status]).' '.$langs->transnoentities($this->statuts[$status]);
659
660
        elseif ($mode == 5)
661
            return '<span class="hideonsmartphone">'.$langs->transnoentities($this->statuts_short[$status]).' </span>'.img_picto($langs->transnoentities($this->statuts_short[$status]), $this->statuts_logo[$status]);
662
663
        elseif ($mode == 6)
664
            return $langs->transnoentities($this->statuts[$status]).' '.img_picto($langs->transnoentities($this->statuts_short[$status]), $this->statuts_logo[$status]);
665
    }
666
667
668
    /**
669
     *  Load information on object
670
     *
671
     *  @param  int     $id      Id of object
672
     *  @return void
673
     */
674
    public function info($id)
675
    {
676
        global $conf;
677
678
        $sql = "SELECT f.rowid,";
679
        $sql.= " f.date_create as datec,";
680
        $sql.= " f.tms as date_modification,";
681
        $sql.= " f.date_valid as datev,";
682
        $sql.= " f.date_approve as datea,";
683
        //$sql.= " f.fk_user_author as fk_user_creation,";      // This is not user of creation but user the expense is for.
684
        $sql.= " f.fk_user_modif as fk_user_modification,";
685
        $sql.= " f.fk_user_valid,";
686
        $sql.= " f.fk_user_approve";
687
        $sql.= " FROM ".MAIN_DB_PREFIX."expensereport as f";
688
        $sql.= " WHERE f.rowid = ".$id;
689
        $sql.= " AND f.entity = ".$conf->entity;
690
691
        $resql = $this->db->query($sql);
692
        if ($resql)
693
        {
694
            if ($this->db->num_rows($resql))
695
            {
696
                $obj = $this->db->fetch_object($resql);
697
698
                $this->id = $obj->rowid;
699
700
                $this->date_creation = $this->db->jdate($obj->datec);
701
                $this->date_modification = $this->db->jdate($obj->date_modification);
702
                $this->date_validation = $this->db->jdate($obj->datev);
703
                $this->date_approbation = $this->db->jdate($obj->datea);
704
705
                $cuser = new User($this->db);
706
                $cuser->fetch($obj->fk_user_author);
707
                $this->user_creation = $cuser;
708
709
                if ($obj->fk_user_creation)
710
                {
711
                    $cuser = new User($this->db);
712
                    $cuser->fetch($obj->fk_user_creation);
713
                    $this->user_creation     = $cuser;
714
                }
715
                if ($obj->fk_user_valid)
716
                {
717
                    $vuser = new User($this->db);
718
                    $vuser->fetch($obj->fk_user_valid);
719
                    $this->user_validation     = $vuser;
720
                }
721
                if ($obj->fk_user_modification)
722
                {
723
                    $muser = new User($this->db);
724
                    $muser->fetch($obj->fk_user_modification);
725
                    $this->user_modification   = $muser;
726
                }
727
                if ($obj->fk_user_approve)
728
                {
729
                    $auser = new User($this->db);
730
                    $auser->fetch($obj->fk_user_approve);
731
                    $this->user_approve   = $auser;
732
                }
733
            }
734
            $this->db->free($resql);
735
        }
736
        else
737
        {
738
            dol_print_error($this->db);
739
        }
740
    }
741
742
743
744
    /**
745
     *  Initialise an instance with random values.
746
     *  Used to build previews or test instances.
747
     *  id must be 0 if object instance is a specimen.
748
     *
749
     *  @return void
750
     */
751
    public function initAsSpecimen()
752
    {
753
        global $user,$langs,$conf;
754
755
        $now=dol_now();
756
757
        // Initialise parametres
758
        $this->id=0;
759
        $this->ref = 'SPECIMEN';
760
        $this->specimen=1;
761
        $this->date_create = $now;
762
        $this->date_debut = $now;
763
        $this->date_fin = $now;
764
        $this->date_valid = $now;
765
        $this->date_approve = $now;
766
767
        $type_fees_id = 2;  // TF_TRIP
768
769
        $this->status = 5;
770
        $this->fk_statut = 5;
771
772
        $this->fk_user_author = $user->id;
773
        $this->fk_user_validator = $user->id;
774
        $this->fk_user_valid = $user->id;
775
        $this->fk_user_approve = $user->id;
776
777
        $this->note_private='Private note';
778
        $this->note_public='SPECIMEN';
779
        $nbp = 5;
780
        $xnbp = 0;
781
        while ($xnbp < $nbp) {
782
            $line=new ExpenseReportLine($this->db);
783
            $line->comments=$langs->trans("Comment")." ".$xnbp;
784
            $line->date=($now-3600*(1+$xnbp));
785
            $line->total_ht=100;
786
            $line->total_tva=20;
787
            $line->total_ttc=120;
788
            $line->qty=1;
789
            $line->vatrate=20;
790
            $line->value_unit=120;
791
            $line->fk_expensereport=0;
792
            $line->type_fees_code='TRA';
793
            $line->fk_c_type_fees=$type_fees_id;
794
795
            $line->projet_ref = 'ABC';
796
797
            $this->lines[$xnbp]=$line;
798
            $xnbp++;
799
800
            $this->total_ht+=$line->total_ht;
801
            $this->total_tva+=$line->total_tva;
802
            $this->total_ttc+=$line->total_ttc;
803
        }
804
    }
805
806
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
807
    /**
808
     * fetch_line_by_project
809
     *
810
     * @param   int     $projectid      Project id
811
     * @param   User    $user           User
812
     * @return  int                     <0 if KO, >0 if OK
813
     */
814
    public function fetch_line_by_project($projectid, $user = '')
815
    {
816
        // phpcs:enable
817
        global $conf,$db,$langs;
818
819
        $langs->load('trips');
820
821
        if ($user->rights->expensereport->lire) {
822
823
            $sql = "SELECT de.fk_expensereport, de.date, de.comments, de.total_ht, de.total_ttc";
824
            $sql.= " FROM ".MAIN_DB_PREFIX."expensereport_det as de";
825
            $sql.= " WHERE de.fk_projet = ".$projectid;
826
827
            dol_syslog(get_class($this)."::fetch sql=".$sql, LOG_DEBUG);
828
            $result = $db->query($sql) ;
829
            if ($result)
830
            {
831
                $num = $db->num_rows($result);
832
                $i = 0;
833
                $total_HT = 0;
834
                $total_TTC = 0;
835
836
                while ($i < $num)
837
                {
838
839
                    $objp = $db->fetch_object($result);
840
841
                    $sql2 = "SELECT d.rowid, d.fk_user_author, d.ref, d.fk_statut";
842
                    $sql2.= " FROM ".MAIN_DB_PREFIX."expensereport as d";
843
                    $sql2.= " WHERE d.rowid = '".$objp->fk_expensereport."'";
844
845
                    $result2 = $db->query($sql2);
846
                    $obj = $db->fetch_object($result2);
847
848
                    $objp->fk_user_author = $obj->fk_user_author;
849
                    $objp->ref = $obj->ref;
850
                    $objp->fk_c_expensereport_status = $obj->fk_statut;
851
                    $objp->rowid = $obj->rowid;
852
853
                    $total_HT = $total_HT + $objp->total_ht;
854
                    $total_TTC = $total_TTC + $objp->total_ttc;
855
                    $author = new User($db);
856
                    $author->fetch($objp->fk_user_author);
857
858
                    print '<tr>';
859
                    print '<td><a href="'.DOL_URL_ROOT.'/expensereport/card.php?id='.$objp->rowid.'">'.$objp->ref_num.'</a></td>';
860
                    print '<td class="center">'.dol_print_date($objp->date, 'day').'</td>';
861
                    print '<td>'.$author->getNomUrl(1).'</td>';
862
                    print '<td>'.$objp->comments.'</td>';
863
                    print '<td class="right">'.price($objp->total_ht).'</td>';
864
                    print '<td class="right">'.price($objp->total_ttc).'</td>';
865
                    print '<td class="right">';
866
867
                    switch($objp->fk_c_expensereport_status) {
868
                        case 4:
869
                            print img_picto($langs->trans('StatusOrderCanceled'), 'statut5');
870
                            break;
871
                        case 1:
872
                            print $langs->trans('Draft').' '.img_picto($langs->trans('Draft'), 'statut0');
873
                            break;
874
                        case 2:
875
                            print $langs->trans('TripForValid').' '.img_picto($langs->trans('TripForValid'), 'statut3');
876
                            break;
877
                        case 5:
878
                            print $langs->trans('TripForPaid').' '.img_picto($langs->trans('TripForPaid'), 'statut3');
879
                            break;
880
                        case 6:
881
                            print $langs->trans('TripPaid').' '.img_picto($langs->trans('TripPaid'), 'statut4');
882
                            break;
883
                    }
884
                    /*
885
                     if ($status==4) return img_picto($langs->trans('StatusOrderCanceled'),'statut5');
886
                    if ($status==1) return img_picto($langs->trans('StatusOrderDraft'),'statut0');
887
                    if ($status==2) return img_picto($langs->trans('StatusOrderValidated'),'statut1');
888
                    if ($status==2) return img_picto($langs->trans('StatusOrderOnProcess'),'statut3');
889
                    if ($status==5) return img_picto($langs->trans('StatusOrderToBill'),'statut4');
890
                    if ($status==6) return img_picto($langs->trans('StatusOrderOnProcess'),'statut6');
891
                    */
892
                    print '</td>';
893
                    print '</tr>';
894
895
                    $i++;
896
                }
897
898
                print '<tr class="liste_total"><td colspan="4">'.$langs->trans("Number").': '.$i.'</td>';
899
                print '<td class="right" width="100">'.$langs->trans("TotalHT").' : '.price($total_HT).'</td>';
900
                print '<td class="right" width="100">'.$langs->trans("TotalTTC").' : '.price($total_TTC).'</td>';
901
                print '<td>&nbsp;</td>';
902
                print '</tr>';
903
            }
904
            else
905
            {
906
                $this->error=$db->lasterror();
907
                return -1;
908
            }
909
        }
910
    }
911
912
    /**
913
     * recalculer
914
     * TODO Replace this with call to update_price if not already done
915
     *
916
     * @param   int         $id     Id of expense report
917
     * @return  int                 <0 if KO, >0 if OK
918
     */
919
    public function recalculer($id)
920
    {
921
        $sql = 'SELECT tt.total_ht, tt.total_ttc, tt.total_tva';
922
        $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as tt';
923
        $sql.= ' WHERE tt.'.$this->fk_element.' = '.$id;
924
925
        $total_ht = 0; $total_tva = 0; $total_ttc = 0;
926
927
        $result = $this->db->query($sql);
928
        if($result)
929
        {
930
            $num = $this->db->num_rows($result);
931
            $i = 0;
932
            while ($i < $num):
933
            $objp = $this->db->fetch_object($result);
934
            $total_ht+=$objp->total_ht;
935
            $total_tva+=$objp->total_tva;
936
            $i++;
937
            endwhile;
938
939
            $total_ttc = $total_ht + $total_tva;
940
            $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
941
            $sql.= " total_ht = ".$total_ht;
942
            $sql.= " , total_ttc = ".$total_ttc;
943
            $sql.= " , total_tva = ".$total_tva;
944
            $sql.= " WHERE rowid = ".$id;
945
            $result = $this->db->query($sql);
946
            if($result):
947
            $this->db->free($result);
948
            return 1;
949
            else:
950
            $this->error=$this->db->lasterror();
951
            dol_syslog(get_class($this)."::recalculer: Error ".$this->error, LOG_ERR);
952
            return -3;
953
            endif;
954
        }
955
        else
956
        {
957
            $this->error=$this->db->lasterror();
958
            dol_syslog(get_class($this)."::recalculer: Error ".$this->error, LOG_ERR);
959
            return -3;
960
        }
961
    }
962
963
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
964
    /**
965
     * fetch_lines
966
     *
967
     * @return  int     <0 if OK, >0 if KO
968
     */
969
    public function fetch_lines()
970
    {
971
        // phpcs:enable
972
        $this->lines=array();
973
974
        $sql = ' SELECT de.rowid, de.comments, de.qty, de.value_unit, de.date, de.rang,';
975
        $sql.= ' de.'.$this->fk_element.', de.fk_c_type_fees, de.fk_c_exp_tax_cat, de.fk_projet as fk_project, de.tva_tx, de.fk_ecm_files,';
976
        $sql.= ' de.total_ht, de.total_tva, de.total_ttc,';
977
        $sql.= ' ctf.code as code_type_fees, ctf.label as libelle_type_fees,';
978
        $sql.= ' p.ref as ref_projet, p.title as title_projet';
979
        $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line.' as de';
980
        $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_type_fees as ctf ON de.fk_c_type_fees = ctf.id';
981
        $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet as p ON de.fk_projet = p.rowid';
982
        $sql.= ' WHERE de.'.$this->fk_element.' = '.$this->id;
983
        if (! empty($conf->global->EXPENSEREPORT_LINES_SORTED_BY_ROWID))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $conf seems to be never defined.
Loading history...
984
        {
985
        	$sql.= ' ORDER BY de.rang ASC, de.rowid ASC';
986
        }
987
        else
988
        {
989
        	$sql.= ' ORDER BY de.rang ASC, de.date ASC';
990
        }
991
992
        $resql = $this->db->query($sql);
993
        if ($resql)
994
        {
995
            $num = $this->db->num_rows($resql);
996
            $i = 0;
997
            while ($i < $num)
998
            {
999
                $objp = $this->db->fetch_object($resql);
1000
1001
                $deplig = new ExpenseReportLine($this->db);
1002
1003
                $deplig->rowid          = $objp->rowid;
1004
                $deplig->id             = $objp->id;
1005
                $deplig->comments       = $objp->comments;
1006
                $deplig->qty            = $objp->qty;
1007
                $deplig->value_unit     = $objp->value_unit;
1008
                $deplig->date           = $objp->date;
1009
                $deplig->dates          = $this->db->jdate($objp->date);
1010
1011
                $deplig->fk_expensereport = $objp->fk_expensereport;
1012
                $deplig->fk_c_type_fees   = $objp->fk_c_type_fees;
1013
                $deplig->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
1014
                $deplig->fk_projet        = $objp->fk_project;  // deprecated
1015
                $deplig->fk_project       = $objp->fk_project;
1016
                $deplig->fk_ecm_files     = $objp->fk_ecm_files;
1017
1018
                $deplig->total_ht         = $objp->total_ht;
1019
                $deplig->total_tva        = $objp->total_tva;
1020
                $deplig->total_ttc        = $objp->total_ttc;
1021
1022
                $deplig->type_fees_code     = empty($objp->code_type_fees)?'TF_OTHER':$objp->code_type_fees;
1023
                $deplig->type_fees_libelle  = $objp->libelle_type_fees;
1024
				$deplig->tva_tx			    = $objp->tva_tx;
1025
                $deplig->vatrate            = $objp->tva_tx;
1026
                $deplig->projet_ref         = $objp->ref_projet;
1027
                $deplig->projet_title       = $objp->title_projet;
1028
1029
                $deplig->rang               = $objp->rang;
1030
1031
                $this->lines[$i] = $deplig;
1032
1033
                $i++;
1034
            }
1035
            $this->db->free($resql);
1036
            return 1;
1037
        }
1038
        else
1039
        {
1040
            $this->error=$this->db->lasterror();
1041
            dol_syslog(get_class($this)."::fetch_lines: Error ".$this->error, LOG_ERR);
1042
            return -3;
1043
        }
1044
    }
1045
1046
1047
    /**
1048
     * delete
1049
     *
1050
     * @param   User    $fuser      User that delete
1051
     * @return  int                 <0 if KO, >0 if OK
1052
     */
1053
    public function delete(User $fuser = null)
1054
    {
1055
        global $user,$langs,$conf;
1056
1057
        if (! $rowid) $rowid=$this->id;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $rowid seems to be never defined.
Loading history...
1058
1059
        $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element_line.' WHERE '.$this->fk_element.' = '.$rowid;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $rowid does not seem to be defined for all execution paths leading up to this point.
Loading history...
1060
        if ($this->db->query($sql))
1061
        {
1062
            $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid = '.$rowid;
1063
            $resql=$this->db->query($sql);
1064
            if ($resql)
1065
            {
1066
                $this->db->commit();
1067
                return 1;
1068
            }
1069
            else
1070
            {
1071
                $this->error=$this->db->error()." sql=".$sql;
1072
                dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
1073
                $this->db->rollback();
1074
                return -6;
1075
            }
1076
        }
1077
        else
1078
        {
1079
            $this->error=$this->db->error()." sql=".$sql;
1080
            dol_syslog(get_class($this)."::delete ".$this->error, LOG_ERR);
1081
            $this->db->rollback();
1082
            return -4;
1083
        }
1084
    }
1085
1086
    /**
1087
     * Set to status validate
1088
     *
1089
     * @param   User    $fuser      User
1090
	 * @param   int     $notrigger  Disable triggers
1091
     * @return  int                 <0 if KO, 0 if nothing done, >0 if OK
1092
     */
1093
    public function setValidate($fuser, $notrigger = 0)
1094
    {
1095
        global $conf,$langs,$user;
1096
1097
		$error = 0;
1098
		$now = dol_now();
1099
1100
        // Protection
1101
        if ($this->statut == self::STATUS_VALIDATED)
1102
        {
1103
            dol_syslog(get_class($this)."::valid action abandonned: already validated", LOG_WARNING);
1104
            return 0;
1105
        }
1106
1107
        $this->date_valid = $now;		// Required for the getNextNum later.
1108
1109
		// Define new ref
1110
        if (! $error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
1111
        {
1112
            $num = $this->getNextNumRef();
1113
        }
1114
        else
1115
		{
1116
            $num = $this->ref;
1117
        }
1118
        if (empty($num) || $num < 0) return -1;
1119
1120
        $this->newref = $num;
1121
1122
		$this->db->begin();
1123
1124
        // Validate
1125
        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
1126
        $sql.= " SET ref = '".$num."',";
1127
        $sql.= " fk_statut = ".self::STATUS_VALIDATED.",";
1128
        $sql.= " date_valid='".$this->db->idate($this->date_valid)."',";
1129
        $sql.= " fk_user_valid = ".$user->id;
1130
        $sql.= " WHERE rowid = ".$this->id;
1131
1132
        $resql=$this->db->query($sql);
1133
        if ($resql)
1134
        {
1135
			if (! $error && ! $notrigger)
1136
			{
1137
				// Call trigger
1138
				$result=$this->call_trigger('EXPENSE_REPORT_VALIDATE', $fuser);
1139
				if ($result < 0) {
1140
					$error++;
1141
				}
1142
				// End call triggers
1143
			}
1144
1145
			if (! $error)
1146
			{
1147
			    $this->oldref = $this->ref;
1148
1149
			    // Rename directory if dir was a temporary ref
1150
			    if (preg_match('/^[\(]?PROV/i', $this->ref))
1151
			    {
1152
			    	require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1153
1154
			    	// Now we rename also files into index
1155
			    	$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)."'";
1156
			    	$sql.= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'expensereport/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
1157
					$resql = $this->db->query($sql);
1158
					if (! $resql) { $error++; $this->error = $this->db->lasterror(); }
1159
1160
			    	// We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
1161
					$oldref = dol_sanitizeFileName($this->ref);
1162
					$newref = dol_sanitizeFileName($num);
1163
					$dirsource = $conf->expensereport->dir_output.'/'.$oldref;
1164
					$dirdest = $conf->expensereport->dir_output.'/'.$newref;
1165
					if (! $error && file_exists($dirsource))
1166
					{
1167
					    dol_syslog(get_class($this)."::setValidate() rename dir ".$dirsource." into ".$dirdest);
1168
1169
					    if (@rename($dirsource, $dirdest))
1170
					    {
1171
					        dol_syslog("Rename ok");
1172
					        // Rename docs starting with $oldref with $newref
1173
					        $listoffiles=dol_dir_list($conf->expensereport->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
1174
					        foreach($listoffiles as $fileentry)
1175
					        {
1176
					        	$dirsource=$fileentry['name'];
1177
					        	$dirdest=preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
1178
					        	$dirsource=$fileentry['path'].'/'.$dirsource;
1179
					        	$dirdest=$fileentry['path'].'/'.$dirdest;
1180
					        	@rename($dirsource, $dirdest);
1 ignored issue
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

1180
					        	/** @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...
1181
					        }
1182
					    }
1183
					}
1184
				}
1185
			}
1186
1187
			// Set new ref and current status
1188
			if (! $error)
1189
			{
1190
			    $this->ref = $num;
1191
			    $this->statut = self::STATUS_VALIDATED;
1192
			}
1193
1194
			if (empty($error))
1195
			{
1196
				$this->db->commit();
1197
				return 1;
1198
			}
1199
			else
1200
			{
1201
				$this->db->rollback();
1202
				$this->error=$this->db->error();
1203
				return -2;
1204
			}
1205
        }
1206
        else
1207
        {
1208
			$this->db->rollback();
1209
            $this->error=$this->db->lasterror();
1210
            return -1;
1211
        }
1212
    }
1213
1214
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1215
    /**
1216
     * set_save_from_refuse
1217
     *
1218
     * @param   User    $fuser      User
1219
     * @return  int                 <0 if KO, >0 if OK
1220
     */
1221
    public function set_save_from_refuse($fuser)
1222
    {
1223
        // phpcs:enable
1224
        global $conf,$langs;
1225
1226
        // Sélection de la date de début de la NDF
1227
        $sql = 'SELECT date_debut';
1228
        $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
1229
        $sql.= ' WHERE rowid = '.$this->id;
1230
1231
        $result = $this->db->query($sql);
1232
1233
        $objp = $this->db->fetch_object($result);
1234
1235
        $this->date_debut = $this->db->jdate($objp->date_debut);
1236
1237
        if ($this->fk_statut != 2)
1238
        {
1239
            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1240
            $sql.= " SET fk_statut = 2";
1241
            $sql.= ' WHERE rowid = '.$this->id;
1242
1243
            dol_syslog(get_class($this)."::set_save_from_refuse sql=".$sql, LOG_DEBUG);
1244
1245
            if ($this->db->query($sql))
1246
            {
1247
                return 1;
1248
            }
1249
            else
1250
            {
1251
                $this->error=$this->db->lasterror();
1252
                return -1;
1253
            }
1254
        }
1255
        else
1256
        {
1257
            dol_syslog(get_class($this)."::set_save_from_refuse expensereport already with save status", LOG_WARNING);
1258
        }
1259
    }
1260
1261
    /**
1262
     * Set status to approved
1263
     *
1264
     * @param   User    $fuser      User
1265
	 * @param   int     $notrigger  Disable triggers
1266
     * @return  int                 <0 if KO, 0 if nothing done, >0 if OK
1267
     */
1268
    public function setApproved($fuser, $notrigger = 0)
1269
    {
1270
        $now=dol_now();
1271
        $error = 0;
1272
1273
        // date approval
1274
        $this->date_approve = $now;
1275
        if ($this->fk_statut != 5) {
1276
            $this->db->begin();
1277
1278
            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1279
            $sql.= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = 5, fk_user_approve = ".$fuser->id.",";
1280
            $sql.= " date_approve='".$this->db->idate($this->date_approve)."'";
1281
            $sql.= ' WHERE rowid = '.$this->id;
1282
            if ($this->db->query($sql))
1283
            {
1284
                if (!$notrigger)
1285
				{
1286
					// Call trigger
1287
					$result=$this->call_trigger('EXPENSE_REPORT_APPROVE', $fuser);
1288
1289
					if ($result < 0) {
1290
						$error++;
1291
					}
1292
					// End call triggers
1293
				}
1294
1295
				if (empty($error))
1296
				{
1297
					$this->db->commit();
1298
					return 1;
1299
				}
1300
				else
1301
				{
1302
					$this->db->rollback();
1303
					$this->error=$this->db->error();
1304
					return -2;
1305
				}
1306
            }
1307
            else
1308
            {
1309
				$this->db->rollback();
1310
                $this->error=$this->db->lasterror();
1311
                return -1;
1312
            }
1313
        }
1314
        else
1315
        {
1316
            dol_syslog(get_class($this)."::setApproved expensereport already with approve status", LOG_WARNING);
1317
        }
1318
1319
        return 0;
1320
    }
1321
1322
    /**
1323
     * setDeny
1324
     *
1325
     * @param User      $fuser      User
1326
     * @param string    $details    Details
1327
     * @param int       $notrigger  Disable triggers
1328
     * @return int
1329
     */
1330
    public function setDeny($fuser, $details, $notrigger = 0)
1331
    {
1332
        $now = dol_now();
1333
		$error = 0;
1334
1335
        // date de refus
1336
        if ($this->fk_statut != 99)
1337
        {
1338
            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1339
            $sql.= " SET ref = '".$this->db->escape($this->ref)."', fk_statut = 99, fk_user_refuse = ".$fuser->id.",";
1340
            $sql.= " date_refuse='".$this->db->idate($now)."',";
1341
            $sql.= " detail_refuse='".$this->db->escape($details)."',";
1342
            $sql.= " fk_user_approve = NULL";
1343
            $sql.= ' WHERE rowid = '.$this->id;
1344
            if ($this->db->query($sql))
1345
            {
1346
                $this->fk_statut = 99;
1347
                $this->fk_user_refuse = $fuser->id;
1348
                $this->detail_refuse = $details;
1349
                $this->date_refuse = $now;
1350
1351
				if (!$notrigger)
1352
				{
1353
					// Call trigger
1354
					$result=$this->call_trigger('EXPENSE_REPORT_DENY', $fuser);
1355
1356
					if ($result < 0) {
1357
						$error++;
1358
					}
1359
					// End call triggers
1360
				}
1361
1362
				if (empty($error))
1363
				{
1364
					$this->db->commit();
1365
					return 1;
1366
				}
1367
				else
1368
				{
1369
					$this->db->rollback();
1370
					$this->error=$this->db->error();
1371
					return -2;
1372
				}
1373
            }
1374
            else
1375
            {
1376
				$this->db->rollback();
1377
                $this->error=$this->db->lasterror();
1378
                return -1;
1379
            }
1380
        }
1381
        else
1382
        {
1383
            dol_syslog(get_class($this)."::setDeny expensereport already with refuse status", LOG_WARNING);
1384
        }
1385
    }
1386
1387
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1388
    /**
1389
     * set_unpaid
1390
     *
1391
     * @param   User    $fuser      User
1392
	 * @param   int     $notrigger  Disable triggers
1393
     * @return  int                 <0 if KO, >0 if OK
1394
     */
1395
    public function set_unpaid($fuser, $notrigger = 0)
1396
    {
1397
        // phpcs:enable
1398
		$error = 0;
1399
1400
        if ($this->fk_c_deplacement_statuts != 5)
0 ignored issues
show
Bug introduced by
The property fk_c_deplacement_statuts does not exist on ExpenseReport. Did you mean statuts?
Loading history...
1401
        {
1402
			$this->db->begin();
1403
1404
            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1405
            $sql.= " SET fk_statut = 5";
1406
            $sql.= ' WHERE rowid = '.$this->id;
1407
1408
            dol_syslog(get_class($this)."::set_unpaid sql=".$sql, LOG_DEBUG);
1409
1410
			if ($this->db->query($sql))
1411
			{
1412
				if (!$notrigger)
1413
				{
1414
					// Call trigger
1415
					$result=$this->call_trigger('EXPENSE_REPORT_UNPAID', $fuser);
1416
1417
					if ($result < 0) {
1418
						$error++;
1419
					}
1420
					// End call triggers
1421
				}
1422
1423
				if (empty($error))
1424
				{
1425
					$this->db->commit();
1426
					return 1;
1427
				}
1428
				else
1429
				{
1430
					$this->db->rollback();
1431
					$this->error=$this->db->error();
1432
					return -2;
1433
				}
1434
			}
1435
			else
1436
			{
1437
				$this->db->rollback();
1438
				$this->error=$this->db->error();
1439
				return -1;
1440
			}
1441
        }
1442
        else
1443
        {
1444
            dol_syslog(get_class($this)."::set_unpaid expensereport already with unpaid status", LOG_WARNING);
1445
        }
1446
    }
1447
1448
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1449
    /**
1450
     * set_cancel
1451
     *
1452
     * @param   User    $fuser      User
1453
     * @param   string  $detail     Detail
1454
	 * @param   int     $notrigger  Disable triggers
1455
     * @return  int                 <0 if KO, >0 if OK
1456
     */
1457
    public function set_cancel($fuser, $detail, $notrigger = 0)
1458
    {
1459
        // phpcs:enable
1460
		$error = 0;
1461
        $this->date_cancel = $this->db->idate(gmmktime());
1462
        if ($this->fk_statut != ExpenseReport::STATUS_CANCELED)
1463
        {
1464
			$this->db->begin();
1465
1466
            $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1467
            $sql.= " SET fk_statut = ".ExpenseReport::STATUS_CANCELED.", fk_user_cancel = ".$fuser->id;
1468
            $sql.= ", date_cancel='".$this->db->idate($this->date_cancel)."'";
1469
            $sql.= " ,detail_cancel='".$this->db->escape($detail)."'";
1470
            $sql.= ' WHERE rowid = '.$this->id;
1471
1472
            dol_syslog(get_class($this)."::set_cancel sql=".$sql, LOG_DEBUG);
1473
1474
            if ($this->db->query($sql))
1475
            {
1476
				if (!$notrigger)
1477
				{
1478
					// Call trigger
1479
					$result=$this->call_trigger('EXPENSE_REPORT_CANCEL', $fuser);
1480
1481
					if ($result < 0) {
1482
						$error++;
1483
					}
1484
					// End call triggers
1485
				}
1486
1487
				if (empty($error))
1488
				{
1489
					$this->db->commit();
1490
					return 1;
1491
				}
1492
				else
1493
				{
1494
					$this->db->rollback();
1495
					$this->error=$this->db->error();
1496
					return -2;
1497
				}
1498
            }
1499
            else
1500
            {
1501
				$this->db->rollback();
1502
                $this->error=$this->db->error();
1503
                return -1;
1504
            }
1505
        }
1506
        else
1507
        {
1508
            dol_syslog(get_class($this)."::set_cancel expensereport already with cancel status", LOG_WARNING);
1509
        }
1510
    }
1511
1512
    /**
1513
     * Return next reference of expense report not already used
1514
     *
1515
     * @return    string            free ref
1516
     */
1517
    public function getNextNumRef()
1518
    {
1519
        global $langs, $conf;
1520
        $langs->load("trips");
1521
1522
        if (! empty($conf->global->EXPENSEREPORT_ADDON))
1523
        {
1524
        	$mybool=false;
1525
1526
        	$file = $conf->global->EXPENSEREPORT_ADDON.".php";
1527
			$classname = $conf->global->EXPENSEREPORT_ADDON;
1528
1529
			// Include file with class
1530
			$dirmodels=array_merge(array('/'), (array) $conf->modules_parts['models']);
1531
			foreach ($dirmodels as $reldir)
1532
			{
1533
                $dir = dol_buildpath($reldir."core/modules/expensereport/");
1534
1535
                // Load file with numbering class (if found)
1536
                $mybool|=@include_once $dir.$file;
1537
            }
1538
1539
            if ($mybool === false) {
1540
                dol_print_error('', "Failed to include file ".$file);
1541
                return '';
1542
            }
1543
1544
            $obj = new $classname();
1545
            $numref = $obj->getNextValue($this);
1546
1547
            if ($numref != "")
1548
            {
1549
            	return $numref;
1550
            }
1551
            else
1552
			{
1553
				$this->error=$obj->error;
1554
				$this->errors=$obj->errors;
1555
            	//dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error);
1556
            	return -1;
1557
            }
1558
        }
1559
        else
1560
        {
1561
            $this->error = "Error_EXPENSEREPORT_ADDON_NotDefined";
1562
            return -2;
1563
        }
1564
    }
1565
1566
    /**
1567
     *  Return clicable name (with picto eventually)
1568
     *
1569
     *	@param		int		$withpicto					0=No picto, 1=Include picto into link, 2=Only picto
1570
     *	@param		int		$max						Max length of shown ref
1571
     *	@param		int		$short						1=Return just URL
1572
     *	@param		string	$moretitle					Add more text to title tooltip
1573
     *	@param		int		$notooltip					1=Disable tooltip
1574
     *  @param  	int     $save_lastsearch_value    	-1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking
1575
     *	@return		string								String with URL
1576
     */
1577
    public function getNomUrl($withpicto = 0, $max = 0, $short = 0, $moretitle = '', $notooltip = 0, $save_lastsearch_value = -1)
1578
    {
1579
        global $langs, $conf;
1580
1581
        $result='';
1582
1583
        $url = DOL_URL_ROOT.'/expensereport/card.php?id='.$this->id;
1584
1585
        if ($short) return $url;
1586
1587
        $label = '<u>' . $langs->trans("ShowExpenseReport") . '</u>';
1588
        if (! empty($this->ref))
1589
            $label .= '<br><b>' . $langs->trans('Ref') . ':</b> ' . $this->ref;
1590
        if (! empty($this->total_ht))
1591
            $label.= '<br><b>' . $langs->trans('AmountHT') . ':</b> ' . price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1592
        if (! empty($this->total_tva))
1593
            $label.= '<br><b>' . $langs->trans('VAT') . ':</b> ' . price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1594
        if (! empty($this->total_ttc))
1595
            $label.= '<br><b>' . $langs->trans('AmountTTC') . ':</b> ' . price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1596
        if ($moretitle) $label.=' - '.$moretitle;
1597
1598
        //if ($option != 'nolink')
1599
        //{
1600
        // Add param to save lastsearch_values or not
1601
        	$add_save_lastsearch_values=($save_lastsearch_value == 1 ? 1 : 0);
1602
        	if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values=1;
1603
        	if ($add_save_lastsearch_values) $url.='&save_lastsearch_values=1';
1604
        //}
1605
1606
        $ref=$this->ref;
1607
        if (empty($ref)) $ref=$this->id;
1608
1609
        $linkclose='';
1610
        if (empty($notooltip))
1611
        {
1612
            if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
1613
            {
1614
                $label=$langs->trans("ShowExpenseReport");
1615
                $linkclose.=' alt="'.dol_escape_htmltag($label, 1).'"';
1616
            }
1617
            $linkclose.= ' title="'.dol_escape_htmltag($label, 1).'"';
1618
            $linkclose.=' class="classfortooltip"';
1619
        }
1620
1621
        $linkstart = '<a href="'.$url.'"';
1622
        $linkstart.=$linkclose.'>';
1623
        $linkend='</a>';
1624
1625
        $result .= $linkstart;
1626
        if ($withpicto) $result.=img_object(($notooltip?'':$label), $this->picto, ($notooltip?(($withpicto != 2) ? 'class="paddingright"' : ''):'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip?0:1);
1627
        if ($withpicto != 2) $result.=($max?dol_trunc($ref, $max):$ref);
1628
        $result .= $linkend;
1629
1630
        return $result;
1631
    }
1632
1633
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1634
    /**
1635
     *  Update total of an expense report when you add a line.
1636
     *
1637
     *  @param    string    $ligne_total_ht    Amount without taxes
1638
     *  @param    string    $ligne_total_tva    Amount of all taxes
1639
     *  @return    void
1640
     */
1641
    public function update_totaux_add($ligne_total_ht, $ligne_total_tva)
1642
    {
1643
        // phpcs:enable
1644
        $this->total_ht = $this->total_ht + $ligne_total_ht;
1645
        $this->total_tva = $this->total_tva + $ligne_total_tva;
1646
        $this->total_ttc = $this->total_ht + $this->total_tva;
1647
1648
        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
1649
        $sql.= " total_ht = ".$this->total_ht;
1650
        $sql.= " , total_ttc = ".$this->total_ttc;
1651
        $sql.= " , total_tva = ".$this->total_tva;
1652
        $sql.= " WHERE rowid = ".$this->id;
1653
1654
        $result = $this->db->query($sql);
1655
        if ($result):
1656
        return 1;
1657
        else:
1658
        $this->error=$this->db->error();
1659
        return -1;
1660
        endif;
1661
    }
1662
1663
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1664
    /**
1665
     *  Update total of an expense report when you delete a line.
1666
     *
1667
     *  @param    string    $ligne_total_ht    Amount without taxes
1668
     *  @param    string    $ligne_total_tva    Amount of all taxes
1669
     *  @return    void
1670
     */
1671
    public function update_totaux_del($ligne_total_ht, $ligne_total_tva)
1672
    {
1673
        // phpcs:enable
1674
        $this->total_ht = $this->total_ht - $ligne_total_ht;
1675
        $this->total_tva = $this->total_tva - $ligne_total_tva;
1676
        $this->total_ttc = $this->total_ht + $this->total_tva;
1677
1678
        $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
1679
        $sql.= " total_ht = ".$this->total_ht;
1680
        $sql.= " , total_ttc = ".$this->total_ttc;
1681
        $sql.= " , total_tva = ".$this->total_tva;
1682
        $sql.= " WHERE rowid = ".$this->id;
1683
1684
        $result = $this->db->query($sql);
1685
        if ($result):
1686
        return 1;
1687
        else:
1688
        $this->error=$this->db->error();
1689
        return -1;
1690
        endif;
1691
    }
1692
1693
	/**
1694
	 * addline
1695
	 *
1696
	 * @param    float       $qty                      Qty
1697
	 * @param    double      $up                       Value init
1698
	 * @param    int         $fk_c_type_fees           Type payment
1699
	 * @param    string      $vatrate                  Vat rate (Can be '10' or '10 (ABC)')
1700
	 * @param    string      $date                     Date
1701
	 * @param    string      $comments                 Description
1702
	 * @param    int         $fk_project               Project id
1703
	 * @param    int         $fk_c_exp_tax_cat         Car category id
1704
	 * @param    int         $type                     Type line
1705
	 * @param    int         $fk_ecm_files             Id of ECM file to link to this expensereport line
1706
	 * @return   int                                   <0 if KO, >0 if OK
1707
	 */
1708
    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)
1709
	{
1710
		global $conf, $langs, $mysoc;
1711
1712
        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);
1713
1714
		if ($this->fk_statut == self::STATUS_DRAFT)
1715
		{
1716
			if (empty($qty)) $qty = 0;
1717
			if (empty($fk_c_type_fees) || $fk_c_type_fees < 0) $fk_c_type_fees = 0;
1718
			if (empty($fk_c_exp_tax_cat) || $fk_c_exp_tax_cat < 0) $fk_c_exp_tax_cat = 0;
1719
			if (empty($vatrate) || $vatrate < 0) $vatrate = 0;
1720
			if (empty($date)) $date = '';
1721
			if (empty($fk_project)) $fk_project = 0;
1722
1723
			$qty = price2num($qty);
1724
			if (! preg_match('/\s*\((.*)\)/', $vatrate)) {
1725
				$vatrate = price2num($vatrate);               // $txtva can have format '5.0 (XXX)' or '5'
1726
			}
1727
			$up = price2num($up);
1728
1729
			$this->db->begin();
1730
1731
			$this->line = new ExpenseReportLine($this->db);
1732
1733
			$localtaxes_type=getLocalTaxesFromRate($vatrate, 0, $mysoc, $this->thirdparty);
1734
1735
			$vat_src_code = '';
1736
			if (preg_match('/\s*\((.*)\)/', $vatrate, $reg))
1737
			{
1738
				$vat_src_code = $reg[1];
1739
				$vatrate = preg_replace('/\s*\(.*\)/', '', $vatrate);    // Remove code into vatrate.
1740
			}
1741
			$vatrate = preg_replace('/\*/', '', $vatrate);
1742
1743
			$seller = '';  // seller is unknown
1744
1745
			$tmp = calcul_price_total($qty, $up, 0, $vatrate, 0, 0, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
1746
1747
			$this->line->value_unit = $up;
1748
			$this->line->vat_src_code = $vat_src_code;
1749
			$this->line->vatrate = price2num($vatrate);
1750
			$this->line->total_ttc = $tmp[2];
1751
			$this->line->total_ht = $tmp[0];
1752
			$this->line->total_tva = $tmp[1];
1753
1754
			$this->line->fk_expensereport = $this->id;
1755
			$this->line->qty = $qty;
1756
			$this->line->date = $date;
1757
			$this->line->fk_c_type_fees = $fk_c_type_fees;
1758
			$this->line->fk_c_exp_tax_cat = $fk_c_exp_tax_cat;
1759
			$this->line->comments = $comments;
1760
			$this->line->fk_projet = $fk_project;    // deprecated
1761
			$this->line->fk_project = $fk_project;
1762
1763
			$this->line->fk_ecm_files = $fk_ecm_files;
1764
1765
			$this->applyOffset();
1766
			$this->checkRules($type, $seller);
1767
1768
			$result=$this->line->insert(0, true);
1769
            if ($result > 0)
1770
            {
1771
                $result=$this->update_price();	// This method is designed to add line from user input so total calculation must be done using 'auto' mode.
1772
                if ($result > 0)
1773
                {
1774
                    $this->db->commit();
1775
                    return $this->line->rowid;
1776
                }
1777
                else
1778
                {
1779
                    $this->db->rollback();
1780
                    return -1;
1781
                }
1782
            }
1783
            else
1784
            {
1785
                $this->error=$this->line->error;
1786
                dol_syslog(get_class($this)."::addline error=".$this->error, LOG_ERR);
1787
                $this->db->rollback();
1788
                return -2;
1789
            }
1790
		}
1791
		else
1792
        {
1793
            dol_syslog(get_class($this)."::addline status of expense report must be Draft to allow use of ->addline()", LOG_ERR);
1794
			$this->error = 'ErrorExpenseNotDraft';
1795
            return -3;
1796
        }
1797
	}
1798
1799
	/**
1800
	 * Check constraint of rules and update price if needed
1801
	 *
1802
	 * @param	int		$type		type of line
1803
	 * @param	string	$seller		seller, but actually he is unknown
1804
	 * @return true or false
1805
	 */
1806
	public function checkRules($type = 0, $seller = '')
1807
	{
1808
		global $user,$conf,$db,$langs;
1809
1810
		$langs->load('trips');
1811
1812
		if (empty($conf->global->MAIN_USE_EXPENSE_RULE)) return true; // if don't use rules
1813
1814
		$rulestocheck = ExpenseReportRule::getAllRule($this->line->fk_c_type_fees, $this->line->date, $this->fk_user_author);
1815
1816
		$violation = 0;
1817
		$rule_warning_message_tab = array();
1818
1819
		$current_total_ttc = $this->line->total_ttc;
1820
		$new_current_total_ttc = $this->line->total_ttc;
1821
1822
		// check if one is violated
1823
		foreach ($rulestocheck as $rule)
1824
		{
1825
			if (in_array($rule->code_expense_rules_type, array('EX_DAY', 'EX_MON', 'EX_YEA'))) $amount_to_test = $this->line->getExpAmount($rule, $this->fk_user_author, $rule->code_expense_rules_type);
1826
			else $amount_to_test = $current_total_ttc; // EX_EXP
1827
1828
			$amount_to_test = $amount_to_test - $current_total_ttc + $new_current_total_ttc; // if amount as been modified by a previous rule
1829
1830
			if ($amount_to_test > $rule->amount)
1831
			{
1832
				$violation++;
1833
1834
				if ($rule->restrictive)
1835
				{
1836
					$this->error = 'ExpenseReportConstraintViolationError';
1837
					$this->errors[] = $this->error;
1838
1839
					$new_current_total_ttc -= $amount_to_test - $rule->amount; // ex, entered 16€, limit 12€, subtracts 4€;
1840
					$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), $langs->trans('by'.$rule->code_expense_rules_type, price($new_current_total_ttc, 0, $langs, 1, -1, -1, $conf->currency)));
1841
				}
1842
				else
1843
				{
1844
					$this->error = 'ExpenseReportConstraintViolationWarning';
1845
					$this->errors[] = $this->error;
1846
1847
					$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), $langs->trans('nolimitby'.$rule->code_expense_rules_type));
1848
				}
1849
1850
				// No break, we sould test if another rule is violated
1851
			}
1852
		}
1853
1854
		$this->line->rule_warning_message = implode('\n', $rule_warning_message_tab);
1855
1856
		if ($violation > 0)
1857
		{
1858
			$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);
1859
1860
			$this->line->value_unit = $tmp[5];
1861
			$this->line->total_ttc = $tmp[2];
1862
			$this->line->total_ht = $tmp[0];
1863
			$this->line->total_tva = $tmp[1];
1864
1865
			return false;
1866
		}
1867
		else return true;
1868
	}
1869
1870
	/**
1871
	 * Method to apply the offset if needed
1872
	 *
1873
	 * @return boolean		true=applied, false=not applied
1874
	 */
1875
	public function applyOffset()
1876
	{
1877
		global $conf;
1878
1879
		if (empty($conf->global->MAIN_USE_EXPENSE_IK)) return false;
1880
1881
		$userauthor = new User($this->db);
1882
		if ($userauthor->fetch($this->fk_user_author) <= 0)
1883
		{
1884
			$this->error = 'ErrorCantFetchUser';
1885
			$this->errors[] = 'ErrorCantFetchUser';
1886
			return false;
1887
		}
1888
1889
		$range = ExpenseReportIk::getRangeByUser($userauthor, $this->line->fk_c_exp_tax_cat);
1890
1891
		if (empty($range))
1892
		{
1893
			$this->error = 'ErrorNoRangeAvailable';
1894
			$this->errors[] = 'ErrorNoRangeAvailable';
1895
			return false;
1896
		}
1897
1898
		if (!empty($conf->global->MAIN_EXPENSE_APPLY_ENTIRE_OFFSET)) $ikoffset = $range->ikoffset;
0 ignored issues
show
Bug introduced by
The property ikoffset does not exist on boolean.
Loading history...
1899
		else $ikoffset = $range->ikoffset / 12; // The amount of offset is a global value for the year
1900
1901
		// Test if ikoffset has been applied for the current month
1902
		if (!$this->offsetAlreadyGiven())
1903
		{
1904
			$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...
1905
			$tmp = calcul_price_total($this->line->qty, $new_up, 0, $this->line->vatrate, 0, 0, 0, 'TTC', 0, $type, $seller);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $type seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $seller seems to be never defined.
Loading history...
1906
1907
			$this->line->value_unit = $tmp[5];
1908
			$this->line->total_ttc = $tmp[2];
1909
			$this->line->total_ht = $tmp[0];
1910
			$this->line->total_tva = $tmp[1];
1911
1912
			return true;
1913
		}
1914
1915
		return false;
1916
	}
1917
1918
	/**
1919
	 * If the sql find any rows then the ikoffset is already given (ikoffset is applied at the first expense report line)
1920
	 *
1921
	 * @return bool
1922
	 */
1923
	public function offsetAlreadyGiven()
1924
	{
1925
		$sql = 'SELECT e.rowid FROM '.MAIN_DB_PREFIX.'expensereport e';
1926
		$sql.= ' INNER JOIN '.MAIN_DB_PREFIX.'expensereport_det d ON (e.rowid = d.fk_expensereport)';
1927
		$sql.= ' INNER JOIN '.MAIN_DB_PREFIX.'c_type_fees f ON (d.fk_c_type_fees = f.id AND f.code = "EX_KME")';
1928
		$sql.= ' WHERE e.fk_user_author = '.(int) $this->fk_user_author;
1929
		$sql.= ' AND YEAR(d.date) = "'.dol_print_date($this->line->date, '%Y').'" AND MONTH(d.date) = "'.dol_print_date($this->line->date, '%m').'"';
1930
		if (!empty($this->line->id)) $sql.= ' AND d.rowid <> '.$this->line->id;
1931
1932
		dol_syslog(get_class($this)."::offsetAlreadyGiven sql=".$sql);
1933
		$resql = $this->db->query($sql);
1934
		if ($resql)
1935
		{
1936
			$num = $this->db->num_rows($resql);
1937
			if ($num > 0) return true;
1938
		}
1939
		else
1940
		{
1941
			dol_print_error($this->db);
1942
		}
1943
1944
		return false;
1945
	}
1946
1947
    /**
1948
     * Update an expense report line
1949
     *
1950
     * @param   int         $rowid                  Line to edit
1951
     * @param   int         $type_fees_id           Type payment
1952
     * @param   int         $projet_id              Project id
1953
     * @param   double      $vatrate                Vat rate. Can be '8.5' or '8.5* (8.5NPROM...)'
1954
     * @param   string      $comments               Description
1955
     * @param   float       $qty                    Qty
1956
     * @param   double      $value_unit             Value init
1957
     * @param   int         $date                   Date
1958
     * @param   int         $expensereport_id       Expense report id
1959
     * @param   int         $fk_c_exp_tax_cat       Id of category of car
1960
	 * @param   int         $fk_ecm_files           Id of ECM file to link to this expensereport line
1961
     * @return  int                                 <0 if KO, >0 if OK
1962
     */
1963
    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)
1964
    {
1965
        global $user, $mysoc;
1966
1967
        if ($this->fk_statut==0 || $this->fk_statut==99)
1968
        {
1969
            $this->db->begin();
1970
1971
            $type = 0;      // TODO What if type is service ?
1972
1973
            // We don't know seller and buyer for expense reports
1974
            $seller = $mysoc;
1975
            $buyer = new Societe($this->db);
1976
1977
            $localtaxes_type=getLocalTaxesFromRate($vatrate, 0, $buyer, $seller);
1978
1979
            // Clean vat code
1980
            $vat_src_code='';
1981
            if (preg_match('/\((.*)\)/', $vatrate, $reg))
1982
            {
1983
                $vat_src_code = $reg[1];
1984
                $vatrate = preg_replace('/\s*\(.*\)/', '', $vatrate);    // Remove code into vatrate.
1985
            }
1986
            $vatrate = preg_replace('/\*/', '', $vatrate);
1987
1988
            $tmp = calcul_price_total($qty, $value_unit, 0, $vatrate, 0, 0, 0, 'TTC', 0, $type, $seller, $localtaxes_type);
1989
1990
            // calcul total of line
1991
            //$total_ttc  = price2num($qty*$value_unit, 'MT');
1992
1993
            $tx_tva = $vatrate / 100;
1994
            $tx_tva = $tx_tva + 1;
1995
            $total_ht   = price2num($total_ttc/$tx_tva, 'MT');
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $total_ttc does not exist. Did you maybe mean $total_tva?
Loading history...
1996
1997
            $total_tva = price2num($total_ttc - $total_ht, 'MT');
1998
            // fin calculs
1999
2000
            $this->line = new ExpenseReportLine($this->db);
2001
            $this->line->comments        = $comments;
2002
            $this->line->qty             = $qty;
2003
            $this->line->value_unit      = $value_unit;
2004
            $this->line->date            = $date;
2005
2006
            $this->line->fk_expensereport= $expensereport_id;
2007
            $this->line->fk_c_type_fees  = $type_fees_id;
2008
            $this->line->fk_c_exp_tax_cat  = $fk_c_exp_tax_cat;
2009
            $this->line->fk_projet       = $projet_id;  // deprecated
2010
            $this->line->fk_project      = $projet_id;
2011
2012
            $this->line->vat_src_code = $vat_src_code;
2013
            $this->line->vatrate = price2num($vatrate);
2014
            $this->line->total_ttc = $tmp[2];
2015
            $this->line->total_ht = $tmp[0];
2016
            $this->line->total_tva = $tmp[1];
2017
            $this->line->localtax1_tx = $localtaxes_type[1];
0 ignored issues
show
Bug introduced by
The property localtax1_tx does not seem to exist on ExpenseReportLine.
Loading history...
2018
            $this->line->localtax2_tx = $localtaxes_type[3];
0 ignored issues
show
Bug introduced by
The property localtax2_tx does not seem to exist on ExpenseReportLine.
Loading history...
2019
            $this->line->localtax1_type = $localtaxes_type[0];
0 ignored issues
show
Bug introduced by
The property localtax1_type does not seem to exist on ExpenseReportLine.
Loading history...
2020
            $this->line->localtax2_type = $localtaxes_type[2];
0 ignored issues
show
Bug introduced by
The property localtax2_type does not seem to exist on ExpenseReportLine.
Loading history...
2021
2022
            $this->line->fk_ecm_files = $fk_ecm_files;
2023
2024
            $this->line->rowid           = $rowid;
2025
            $this->line->id              = $rowid;
2026
2027
            // Select des infos sur le type fees
2028
            $sql = "SELECT c.code as code_type_fees, c.label as libelle_type_fees";
2029
            $sql.= " FROM ".MAIN_DB_PREFIX."c_type_fees as c";
2030
            $sql.= " WHERE c.id = ".$type_fees_id;
2031
            $resql = $this->db->query($sql);
2032
            if ($resql)
2033
            {
2034
                $objp_fees = $this->db->fetch_object($resql);
2035
                $this->line->type_fees_code      = $objp_fees->code_type_fees;
2036
                $this->line->type_fees_libelle   = $objp_fees->libelle_type_fees;
2037
                $this->db->free($resql);
2038
            }
2039
2040
            // Select des informations du projet
2041
            $sql = "SELECT p.ref as ref_projet, p.title as title_projet";
2042
            $sql.= " FROM ".MAIN_DB_PREFIX."projet as p";
2043
            $sql.= " WHERE p.rowid = ".$projet_id;
2044
            $resql = $this->db->query($sql);
2045
            if ($resql) {
2046
            	$objp_projet = $this->db->fetch_object($resql);
2047
            	$this->line->projet_ref          = $objp_projet->ref_projet;
2048
            	$this->line->projet_title        = $objp_projet->title_projet;
2049
            	$this->db->free($resql);
2050
            }
2051
2052
			$this->applyOffset();
2053
			$this->checkRules();
2054
2055
            $result = $this->line->update($user);
2056
            if ($result > 0)
2057
            {
2058
                $this->db->commit();
2059
                return 1;
2060
            }
2061
            else
2062
            {
2063
                $this->error=$this->line->error;
2064
                $this->errors=$this->line->errors;
2065
                $this->db->rollback();
2066
                return -2;
2067
            }
2068
        }
2069
    }
2070
2071
    /**
2072
     * deleteline
2073
     *
2074
     * @param   int     $rowid      Row id
2075
     * @param   User    $fuser      User
2076
     * @return  int                 <0 if KO, >0 if OK
2077
     */
2078
    public function deleteline($rowid, $fuser = '')
2079
    {
2080
        $this->db->begin();
2081
2082
        $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2083
        $sql.= ' WHERE rowid = '.$rowid;
2084
2085
        dol_syslog(get_class($this)."::deleteline sql=".$sql);
2086
        $result = $this->db->query($sql);
2087
        if (!$result)
2088
        {
2089
            $this->error=$this->db->error();
2090
            dol_syslog(get_class($this)."::deleteline  Error ".$this->error, LOG_ERR);
2091
            $this->db->rollback();
2092
            return -1;
2093
        }
2094
2095
        $this->db->commit();
2096
2097
        return 1;
2098
    }
2099
2100
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2101
    /**
2102
     * periode_existe
2103
     *
2104
     * @param   User       $fuser          User
2105
     * @param   integer    $date_debut     Start date
2106
     * @param   integer    $date_fin       End date
2107
     * @return  int                        <0 if KO, >0 if OK
2108
     */
2109
    public function periode_existe($fuser, $date_debut, $date_fin)
2110
    {
2111
        // phpcs:enable
2112
        $sql = "SELECT rowid, date_debut, date_fin";
2113
        $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
2114
        $sql.= " WHERE fk_user_author = '{$fuser->id}'";
2115
2116
        dol_syslog(get_class($this)."::periode_existe sql=".$sql);
2117
        $result = $this->db->query($sql);
2118
        if ($result) {
2119
            $num_rows = $this->db->num_rows($result); $i = 0;
2120
2121
            if ($num_rows > 0)
2122
            {
2123
                $date_d_form = $date_debut;
2124
                $date_f_form = $date_fin;
2125
2126
                $existe = false;
2127
2128
                while ($i < $num_rows)
2129
                {
2130
                    $objp = $this->db->fetch_object($result);
2131
2132
                    $date_d_req = $this->db->jdate($objp->date_debut); // 3
2133
                    $date_f_req = $this->db->jdate($objp->date_fin);      // 4
2134
2135
                    if (!($date_f_form < $date_d_req || $date_d_form > $date_f_req)) $existe = true;
2136
2137
                    $i++;
2138
                }
2139
2140
                if($existe) return 1;
2141
                else return 0;
2142
            }
2143
            else
2144
            {
2145
                return 0;
2146
            }
2147
        }
2148
        else
2149
        {
2150
            $this->error=$this->db->lasterror();
2151
            dol_syslog(get_class($this)."::periode_existe  Error ".$this->error, LOG_ERR);
2152
            return -1;
2153
        }
2154
    }
2155
2156
2157
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2158
    /**
2159
     * Return list of people with permission to validate expense reports.
2160
     * Search for permission "approve expense report"
2161
     *
2162
     * @return  array       Array of user ids
2163
     */
2164
    public function fetch_users_approver_expensereport()
2165
    {
2166
        // phpcs:enable
2167
        $users_validator=array();
2168
2169
        $sql = "SELECT DISTINCT ur.fk_user";
2170
        $sql.= " FROM ".MAIN_DB_PREFIX."user_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
2171
        $sql.= " WHERE ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'";                                              // Permission 'Approve';
2172
        $sql.= "UNION";
2173
        $sql.= " SELECT DISTINCT ugu.fk_user";
2174
        $sql.= " FROM ".MAIN_DB_PREFIX."usergroup_user as ugu, ".MAIN_DB_PREFIX."usergroup_rights as ur, ".MAIN_DB_PREFIX."rights_def as rd";
2175
        $sql.= " WHERE ugu.fk_usergroup = ur.fk_usergroup AND ur.fk_id = rd.id and rd.module = 'expensereport' AND rd.perms = 'approve'";       // Permission 'Approve';
2176
        //print $sql;
2177
2178
        dol_syslog(get_class($this)."::fetch_users_approver_expensereport sql=".$sql);
2179
        $result = $this->db->query($sql);
2180
        if($result)
2181
        {
2182
            $num_rows = $this->db->num_rows($result); $i = 0;
2183
            while ($i < $num_rows)
2184
            {
2185
                $objp = $this->db->fetch_object($result);
2186
                array_push($users_validator, $objp->fk_user);
2187
                $i++;
2188
            }
2189
            return $users_validator;
2190
        }
2191
        else
2192
        {
2193
            $this->error=$this->db->lasterror();
2194
            dol_syslog(get_class($this)."::fetch_users_approver_expensereport  Error ".$this->error, LOG_ERR);
2195
            return -1;
2196
        }
2197
    }
2198
2199
    /**
2200
     *  Create a document onto disk accordign to template module.
2201
     *
2202
     *  @param      string      $modele         Force le mnodele a utiliser ('' to not force)
2203
     *  @param      Translate   $outputlangs    objet lang a utiliser pour traduction
2204
     *  @param      int         $hidedetails    Hide details of lines
2205
     *  @param      int         $hidedesc       Hide description
2206
     *  @param      int         $hideref        Hide ref
2207
     *  @param   null|array  $moreparams     Array to provide more information
2208
     *  @return     int                         0 if KO, 1 if OK
2209
     */
2210
    public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2211
    {
2212
        global $conf,$langs;
2213
2214
        $langs->load("trips");
2215
2216
	    if (! dol_strlen($modele)) {
2217
2218
		    $modele = 'standard';
2219
2220
		    if ($this->modelpdf) {
2221
			    $modele = $this->modelpdf;
2222
		    } elseif (! empty($conf->global->EXPENSEREPORT_ADDON_PDF)) {
2223
			    $modele = $conf->global->EXPENSEREPORT_ADDON_PDF;
2224
		    }
2225
	    }
2226
2227
        $modelpath = "core/modules/expensereport/doc/";
2228
2229
        return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2230
    }
2231
2232
    /**
2233
     * List of types
2234
     *
2235
     * @param   int     $active     Active or not
2236
     * @return  array
2237
     */
2238
    public function listOfTypes($active = 1)
2239
    {
2240
        global $langs;
2241
        $ret=array();
2242
        $sql = "SELECT id, code, label";
2243
        $sql.= " FROM ".MAIN_DB_PREFIX."c_type_fees";
2244
        $sql.= " WHERE active = ".$active;
2245
        dol_syslog(get_class($this)."::listOfTypes", LOG_DEBUG);
2246
        $result = $this->db->query($sql);
2247
        if ( $result )
2248
        {
2249
            $num = $this->db->num_rows($result);
2250
            $i=0;
2251
            while ($i < $num)
2252
            {
2253
                $obj = $this->db->fetch_object($result);
2254
                $ret[$obj->code]=(($langs->trans($obj->code)!=$obj->code)?$langs->trans($obj->code):$obj->label);
2255
                $i++;
2256
            }
2257
        }
2258
        else
2259
        {
2260
            dol_print_error($this->db);
2261
        }
2262
        return $ret;
2263
    }
2264
2265
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2266
    /**
2267
     *      Charge indicateurs this->nb pour le tableau de bord
2268
     *
2269
     *      @return     int         <0 if KO, >0 if OK
2270
     */
2271
    public function load_state_board()
2272
    {
2273
        // phpcs:enable
2274
        global $conf;
2275
2276
        $this->nb=array();
2277
2278
        $sql = "SELECT count(ex.rowid) as nb";
2279
        $sql.= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
2280
        $sql.= " WHERE ex.fk_statut > 0";
2281
        $sql.= " AND ex.entity IN (".getEntity('expensereport').")";
2282
2283
        $resql=$this->db->query($sql);
2284
        if ($resql) {
2285
            while ($obj=$this->db->fetch_object($resql)) {
2286
                $this->nb["expensereports"]=$obj->nb;
2287
            }
2288
            $this->db->free($resql);
2289
            return 1;
2290
        }
2291
        else
2292
        {
2293
            dol_print_error($this->db);
2294
            $this->error=$this->db->error();
2295
            return -1;
2296
        }
2297
    }
2298
2299
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2300
    /**
2301
     *      Load indicators for dashboard (this->nbtodo and this->nbtodolate)
2302
     *
2303
     *      @param	User	$user   		Objet user
2304
     *      @param  string  $option         'topay' or 'toapprove'
2305
     *      @return WorkboardResponse|int 	<0 if KO, WorkboardResponse if OK
2306
     */
2307
    public function load_board($user, $option = 'topay')
2308
    {
2309
        // phpcs:enable
2310
        global $conf, $langs;
2311
2312
        if ($user->societe_id) return -1;   // protection pour eviter appel par utilisateur externe
1 ignored issue
show
Deprecated Code introduced by
The property User::$societe_id has been deprecated. ( Ignorable by Annotation )

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

2312
        if (/** @scrutinizer ignore-deprecated */ $user->societe_id) return -1;   // protection pour eviter appel par utilisateur externe
Loading history...
2313
2314
        $now=dol_now();
2315
2316
        $userchildids = $user->getAllChildIds(1);
2317
2318
        $sql = "SELECT ex.rowid, ex.date_valid";
2319
        $sql.= " FROM ".MAIN_DB_PREFIX."expensereport as ex";
2320
        if ($option == 'toapprove') $sql.= " WHERE ex.fk_statut = 2";
2321
        else $sql.= " WHERE ex.fk_statut = 5";
2322
        $sql.= " AND ex.entity IN (".getEntity('expensereport').")";
2323
        $sql.= " AND (ex.fk_user_author IN (".join(',', $userchildids).")";
2324
        $sql.= " OR ex.fk_user_validator IN (".join(',', $userchildids)."))";
2325
2326
        $resql=$this->db->query($sql);
2327
        if ($resql)
2328
        {
2329
	        $langs->load("members");
2330
2331
	        $response = new WorkboardResponse();
2332
	        if ($option == 'toapprove')
2333
	        {
2334
	           $response->warning_delay=$conf->expensereport->approve->warning_delay/60/60/24;
2335
	           $response->label=$langs->trans("ExpenseReportsToApprove");
2336
	           $response->url=DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut=2';
2337
	        }
2338
	        else
2339
	        {
2340
	            $response->warning_delay=$conf->expensereport->payment->warning_delay/60/60/24;
2341
	            $response->label=$langs->trans("ExpenseReportsToPay");
2342
	            $response->url=DOL_URL_ROOT.'/expensereport/list.php?mainmenu=hrm&amp;statut=5';
2343
	        }
2344
	        $response->img=img_object('', "trip");
2345
2346
            while ($obj=$this->db->fetch_object($resql))
2347
            {
2348
	            $response->nbtodo++;
2349
2350
	            if ($option == 'toapprove')
2351
	            {
2352
	                if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->approve->warning_delay)) {
2353
	                    $response->nbtodolate++;
2354
	                }
2355
	            }
2356
	            else
2357
	            {
2358
                    if ($this->db->jdate($obj->date_valid) < ($now - $conf->expensereport->payment->warning_delay)) {
2359
    	                $response->nbtodolate++;
2360
                    }
2361
	            }
2362
            }
2363
2364
            return $response;
2365
        }
2366
        else
2367
        {
2368
            dol_print_error($this->db);
2369
            $this->error=$this->db->error();
2370
            return -1;
2371
        }
2372
    }
2373
2374
    /**
2375
     * Return if an expense report is late or not
2376
     *
2377
     * @param  string  $option          'topay' or 'toapprove'
2378
     * @return boolean                  True if late, False if not late
2379
     */
2380
    public function hasDelay($option)
2381
    {
2382
        global $conf;
2383
2384
        //Only valid members
2385
        if ($option == 'toapprove' && $this->status != 2) return false;
2386
        if ($option == 'topay' && $this->status != 5) return false;
2387
2388
        $now = dol_now();
2389
        if ($option == 'toapprove')
2390
        {
2391
            return ($this->datevalid?$this->datevalid:$this->date_valid) < ($now - $conf->expensereport->approve->warning_delay);
0 ignored issues
show
Bug introduced by
The property datevalid does not exist on ExpenseReport. Did you mean date_valid?
Loading history...
2392
        }
2393
        else
2394
            return ($this->datevalid?$this->datevalid:$this->date_valid) < ($now - $conf->expensereport->payment->warning_delay);
2395
    }
2396
2397
    /**
2398
     *	Return if an expensereport was dispatched into bookkeeping
2399
     *
2400
     *	@return     int         <0 if KO, 0=no, 1=yes
2401
     */
2402
    public function getVentilExportCompta()
2403
    {
2404
    	$alreadydispatched = 0;
2405
2406
    	$type = 'expense_report';
2407
2408
    	$sql = " SELECT COUNT(ab.rowid) as nb FROM ".MAIN_DB_PREFIX."accounting_bookkeeping as ab WHERE ab.doc_type='".$type."' AND ab.fk_doc = ".$this->id;
2409
    	$resql = $this->db->query($sql);
2410
    	if ($resql)
2411
    	{
2412
    		$obj = $this->db->fetch_object($resql);
2413
    		if ($obj)
2414
    		{
2415
    			$alreadydispatched = $obj->nb;
2416
    		}
2417
    	}
2418
    	else
2419
    	{
2420
    		$this->error = $this->db->lasterror();
2421
    		return -1;
2422
    	}
2423
2424
    	if ($alreadydispatched)
2425
    	{
2426
    		return 1;
2427
    	}
2428
    	return 0;
2429
    }
2430
2431
	/**
2432
	 * 	Return amount of payments already done
2433
	 *
2434
	 *  @return		int						Amount of payment already done, <0 if KO
2435
	 */
2436
	public function getSumPayments()
2437
	{
2438
		$table='payment_expensereport';
2439
		$field='fk_expensereport';
2440
2441
		$sql = 'SELECT sum(amount) as amount';
2442
		$sql.= ' FROM '.MAIN_DB_PREFIX.$table;
2443
		$sql.= ' WHERE '.$field.' = '.$this->id;
2444
2445
		dol_syslog(get_class($this)."::getSumPayments", LOG_DEBUG);
2446
		$resql=$this->db->query($sql);
2447
		if ($resql)
2448
		{
2449
			$obj = $this->db->fetch_object($resql);
2450
			$this->db->free($resql);
2451
			return $obj->amount;
2452
		}
2453
		else
2454
		{
2455
			$this->error=$this->db->lasterror();
2456
			return -1;
2457
		}
2458
	}
2459
}
2460
2461
2462
/**
2463
 * Class of expense report details lines
2464
 */
2465
class ExpenseReportLine
2466
{
2467
    /**
2468
     * @var DoliDB Database handler.
2469
     */
2470
    public $db;
2471
2472
    /**
2473
	 * @var string Error code (or message)
2474
	 */
2475
	public $error='';
2476
2477
    /**
2478
	 * @var int ID
2479
	 */
2480
	public $rowid;
2481
2482
    public $comments;
2483
    public $qty;
2484
    public $value_unit;
2485
    public $date;
2486
2487
    /**
2488
     * @var int ID
2489
     */
2490
    public $fk_c_type_fees;
2491
2492
    /**
2493
     * @var int ID
2494
     */
2495
    public $fk_c_exp_tax_cat;
2496
2497
    /**
2498
     * @var int ID
2499
     */
2500
    public $fk_projet;
2501
2502
    /**
2503
     * @var int ID
2504
     */
2505
    public $fk_expensereport;
2506
2507
    public $type_fees_code;
2508
    public $type_fees_libelle;
2509
2510
    public $projet_ref;
2511
    public $projet_title;
2512
2513
    public $vatrate;
2514
    public $total_ht;
2515
    public $total_tva;
2516
    public $total_ttc;
2517
2518
    /**
2519
     * @var int ID into llx_ecm_files table to link line to attached file
2520
     */
2521
    public $fk_ecm_files;
2522
2523
2524
    /**
2525
     * Constructor
2526
     *
2527
     * @param DoliDB    $db     Handlet database
2528
     */
2529
    public function __construct($db)
2530
    {
2531
        $this->db= $db;
2532
    }
2533
2534
    /**
2535
     * Fetch record for expense report detailed line
2536
     *
2537
     * @param   int     $rowid      Id of object to load
2538
     * @return  int                 <0 if KO, >0 if OK
2539
     */
2540
    public function fetch($rowid)
2541
    {
2542
        $sql = 'SELECT fde.rowid, fde.fk_expensereport, fde.fk_c_type_fees, fde.fk_c_exp_tax_cat, fde.fk_projet as fk_project, fde.date,';
2543
        $sql.= ' fde.tva_tx as vatrate, fde.vat_src_code, fde.comments, fde.qty, fde.value_unit, fde.total_ht, fde.total_tva, fde.total_ttc, fde.fk_ecm_files,';
2544
        $sql.= ' ctf.code as type_fees_code, ctf.label as type_fees_libelle,';
2545
        $sql.= ' pjt.rowid as projet_id, pjt.title as projet_title, pjt.ref as projet_ref';
2546
        $sql.= ' FROM '.MAIN_DB_PREFIX.'expensereport_det as fde';
2547
        $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'c_type_fees as ctf ON fde.fk_c_type_fees=ctf.id';	// Sometimes type of expense report has been removed, so we use a left join here.
2548
        $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'projet as pjt ON fde.fk_projet=pjt.rowid';
2549
        $sql.= ' WHERE fde.rowid = '.$rowid;
2550
2551
        $result = $this->db->query($sql);
2552
2553
        if($result)
2554
        {
2555
            $objp = $this->db->fetch_object($result);
2556
2557
            $this->rowid = $objp->rowid;
2558
            $this->id = $objp->rowid;
2559
            $this->ref = $objp->ref;
2560
            $this->fk_expensereport = $objp->fk_expensereport;
2561
            $this->comments = $objp->comments;
2562
            $this->qty = $objp->qty;
2563
            $this->date = $objp->date;
2564
            $this->dates = $this->db->jdate($objp->date);
2565
            $this->value_unit = $objp->value_unit;
2566
            $this->fk_c_type_fees = $objp->fk_c_type_fees;
2567
            $this->fk_c_exp_tax_cat = $objp->fk_c_exp_tax_cat;
2568
            $this->fk_projet = $objp->fk_project;   // deprecated
2569
            $this->fk_project = $objp->fk_project;
2570
            $this->type_fees_code = $objp->type_fees_code;
2571
            $this->type_fees_libelle = $objp->type_fees_libelle;
2572
            $this->projet_ref = $objp->projet_ref;
2573
            $this->projet_title = $objp->projet_title;
2574
            $this->vatrate = $objp->vatrate;
2575
            $this->vat_src_code = $objp->vat_src_code;
2576
            $this->total_ht = $objp->total_ht;
2577
            $this->total_tva = $objp->total_tva;
2578
            $this->total_ttc = $objp->total_ttc;
2579
            $this->fk_ecm_files = $objp->fk_ecm_files;
2580
2581
            $this->db->free($result);
2582
        } else {
2583
            dol_print_error($this->db);
2584
        }
2585
    }
2586
2587
    /**
2588
     * insert
2589
     *
2590
     * @param   int     $notrigger      1=No trigger
2591
     * @param   bool    $fromaddline    false=keep default behavior, true=exclude the update_price() of parent object
2592
     * @return  int                     <0 if KO, >0 if OK
2593
     */
2594
    public function insert($notrigger = 0, $fromaddline = false)
2595
    {
2596
        global $langs, $user, $conf;
2597
2598
        $error=0;
2599
2600
        dol_syslog("ExpenseReportLine::Insert rang=".$this->rang, LOG_DEBUG);
0 ignored issues
show
Bug Best Practice introduced by
The property rang does not exist on ExpenseReportLine. Did you maybe forget to declare it?
Loading history...
2601
2602
        // Clean parameters
2603
        $this->comments=trim($this->comments);
2604
        if (!$this->value_unit_HT) $this->value_unit_HT=0;
2605
        $this->qty = price2num($this->qty);
2606
        $this->vatrate = price2num($this->vatrate);
2607
		if (empty($this->fk_c_exp_tax_cat)) $this->fk_c_exp_tax_cat = 0;
2608
2609
        $this->db->begin();
2610
2611
        $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'expensereport_det';
2612
        $sql.= ' (fk_expensereport, fk_c_type_fees, fk_projet,';
2613
        $sql.= ' tva_tx, vat_src_code, comments, qty, value_unit, total_ht, total_tva, total_ttc, date, rule_warning_message, fk_c_exp_tax_cat, fk_ecm_files)';
2614
        $sql.= " VALUES (".$this->db->escape($this->fk_expensereport).",";
2615
        $sql.= " ".$this->db->escape($this->fk_c_type_fees).",";
2616
        $sql.= " ".$this->db->escape($this->fk_project>0?$this->fk_project:($this->fk_projet>0?$this->fk_projet:'null')).",";
2617
        $sql.= " ".$this->db->escape($this->vatrate).",";
2618
        $sql.= " '".$this->db->escape($this->vat_src_code)."',";
2619
        $sql.= " '".$this->db->escape($this->comments)."',";
2620
        $sql.= " ".$this->db->escape($this->qty).",";
2621
        $sql.= " ".$this->db->escape($this->value_unit).",";
2622
        $sql.= " ".$this->db->escape($this->total_ht).",";
2623
        $sql.= " ".$this->db->escape($this->total_tva).",";
2624
        $sql.= " ".$this->db->escape($this->total_ttc).",";
2625
        $sql.= "'".$this->db->idate($this->date)."',";
2626
		$sql.= " '".$this->db->escape($this->rule_warning_message)."',";
0 ignored issues
show
Bug Best Practice introduced by
The property rule_warning_message does not exist on ExpenseReportLine. Did you maybe forget to declare it?
Loading history...
2627
		$sql.= " ".$this->db->escape($this->fk_c_exp_tax_cat).",";
2628
		$sql.= " ".($this->fk_ecm_files > 0 ? $this->fk_ecm_files : 'null');
2629
        $sql.= ")";
2630
2631
        $resql=$this->db->query($sql);
2632
        if ($resql)
2633
        {
2634
            $this->rowid=$this->db->last_insert_id(MAIN_DB_PREFIX.'expensereport_det');
2635
2636
			if (! $fromaddline)
2637
			{
2638
				$tmpparent=new ExpenseReport($this->db);
2639
				$tmpparent->fetch($this->fk_expensereport);
2640
				$result = $tmpparent->update_price();
2641
				if ($result < 0)
2642
				{
2643
					$error++;
2644
					$this->error = $tmpparent->error;
2645
					$this->errors = $tmpparent->errors;
2646
				}
2647
			}
2648
        }
2649
		else
2650
		{
2651
			$error++;
2652
		}
2653
2654
        if (! $error)
2655
        {
2656
            $this->db->commit();
2657
            return $this->rowid;
2658
        }
2659
        else
2660
        {
2661
            $this->error=$this->db->lasterror();
2662
            dol_syslog("ExpenseReportLine::insert Error ".$this->error, LOG_ERR);
2663
            $this->db->rollback();
2664
            return -2;
2665
        }
2666
    }
2667
2668
	/**
2669
	 * Function to get total amount in expense reports for a same rule
2670
	 *
2671
	 * @param  ExpenseReportRule $rule		object rule to check
2672
	 * @param  int				 $fk_user	user author id
2673
	 * @param  string			 $mode		day|EX_DAY / month|EX_MON / year|EX_YEA to get amount
2674
	 * @return float                        Amount
2675
	 */
2676
	public function getExpAmount(ExpenseReportRule $rule, $fk_user, $mode = 'day')
2677
	{
2678
		$amount = 0;
2679
2680
		$sql = 'SELECT SUM(d.total_ttc) as total_amount';
2681
		$sql .= ' FROM '.MAIN_DB_PREFIX.'expensereport_det d';
2682
		$sql .= ' INNER JOIN '.MAIN_DB_PREFIX.'expensereport e ON (d.fk_expensereport = e.rowid)';
2683
		$sql .= ' WHERE e.fk_user_author = '.$fk_user;
2684
		if (!empty($this->id)) $sql.= ' AND d.rowid <> '.$this->id;
2685
		$sql .= ' AND d.fk_c_type_fees = '.$rule->fk_c_type_fees;
2686
		if ($mode == 'day' || $mode == 'EX_DAY') $sql .= ' AND d.date = \''.dol_print_date($this->date, '%Y-%m-%d').'\'';
2687
		elseif ($mode == 'mon' || $mode == 'EX_MON') $sql .= ' AND DATE_FORMAT(d.date, \'%Y-%m\') = \''.dol_print_date($this->date, '%Y-%m').'\'';    // @TODO DATE_FORMAT is forbidden
2688
		elseif ($mode == 'year' || $mode == 'EX_YEA') $sql .= ' AND DATE_FORMAT(d.date, \'%Y\') = \''.dol_print_date($this->date, '%Y').'\'';         // @TODO DATE_FORMAT is forbidden
2689
2690
		dol_syslog('ExpenseReportLine::getExpAmount');
2691
2692
		$resql = $this->db->query($sql);
2693
		if ($resql)
2694
		{
2695
			$num = $this->db->num_rows($resql);
2696
			if ($num > 0)
2697
			{
2698
				$obj = $this->db->fetch_object($resql);
2699
				$amount = (double) $obj->total_amount;
2700
			}
2701
		}
2702
		else
2703
		{
2704
			dol_print_error($this->db);
2705
		}
2706
2707
		return $amount + $this->total_ttc;
2708
	}
2709
2710
    /**
2711
     * Update line
2712
     *
2713
     * @param   User    $user      User
2714
     * @return  int                <0 if KO, >0 if OK
2715
     */
2716
    public function update(User $user)
2717
    {
2718
        global $langs,$conf;
2719
2720
        $error=0;
2721
2722
        // Clean parameters
2723
        $this->comments=trim($this->comments);
2724
        $this->vatrate = price2num($this->vatrate);
2725
        $this->value_unit = price2num($this->value_unit);
2726
		if (empty($this->fk_c_exp_tax_cat)) $this->fk_c_exp_tax_cat = 0;
2727
2728
        $this->db->begin();
2729
2730
        // Update line in database
2731
        $sql = "UPDATE ".MAIN_DB_PREFIX."expensereport_det SET";
2732
        $sql.= " comments='".$this->db->escape($this->comments)."'";
2733
        $sql.= ",value_unit=".$this->db->escape($this->value_unit);
2734
        $sql.= ",qty=".$this->db->escape($this->qty);
2735
        $sql.= ",date='".$this->db->idate($this->date)."'";
2736
        $sql.= ",total_ht=".$this->db->escape($this->total_ht)."";
2737
        $sql.= ",total_tva=".$this->db->escape($this->total_tva)."";
2738
        $sql.= ",total_ttc=".$this->db->escape($this->total_ttc)."";
2739
        $sql.= ",tva_tx=".$this->db->escape($this->vatrate);
2740
		$sql.= ",vat_src_code='".$this->db->escape($this->vat_src_code)."'";
2741
        $sql.= ",rule_warning_message='".$this->db->escape($this->rule_warning_message)."'";
0 ignored issues
show
Bug Best Practice introduced by
The property rule_warning_message does not exist on ExpenseReportLine. Did you maybe forget to declare it?
Loading history...
2742
		$sql.= ",fk_c_exp_tax_cat=".$this->db->escape($this->fk_c_exp_tax_cat);
2743
		$sql.= ",fk_ecm_files=".($this->fk_ecm_files > 0 ? $this->fk_ecm_files : 'null');
2744
		if ($this->fk_c_type_fees) $sql.= ",fk_c_type_fees=".$this->db->escape($this->fk_c_type_fees);
2745
        else $sql.= ",fk_c_type_fees=null";
2746
        if ($this->fk_project > 0) $sql.= ",fk_projet=".$this->db->escape($this->fk_project);
2747
        else $sql.= ",fk_projet=null";
2748
        $sql.= " WHERE rowid = ".$this->db->escape($this->rowid);
2749
2750
        dol_syslog("ExpenseReportLine::update sql=".$sql);
2751
2752
        $resql=$this->db->query($sql);
2753
        if ($resql)
2754
        {
2755
            $tmpparent=new ExpenseReport($this->db);
2756
            $result = $tmpparent->fetch($this->fk_expensereport);
2757
            if ($result > 0)
2758
            {
2759
                $result = $tmpparent->update_price();
2760
                if ($result < 0)
2761
                {
2762
                    $error++;
2763
                    $this->error = $tmpparent->error;
2764
                    $this->errors = $tmpparent->errors;
2765
                }
2766
            }
2767
            else
2768
            {
2769
                $error++;
2770
                $this->error = $tmpparent->error;
2771
                $this->errors = $tmpparent->errors;
2772
            }
2773
        }
2774
        else
2775
        {
2776
            $error++;
2777
            dol_print_error($this->db);
2778
        }
2779
2780
        if (! $error)
2781
        {
2782
            $this->db->commit();
2783
            return 1;
2784
        }
2785
        else
2786
        {
2787
            $this->error=$this->db->lasterror();
2788
            dol_syslog("ExpenseReportLine::update Error ".$this->error, LOG_ERR);
2789
            $this->db->rollback();
2790
            return -2;
2791
        }
2792
    }
2793
}
2794
2795
2796
/**
2797
 *    Retourne la liste deroulante des differents etats d'une note de frais.
2798
 *    Les valeurs de la liste sont les id de la table c_expensereport_statuts
2799
 *
2800
 *    @param    int     $selected       preselect status
2801
 *    @param    string  $htmlname       Name of HTML select
2802
 *    @param    int     $useempty       1=Add empty line
2803
 *    @param    int     $useshortlabel  Use short labels
2804
 *    @return   string                  HTML select with status
2805
 */
2806
function select_expensereport_statut($selected = '', $htmlname = 'fk_statut', $useempty = 1, $useshortlabel = 0)
2807
{
2808
    global $db, $langs;
2809
2810
    $tmpep=new ExpenseReport($db);
2811
2812
    print '<select class="flat" name="'.$htmlname.'">';
2813
    if ($useempty) print '<option value="-1">&nbsp;</option>';
2814
    $arrayoflabels=$tmpep->statuts;
2815
    if ($useshortlabel) $arrayoflabels=$tmpep->statuts_short;
2816
    foreach ($arrayoflabels as $key => $val)
2817
    {
2818
        if ($selected != '' && $selected == $key)
2819
        {
2820
            print '<option value="'.$key.'" selected>';
2821
        }
2822
        else
2823
        {
2824
            print '<option value="'.$key.'">';
2825
        }
2826
        print $langs->trans($val);
2827
        print '</option>';
2828
    }
2829
    print '</select>';
2830
}
2831
2832
/**
2833
 *  Return list of types of notes with select value = id
2834
 *
2835
 *  @param      int     $selected       Preselected type
2836
 *  @param      string  $htmlname       Name of field in form
2837
 *  @param      int     $showempty      Add an empty field
2838
 *  @param      int     $active         1=Active only, 0=Unactive only, -1=All
2839
 *  @return     string                  Select html
2840
 */
2841
function select_type_fees_id($selected = '', $htmlname = 'type', $showempty = 0, $active = 1)
2842
{
2843
    global $db,$langs,$user;
2844
    $langs->load("trips");
2845
2846
    print '<select class="flat" name="'.$htmlname.'">';
2847
    if ($showempty)
2848
    {
2849
        print '<option value="-1"';
2850
        if ($selected == -1) print ' selected';
2851
        print '>&nbsp;</option>';
2852
    }
2853
2854
    $sql = "SELECT c.id, c.code, c.label as type FROM ".MAIN_DB_PREFIX."c_type_fees as c";
2855
    if ($active >= 0) $sql.= " WHERE c.active = ".$active;
2856
    $sql.= " ORDER BY c.label ASC";
2857
    $resql=$db->query($sql);
2858
    if ($resql)
2859
    {
2860
        $num = $db->num_rows($resql);
2861
        $i = 0;
2862
2863
        while ($i < $num)
2864
        {
2865
            $obj = $db->fetch_object($resql);
2866
            print '<option value="'.$obj->id.'"';
2867
            if ($obj->code == $selected || $obj->id == $selected) print ' selected';
2868
            print '>';
2869
            if ($obj->code != $langs->trans($obj->code)) print $langs->trans($obj->code);
2870
            else print $langs->trans($obj->type);
2871
            $i++;
2872
        }
2873
    }
2874
    print '</select>';
2875
}
2876