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

fetch_users_approver_expensereport()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 32
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 21
c 0
b 0
f 0
nc 3
nop 0
dl 0
loc 32
rs 9.584
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